diff --git a/Demos/Demos.xcworkspace/contents.xcworkspacedata b/Demos/Demos.xcworkspace/contents.xcworkspacedata index 87886c356..c306685bc 100644 --- a/Demos/Demos.xcworkspace/contents.xcworkspacedata +++ b/Demos/Demos.xcworkspace/contents.xcworkspacedata @@ -7,9 +7,6 @@ - - diff --git a/Demos/README.md b/Demos/README.md index bf32a4bc5..3b33c84e8 100644 --- a/Demos/README.md +++ b/Demos/README.md @@ -64,7 +64,12 @@ as a system library instead. ### *API-Samples* -This *Xcode* project actually contains a large number of modular demos, with each demo +> **_Note:_** The `Vulkan-Samples API-Samples` have recently changed to use a different build + process that involves converting GLSL to SPIR-V via scripts. This upgrade has not yet been + integrated into the *Xcode*-based build environment used to build these demos here. + As a result, the `API-Samples` demos have been disabled here until this can be corrected. + +This *Xcode* project contains a large number of modular demos, with each demo demonstrating a particular *Vulkan* feature, or suite of calls. This demo can be found in the `LunarG-VulkanSamples/API-Samples` folder, and in the @@ -77,7 +82,7 @@ To specify which of the many modular demos to run, open the `Samples.h` in the ` project in the *Xcode Project Navigator* in the `Demos.xcworkspace` *Xcode* workspace, and follow the instructions in the comments within that file. -> **Note:** For simplicity, the `API-Samples` demos are bare-bones. Each of the `API-Samples` +> **_Note:_** For simplicity, the `API-Samples` demos are bare-bones. Each of the `API-Samples` > demos renders a single frame during app startup, and then leaves the rendered image static. > There is no display loop or motion in any of these demos. > **This is normal for these demos, and the demo has not "hung" or "crashed" when this occurs.** @@ -91,7 +96,7 @@ The `API-Samples` demo is a simple example of installing **MoltenVK** as a *stat ### *Hologram* -> **Note:** In order to build the `Hologram` demo, you must have *Python3* installed +> **_Note:_** In order to build the `Hologram` demo, you must have *Python3* installed > on your build computer. This is a sophisticated particle demo that populates command buffers from multiple threads. diff --git a/Docs/Whats_New.md b/Docs/Whats_New.md index c50838c44..18df43410 100644 --- a/Docs/Whats_New.md +++ b/Docs/Whats_New.md @@ -32,11 +32,13 @@ Released 2020/04/05 - Fix issue causing screen captures from swapchain image to deadlock. - Fix memory estimates for iOS 13+. - Broaden conditions for host read sync for image memory barriers on macOS. +- Fix issue of reseting `CAMetalDrawable` and `MTLTexture` of peer swapchain images. - Fix the `make install` build command to overwrite the existing framework in the system framework library, and update `README.md` to clarify the instructions for using `make install`. - Update the `README.md` and `MoltenVK_Runtime_UserGuide.md` documents to clarify that **MoltenVK** is not a fully-compliant implementation of *Vulkan*. - Support Xcode 11.4. +- Disable `API-Samples` demos and document in `Demos/README.md`. - Update dependency libraries to match *Vulkan SDK 1.2.135*. - Update to latest SPIRV-Cross version: - MSL: Support inline uniform blocks in argument buffers. diff --git a/MoltenVK/MoltenVK.xcodeproj/project.pbxproj b/MoltenVK/MoltenVK.xcodeproj/project.pbxproj index 321eea65a..8302b2e47 100644 --- a/MoltenVK/MoltenVK.xcodeproj/project.pbxproj +++ b/MoltenVK/MoltenVK.xcodeproj/project.pbxproj @@ -503,8 +503,6 @@ A94FB7841C7DFB4800632CA3 /* MVKDevice.mm */, A94FB7851C7DFB4800632CA3 /* MVKDeviceMemory.h */, A94FB7861C7DFB4800632CA3 /* MVKDeviceMemory.mm */, - A9653FB724129C84005999D7 /* MVKPixelFormats.h */, - A9653FB924129C84005999D7 /* MVKPixelFormats.mm */, A94FB7871C7DFB4800632CA3 /* MVKFramebuffer.h */, A94FB7881C7DFB4800632CA3 /* MVKFramebuffer.mm */, A94FB7891C7DFB4800632CA3 /* MVKImage.h */, @@ -513,6 +511,8 @@ A94FB78C1C7DFB4800632CA3 /* MVKInstance.mm */, A94FB78D1C7DFB4800632CA3 /* MVKPipeline.h */, A94FB78E1C7DFB4800632CA3 /* MVKPipeline.mm */, + A9653FB724129C84005999D7 /* MVKPixelFormats.h */, + A9653FB924129C84005999D7 /* MVKPixelFormats.mm */, A94FB78F1C7DFB4800632CA3 /* MVKQueryPool.h */, A94FB7901C7DFB4800632CA3 /* MVKQueryPool.mm */, A94FB7911C7DFB4800632CA3 /* MVKQueue.h */, diff --git a/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h b/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h index a988a15f6..95056cb1d 100644 --- a/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h +++ b/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h @@ -163,6 +163,18 @@ typedef unsigned long MTLLanguageVersion; * a command is executed. This is a classic time-space trade off. When command pooling is * active, the memory in the pool can be cleared via a call to the vkTrimCommandPoolKHR() * command. This setting is enabled by default, and MoltenVK will pool command memory. + * + * 9. The MVK_CONFIG_USE_MTLHEAP runtime environment variable or MoltenVK compile-time build + * setting controls whether MoltenVK should use MTLHeaps for allocating textures and buffers + * from device memory. If this environment variable is enabled, and placement MTLHeaps are + * available on the platform, MoltenVK will allocate a placement MTLHeap for each VkDeviceMemory + * instance, and allocate textures and buffers from that placement heap. If this environment + * variable is disabled, MoltenVK will allocate textures and buffers from general device memory. + * Apple recommends that MTLHeaps should only be used for specific requirements such as aliasing + * or hazard tracking, and MoltenVK testing has shown that allocating multiple textures of + * different types or usages from one MTLHeap can occassionally cause corruption issues under + * certain circumstances. Because of this, this setting is disabled by default, and MoltenVK + * will allocate texures and buffers from general device memory. */ typedef struct { diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h index 95bbad25d..d17710188 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h @@ -42,7 +42,7 @@ class MVKResource; class MVKBuffer; class MVKBufferView; class MVKImage; -class MVKSwapchainImage; +class MVKPresentableSwapchainImage; class MVKImageView; class MVKSwapchain; class MVKDeviceMemory; @@ -450,12 +450,12 @@ class MVKDevice : public MVKDispatchableVulkanAPIObject { void destroySwapchain(MVKSwapchain* mvkSwpChn, const VkAllocationCallbacks* pAllocator); - MVKSwapchainImage* createSwapchainImage(const VkImageCreateInfo* pCreateInfo, - MVKSwapchain* swapchain, - uint32_t swapchainIndex, - const VkAllocationCallbacks* pAllocator); - void destroySwapchainImage(MVKSwapchainImage* mvkImg, - const VkAllocationCallbacks* pAllocator); + MVKPresentableSwapchainImage* createPresentableSwapchainImage(const VkImageCreateInfo* pCreateInfo, + MVKSwapchain* swapchain, + uint32_t swapchainIndex, + const VkAllocationCallbacks* pAllocator); + void destroyPresentableSwapchainImage(MVKPresentableSwapchainImage* mvkImg, + const VkAllocationCallbacks* pAllocator); MVKFence* createFence(const VkFenceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator); diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm index b49b4f120..c9781fd0a 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm @@ -773,7 +773,15 @@ // Initializes the Metal-specific physical device features of this instance. void MVKPhysicalDevice::initMetalFeatures() { - mvkClear(&_metalFeatures); // Start with everything cleared + +# ifndef MVK_CONFIG_USE_MTLHEAP +# define MVK_CONFIG_USE_MTLHEAP 0 +# endif + bool useMTLHeaps; + MVK_SET_FROM_ENV_OR_BUILD_BOOL(useMTLHeaps, MVK_CONFIG_USE_MTLHEAP); + + // Start with all Metal features cleared + mvkClear(&_metalFeatures); _metalFeatures.maxPerStageBufferCount = 31; _metalFeatures.maxMTLBufferSize = (256 * MEBI); @@ -848,7 +856,7 @@ if ( mvkOSVersionIsAtLeast(13.0) ) { _metalFeatures.mslVersionEnum = MTLLanguageVersion2_2; - _metalFeatures.placementHeaps = true; + _metalFeatures.placementHeaps = useMTLHeaps; if (supportsMTLGPUFamily(Apple4)) { _metalFeatures.nativeTextureSwizzle = true; } @@ -904,7 +912,7 @@ _metalFeatures.native3DCompressedTextures = true; if (supportsMTLGPUFamily(Mac2)) { _metalFeatures.nativeTextureSwizzle = true; - _metalFeatures.placementHeaps = true; + _metalFeatures.placementHeaps = useMTLHeaps; } } @@ -957,7 +965,6 @@ break; #endif } - } // Initializes the physical device features of this instance. @@ -2131,7 +2138,7 @@ static uint32_t mvkGetEntryProperty(io_registry_entry_t entry, CFStringRef prope } } if (swapchainInfo) { - return createSwapchainImage(pCreateInfo, (MVKSwapchain*)swapchainInfo->swapchain, uint32_t(-1), pAllocator); + return (MVKImage*)addResource(new MVKPeerSwapchainImage(this, pCreateInfo, (MVKSwapchain*)swapchainInfo->swapchain, uint32_t(-1))); } return (MVKImage*)addResource(new MVKImage(this, pCreateInfo)); } @@ -2162,15 +2169,15 @@ static uint32_t mvkGetEntryProperty(io_registry_entry_t entry, CFStringRef prope mvkSwpChn->destroy(); } -MVKSwapchainImage* MVKDevice::createSwapchainImage(const VkImageCreateInfo* pCreateInfo, - MVKSwapchain* swapchain, - uint32_t swapchainIndex, - const VkAllocationCallbacks* pAllocator) { - return (MVKSwapchainImage*)addResource(new MVKSwapchainImage(this, pCreateInfo, swapchain, swapchainIndex)); +MVKPresentableSwapchainImage* MVKDevice::createPresentableSwapchainImage(const VkImageCreateInfo* pCreateInfo, + MVKSwapchain* swapchain, + uint32_t swapchainIndex, + const VkAllocationCallbacks* pAllocator) { + return (MVKPresentableSwapchainImage*)addResource(new MVKPresentableSwapchainImage(this, pCreateInfo, swapchain, swapchainIndex)); } -void MVKDevice::destroySwapchainImage(MVKSwapchainImage* mvkImg, - const VkAllocationCallbacks* pAllocator) { +void MVKDevice::destroyPresentableSwapchainImage(MVKPresentableSwapchainImage* mvkImg, + const VkAllocationCallbacks* pAllocator) { removeResource(mvkImg); mvkImg->destroy(); } @@ -2656,10 +2663,9 @@ static uint32_t mvkGetEntryProperty(io_registry_entry_t entry, CFStringRef prope } MVKLogInfo("Using %s for Vulkan semaphores.", _useMTLFenceForSemaphores ? "MTLFence" : (_useMTLEventForSemaphores ? "MTLEvent" : "emulation")); -#ifndef MVK_CONFIG_USE_COMMAND_POOLING -# define MVK_CONFIG_USE_COMMAND_POOLING 1 -#endif - _useCommandPooling = MVK_CONFIG_USE_COMMAND_POOLING; +# ifndef MVK_CONFIG_USE_COMMAND_POOLING +# define MVK_CONFIG_USE_COMMAND_POOLING 1 +# endif MVK_SET_FROM_ENV_OR_BUILD_BOOL(_useCommandPooling, MVK_CONFIG_USE_COMMAND_POOLING); #if MVK_MACOS diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.h b/MoltenVK/MoltenVK/GPUObjects/MVKImage.h index fc1ba816d..89e5af024 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.h @@ -147,7 +147,7 @@ class MVKImage : public MVKResource { #pragma mark Metal /** Returns the Metal texture underlying this image. */ - id getMTLTexture(); + virtual id getMTLTexture(); /** Returns a Metal texture that interprets the pixels in the specified format. */ id getMTLTexture(MTLPixelFormat mtlPixFmt); @@ -235,9 +235,9 @@ class MVKImage : public MVKResource { bool validateUseTexelBuffer(); void initSubresources(const VkImageCreateInfo* pCreateInfo); void initSubresourceLayout(MVKImageSubresource& imgSubRez); - virtual id newMTLTexture(); - void resetMTLTexture(); - void resetIOSurface(); + id newMTLTexture(); + void releaseMTLTexture(); + void releaseIOSurface(); MTLTextureDescriptor* newMTLTextureDescriptor(); void updateMTLTextureContent(MVKImageSubresource& subresource, VkDeviceSize offset, VkDeviceSize size); void getMTLTextureContent(MVKImageSubresource& subresource, VkDeviceSize offset, VkDeviceSize size); @@ -274,10 +274,44 @@ class MVKImage : public MVKResource { #pragma mark - #pragma mark MVKSwapchainImage +/** Abstract class of Vulkan image used as a rendering destination within a swapchain. */ +class MVKSwapchainImage : public MVKImage { + +public: + + /** Binds this resource to the specified offset within the specified memory allocation. */ + VkResult bindDeviceMemory(MVKDeviceMemory* mvkMem, VkDeviceSize memOffset) override; + +#pragma mark Metal + + /** Returns the Metal texture used by the CAMetalDrawable underlying this image. */ + id getMTLTexture() override; + + +#pragma mark Construction + + /** Constructs an instance for the specified device and swapchain. */ + MVKSwapchainImage(MVKDevice* device, + const VkImageCreateInfo* pCreateInfo, + MVKSwapchain* swapchain, + uint32_t swapchainIndex); + +protected: + friend class MVKPeerSwapchainImage; + + virtual id getCAMetalDrawable() = 0; + + MVKSwapchain* _swapchain; + uint32_t _swapchainIndex; +}; + + +#pragma mark - +#pragma mark MVKPresentableSwapchainImage + /** Indicates the relative availability of each image in the swapchain. */ typedef struct MVKSwapchainImageAvailability { uint64_t acquisitionID; /**< When this image was last made available, relative to the other images in the swapchain. Smaller value is earlier. */ - uint32_t waitCount; /**< The number of semaphores already waiting for this image. */ bool isAvailable; /**< Indicates whether this image is currently available. */ bool operator< (const MVKSwapchainImageAvailability& rhs) const; @@ -287,18 +321,11 @@ typedef struct MVKSwapchainImageAvailability { typedef std::pair MVKSwapchainSignaler; -/** Represents a Vulkan image used as a rendering destination within a swapchain. */ -class MVKSwapchainImage : public MVKImage { +/** Represents a Vulkan swapchain image that can be submitted to the presentation engine. */ +class MVKPresentableSwapchainImage : public MVKSwapchainImage { public: - /** Binds this resource to the specified offset within the specified memory allocation. */ - VkResult bindDeviceMemory(MVKDeviceMemory* mvkMem, VkDeviceSize memOffset) override; - - /** Binds this resource according to the specified bind information. */ - VkResult bindDeviceMemory2(const void* pBindInfo) override; - - #pragma mark Metal /** @@ -315,30 +342,27 @@ class MVKSwapchainImage : public MVKImage { #pragma mark Construction /** Constructs an instance for the specified device and swapchain. */ - MVKSwapchainImage(MVKDevice* device, - const VkImageCreateInfo* pCreateInfo, - MVKSwapchain* swapchain, - uint32_t swapchainIndex); + MVKPresentableSwapchainImage(MVKDevice* device, + const VkImageCreateInfo* pCreateInfo, + MVKSwapchain* swapchain, + uint32_t swapchainIndex); - ~MVKSwapchainImage() override; + ~MVKPresentableSwapchainImage() override; protected: friend MVKSwapchain; - id newMTLTexture() override; - id getCAMetalDrawable(); - void resetMetalDrawable(); + id getCAMetalDrawable() override; + void releaseMetalDrawable(); MVKSwapchainImageAvailability getAvailability(); void makeAvailable(); - void signalWhenAvailable(MVKSemaphore* semaphore, MVKFence* fence); + void acquireAndSignalWhenAvailable(MVKSemaphore* semaphore, MVKFence* fence); void signal(MVKSwapchainSignaler& signaler, id mtlCmdBuff); void signalPresentationSemaphore(id mtlCmdBuff); static void markAsTracked(MVKSwapchainSignaler& signaler); static void unmarkAsTracked(MVKSwapchainSignaler& signaler); void renderWatermark(id mtlCmdBuff); - MVKSwapchain* _swapchain; - uint32_t _swapchainIndex; id _mtlDrawable; MVKSwapchainImageAvailability _availability; MVKVectorInline _availabilitySignalers; @@ -347,6 +371,32 @@ class MVKSwapchainImage : public MVKImage { }; +#pragma mark - +#pragma mark MVKPeerSwapchainImage + +/** Represents a Vulkan swapchain image that can be associated as a peer to a swapchain image. */ +class MVKPeerSwapchainImage : public MVKSwapchainImage { + +public: + + /** Binds this resource according to the specified bind information. */ + VkResult bindDeviceMemory2(const void* pBindInfo) override; + + +#pragma mark Construction + + /** Constructs an instance for the specified device and swapchain. */ + MVKPeerSwapchainImage(MVKDevice* device, + const VkImageCreateInfo* pCreateInfo, + MVKSwapchain* swapchain, + uint32_t swapchainIndex); + +protected: + id getCAMetalDrawable() override; + +}; + + #pragma mark - #pragma mark MVKImageView diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm index 4a226d94d..97c52e7e3 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm @@ -298,34 +298,6 @@ return mtlTex; } -VkResult MVKImage::setMTLTexture(id mtlTexture) { - lock_guard lock(_lock); - resetMTLTexture(); - resetIOSurface(); - - _mtlTexture = [mtlTexture retain]; // retained - - _mtlPixelFormat = _mtlTexture.pixelFormat; - _mtlTextureType = _mtlTexture.textureType; - _extent.width = uint32_t(_mtlTexture.width); - _extent.height = uint32_t(_mtlTexture.height); - _extent.depth = uint32_t(_mtlTexture.depth); - _mipLevels = uint32_t(_mtlTexture.mipmapLevelCount); - _samples = mvkVkSampleCountFlagBitsFromSampleCount(_mtlTexture.sampleCount); - _arrayLayers = uint32_t(_mtlTexture.arrayLength); - _usage = getPixelFormats()->getVkImageUsageFlagsFromMTLTextureUsage(_mtlTexture.usage, _mtlPixelFormat); - - if (_device->_pMetalFeatures->ioSurfaces) { - _ioSurface = mtlTexture.iosurface; - CFRetain(_ioSurface); - } - - return VK_SUCCESS; -} - -// Creates and returns a retained Metal texture suitable for use in this instance. -// This implementation creates a new MTLTexture from a MTLTextureDescriptor and possible IOSurface. -// Subclasses may override this function to create the MTLTexture in a different manner. id MVKImage::newMTLTexture() { id mtlTex = nil; MTLTextureDescriptor* mtlTexDesc = newMTLTextureDescriptor(); // temp retain @@ -348,15 +320,41 @@ return mtlTex; } +VkResult MVKImage::setMTLTexture(id mtlTexture) { + lock_guard lock(_lock); + + releaseMTLTexture(); + releaseIOSurface(); + + _mtlTexture = [mtlTexture retain]; // retained + + _mtlPixelFormat = mtlTexture.pixelFormat; + _mtlTextureType = mtlTexture.textureType; + _extent.width = uint32_t(mtlTexture.width); + _extent.height = uint32_t(mtlTexture.height); + _extent.depth = uint32_t(mtlTexture.depth); + _mipLevels = uint32_t(mtlTexture.mipmapLevelCount); + _samples = mvkVkSampleCountFlagBitsFromSampleCount(mtlTexture.sampleCount); + _arrayLayers = uint32_t(mtlTexture.arrayLength); + _usage = getPixelFormats()->getVkImageUsageFlagsFromMTLTextureUsage(mtlTexture.usage, _mtlPixelFormat); + + if (_device->_pMetalFeatures->ioSurfaces) { + _ioSurface = mtlTexture.iosurface; + CFRetain(_ioSurface); + } + + return VK_SUCCESS; +} + // Removes and releases the MTLTexture object, and all associated texture views -void MVKImage::resetMTLTexture() { +void MVKImage::releaseMTLTexture() { [_mtlTexture release]; _mtlTexture = nil; for (auto elem : _mtlTextureViews) { [elem.second release]; } _mtlTextureViews.clear(); } -void MVKImage::resetIOSurface() { +void MVKImage::releaseIOSurface() { if (_ioSurface) { CFRelease(_ioSurface); _ioSurface = nil; @@ -372,8 +370,8 @@ #if MVK_SUPPORT_IOSURFACE_BOOL - resetMTLTexture(); - resetIOSurface(); + releaseMTLTexture(); + releaseIOSurface(); MVKPixelFormats* pixFmts = getPixelFormats(); @@ -788,8 +786,8 @@ MVKImage::~MVKImage() { if (_deviceMemory) { _deviceMemory->removeImage(this); } - resetMTLTexture(); - resetIOSurface(); + releaseMTLTexture(); + releaseIOSurface(); } @@ -800,37 +798,34 @@ return VK_ERROR_OUT_OF_DEVICE_MEMORY; } -VkResult MVKSwapchainImage::bindDeviceMemory2(const void* pBindInfo) { - const auto* imageInfo = (const VkBindImageMemoryInfo*)pBindInfo; - const VkBindImageMemorySwapchainInfoKHR* swapchainInfo = nullptr; - for (const auto* next = (const VkBaseInStructure*)imageInfo->pNext; next; next = next->pNext) { - switch (next->sType) { - case VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_SWAPCHAIN_INFO_KHR: - swapchainInfo = (const VkBindImageMemorySwapchainInfoKHR*)next; - break; - default: - break; - } - if (swapchainInfo) { break; } - } - if (!swapchainInfo) { - return VK_ERROR_OUT_OF_DEVICE_MEMORY; - } - _swapchainIndex = swapchainInfo->imageIndex; - return VK_SUCCESS; + +#pragma mark Metal + +// Overridden to always retrieve the MTLTexture directly from the CAMetalDrawable. +id MVKSwapchainImage::getMTLTexture() { return [getCAMetalDrawable() texture]; } + + +#pragma mark Construction + +MVKSwapchainImage::MVKSwapchainImage(MVKDevice* device, + const VkImageCreateInfo* pCreateInfo, + MVKSwapchain* swapchain, + uint32_t swapchainIndex) : MVKImage(device, pCreateInfo) { + _swapchain = swapchain; + _swapchainIndex = swapchainIndex; } + +#pragma mark - +#pragma mark MVKPresentableSwapchainImage + bool MVKSwapchainImageAvailability::operator< (const MVKSwapchainImageAvailability& rhs) const { if ( isAvailable && !rhs.isAvailable) { return true; } if ( !isAvailable && rhs.isAvailable) { return false; } - - if (waitCount < rhs.waitCount) { return true; } - if (waitCount > rhs.waitCount) { return false; } - return acquisitionID < rhs.acquisitionID; } -MVKSwapchainImageAvailability MVKSwapchainImage::getAvailability() { +MVKSwapchainImageAvailability MVKPresentableSwapchainImage::getAvailability() { lock_guard lock(_availabilityLock); return _availability; @@ -839,7 +834,7 @@ // Makes an image available for acquisition by the app. // If any semaphores are waiting to be signaled when this image becomes available, the // earliest semaphore is signaled, and this image remains unavailable for other uses. -void MVKSwapchainImage::makeAvailable() { +void MVKPresentableSwapchainImage::makeAvailable() { lock_guard lock(_availabilityLock); // Mark when this event happened, relative to that of other images @@ -870,9 +865,13 @@ // MVKLogDebug("Signaling%s swapchain image %p semaphore %p from present, with %lu remaining semaphores.", (_availability.isAvailable ? " pre-signaled" : ""), this, signaler.first, _availabilitySignalers.size()); } -void MVKSwapchainImage::signalWhenAvailable(MVKSemaphore* semaphore, MVKFence* fence) { +void MVKPresentableSwapchainImage::acquireAndSignalWhenAvailable(MVKSemaphore* semaphore, MVKFence* fence) { lock_guard lock(_availabilityLock); + // Now that this image is being acquired, release the existing drawable and its texture. + // This is not done earlier so the texture is retained for any post-processing such as screen captures, etc. + releaseMetalDrawable(); + auto signaler = make_pair(semaphore, fence); if (_availability.isAvailable) { _availability.isAvailable = false; @@ -899,7 +898,7 @@ } // If present, signal the semaphore for the first waiter for the given image. -void MVKSwapchainImage::signalPresentationSemaphore(id mtlCmdBuff) { +void MVKPresentableSwapchainImage::signalPresentationSemaphore(id mtlCmdBuff) { lock_guard lock(_availabilityLock); if ( !_availabilitySignalers.empty() ) { @@ -909,19 +908,19 @@ } // Signal either or both of the semaphore and fence in the specified tracker pair. -void MVKSwapchainImage::signal(MVKSwapchainSignaler& signaler, id mtlCmdBuff) { +void MVKPresentableSwapchainImage::signal(MVKSwapchainSignaler& signaler, id mtlCmdBuff) { if (signaler.first) { signaler.first->encodeSignal(mtlCmdBuff); } if (signaler.second) { signaler.second->signal(); } } // Tell the semaphore and fence that they are being tracked for future signaling. -void MVKSwapchainImage::markAsTracked(MVKSwapchainSignaler& signaler) { +void MVKPresentableSwapchainImage::markAsTracked(MVKSwapchainSignaler& signaler) { if (signaler.first) { signaler.first->retain(); } if (signaler.second) { signaler.second->retain(); } } // Tell the semaphore and fence that they are no longer being tracked for future signaling. -void MVKSwapchainImage::unmarkAsTracked(MVKSwapchainSignaler& signaler) { +void MVKPresentableSwapchainImage::unmarkAsTracked(MVKSwapchainSignaler& signaler) { if (signaler.first) { signaler.first->release(); } if (signaler.second) { signaler.second->release(); } } @@ -929,19 +928,13 @@ #pragma mark Metal -// Creates and returns a retained Metal texture suitable for use in this instance. -// This implementation retrieves a MTLTexture from the CAMetalDrawable. -id MVKSwapchainImage::newMTLTexture() { - return [[getCAMetalDrawable() texture] retain]; -} - -id MVKSwapchainImage::getCAMetalDrawable() { +id MVKPresentableSwapchainImage::getCAMetalDrawable() { while ( !_mtlDrawable ) { @autoreleasepool { // Reclaim auto-released drawable object before end of loop uint64_t startTime = _device->getPerformanceTimestamp(); _mtlDrawable = [_swapchain->_mtlLayer.nextDrawable retain]; - if ( !_mtlDrawable ) { MVKLogError("CAMetalDrawable could not be acquired after %.6f ms.", mvkGetElapsedMilliseconds(startTime)); } + if ( !_mtlDrawable ) { MVKLogError("CAMetalDrawable could not be acquired."); } _device->addActivityPerformance(_device->_performanceStatistics.queue.nextCAMetalDrawable, startTime); } @@ -950,7 +943,7 @@ } // Present the drawable and make myself available only once the command buffer has completed. -void MVKSwapchainImage::presentCAMetalDrawable(id mtlCmdBuff) { +void MVKPresentableSwapchainImage::presentCAMetalDrawable(id mtlCmdBuff) { _swapchain->willPresentSurface(getMTLTexture(), mtlCmdBuff); NSString* scName = _swapchain->getDebugName(); @@ -968,8 +961,8 @@ } // Resets the MTLTexture and CAMetalDrawable underlying this image. -void MVKSwapchainImage::resetMetalDrawable() { - resetMTLTexture(); // Release texture first so drawable will be last to release it +void MVKPresentableSwapchainImage::releaseMetalDrawable() { + releaseMTLTexture(); // Release texture first so drawable will be last to release it [_mtlDrawable release]; _mtlDrawable = nil; } @@ -977,12 +970,12 @@ #pragma mark Construction -MVKSwapchainImage::MVKSwapchainImage(MVKDevice* device, - const VkImageCreateInfo* pCreateInfo, - MVKSwapchain* swapchain, - uint32_t swapchainIndex) : MVKImage(device, pCreateInfo) { - _swapchain = swapchain; - _swapchainIndex = swapchainIndex; +MVKPresentableSwapchainImage::MVKPresentableSwapchainImage(MVKDevice* device, + const VkImageCreateInfo* pCreateInfo, + MVKSwapchain* swapchain, + uint32_t swapchainIndex) : + MVKSwapchainImage(device, pCreateInfo, swapchain, swapchainIndex) { + _mtlDrawable = nil; _availability.acquisitionID = _swapchain->getNextAcquisitionID(); @@ -990,11 +983,52 @@ _preSignaler = make_pair(nullptr, nullptr); } -MVKSwapchainImage::~MVKSwapchainImage() { - resetMetalDrawable(); +MVKPresentableSwapchainImage::~MVKPresentableSwapchainImage() { + releaseMetalDrawable(); } +#pragma mark - +#pragma mark MVKPeerSwapchainImage + +VkResult MVKPeerSwapchainImage::bindDeviceMemory2(const void* pBindInfo) { + const auto* imageInfo = (const VkBindImageMemoryInfo*)pBindInfo; + const VkBindImageMemorySwapchainInfoKHR* swapchainInfo = nullptr; + for (const auto* next = (const VkBaseInStructure*)imageInfo->pNext; next; next = next->pNext) { + switch (next->sType) { + case VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_SWAPCHAIN_INFO_KHR: + swapchainInfo = (const VkBindImageMemorySwapchainInfoKHR*)next; + break; + default: + break; + } + if (swapchainInfo) { break; } + } + if (!swapchainInfo) { + return VK_ERROR_OUT_OF_DEVICE_MEMORY; + } + _swapchainIndex = swapchainInfo->imageIndex; + return VK_SUCCESS; +} + + +#pragma mark Metal + +id MVKPeerSwapchainImage::getCAMetalDrawable() { + return ((MVKSwapchainImage*)_swapchain->getPresentableImage(_swapchainIndex))->getCAMetalDrawable(); +} + + +#pragma mark Construction + +MVKPeerSwapchainImage::MVKPeerSwapchainImage(MVKDevice* device, + const VkImageCreateInfo* pCreateInfo, + MVKSwapchain* swapchain, + uint32_t swapchainIndex) : + MVKSwapchainImage(device, pCreateInfo, swapchain, swapchainIndex) {} + + + #pragma mark - #pragma mark MVKImageView diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm b/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm index 6f7bc0c2f..c231f2eb1 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm @@ -289,7 +289,7 @@ #if MVK_MACOS NSArray* rawMTLDevs = [MTLCopyAllDevices() autorelease]; if (rawMTLDevs) { - bool forceLowPower = MVK_CONFIG_FORCE_LOW_POWER_GPU; + bool forceLowPower; MVK_SET_FROM_ENV_OR_BUILD_BOOL(forceLowPower, MVK_CONFIG_FORCE_LOW_POWER_GPU); // Populate the array of appropriate MTLDevices diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKQueue.h b/MoltenVK/MoltenVK/GPUObjects/MVKQueue.h index 9dda53c45..c705a9df8 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKQueue.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKQueue.h @@ -228,6 +228,6 @@ class MVKQueuePresentSurfaceSubmission : public MVKQueueSubmission { protected: id getMTLCommandBuffer(); - MVKVectorInline _surfaceImages; + MVKVectorInline _presentableImages; }; diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm b/MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm index 794c9442d..4c71d02af 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm @@ -339,7 +339,7 @@ // The semaphores know what to do. id mtlCmdBuff = getMTLCommandBuffer(); for (auto& ws : _waitSemaphores) { ws->encodeWait(mtlCmdBuff); } - for (auto& si : _surfaceImages) { si->presentCAMetalDrawable(mtlCmdBuff); } + for (auto& img : _presentableImages) { img->presentCAMetalDrawable(mtlCmdBuff); } for (auto& ws : _waitSemaphores) { ws->encodeWait(nil); } [mtlCmdBuff commit]; @@ -363,10 +363,10 @@ : MVKQueueSubmission(queue, pPresentInfo->waitSemaphoreCount, pPresentInfo->pWaitSemaphores) { // Populate the array of swapchain images, testing each one for a change in surface size - _surfaceImages.reserve(pPresentInfo->swapchainCount); + _presentableImages.reserve(pPresentInfo->swapchainCount); for (uint32_t i = 0; i < pPresentInfo->swapchainCount; i++) { MVKSwapchain* mvkSC = (MVKSwapchain*)pPresentInfo->pSwapchains[i]; - _surfaceImages.push_back(mvkSC->getImage(pPresentInfo->pImageIndices[i])); + _presentableImages.push_back(mvkSC->getPresentableImage(pPresentInfo->pImageIndices[i])); // Surface loss takes precedence over out-of-date errors. if (mvkSC->getIsSurfaceLost()) { setConfigurationResult(VK_ERROR_SURFACE_LOST_KHR); diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.h b/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.h index 8b88c6441..8a3304594 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.h @@ -42,10 +42,10 @@ class MVKSwapchain : public MVKVulkanAPIDeviceObject { VkDebugReportObjectTypeEXT getVkDebugReportObjectType() override { return VK_DEBUG_REPORT_OBJECT_TYPE_SWAPCHAIN_KHR_EXT; } /** Returns the number of images in this swapchain. */ - inline uint32_t getImageCount() { return (uint32_t)_surfaceImages.size(); } + inline uint32_t getImageCount() { return (uint32_t)_presentableImages.size(); } /** Returns the image at the specified index. */ - inline MVKSwapchainImage* getImage(uint32_t index) { return _surfaceImages[index]; } + inline MVKPresentableSwapchainImage* getPresentableImage(uint32_t index) { return _presentableImages[index]; } /** * Returns the array of presentable images associated with this swapchain. @@ -89,7 +89,7 @@ class MVKSwapchain : public MVKVulkanAPIDeviceObject { ~MVKSwapchain() override; protected: - friend class MVKSwapchainImage; + friend class MVKPresentableSwapchainImage; void propogateDebugName() override; void initCAMetalLayer(const VkSwapchainCreateInfoKHR* pCreateInfo, uint32_t imgCnt); @@ -103,7 +103,7 @@ class MVKSwapchain : public MVKVulkanAPIDeviceObject { CAMetalLayer* _mtlLayer; MVKWatermark* _licenseWatermark; - MVKVectorInline _surfaceImages; + MVKVectorInline _presentableImages; std::atomic _currentAcquisitionID; CGSize _mtlLayerOrigDrawSize; MVKSwapchainPerformance _performanceStatistics; diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.mm b/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.mm index b93d3b94d..ddf6d27f0 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.mm @@ -40,10 +40,10 @@ void MVKSwapchain::propogateDebugName() { if (_debugName) { - size_t imgCnt = _surfaceImages.size(); + size_t imgCnt = _presentableImages.size(); for (size_t imgIdx = 0; imgIdx < imgCnt; imgIdx++) { NSString* nsName = [[NSString alloc] initWithFormat: @"%@(%lu)", _debugName, imgIdx]; // temp retain - _surfaceImages[imgIdx]->setDebugName(nsName.UTF8String); + _presentableImages[imgIdx]->setDebugName(nsName.UTF8String); [nsName release]; // release temp string } } @@ -66,7 +66,7 @@ // Now populate the images for (uint32_t imgIdx = 0; imgIdx < *pCount; imgIdx++) { - pSwapchainImages[imgIdx] = (VkImage)_surfaceImages[imgIdx]; + pSwapchainImages[imgIdx] = (VkImage)_presentableImages[imgIdx]; } return result; @@ -80,14 +80,12 @@ if ( getIsSurfaceLost() ) { return VK_ERROR_SURFACE_LOST_KHR; } - // Find the image that has the smallest availability measure - MVKSwapchainImage* minWaitImage = nullptr; - MVKSwapchainImageAvailability minAvailability = { .acquisitionID = kMVKUndefinedLargeUInt64, - .waitCount = kMVKUndefinedLargeUInt32, - .isAvailable = false }; + // Find the image that has the shortest wait by finding the smallest availability measure. + MVKPresentableSwapchainImage* minWaitImage = nullptr; + MVKSwapchainImageAvailability minAvailability = { kMVKUndefinedLargeUInt64, false }; uint32_t imgCnt = getImageCount(); for (uint32_t imgIdx = 0; imgIdx < imgCnt; imgIdx++) { - MVKSwapchainImage* img = getImage(imgIdx); + auto* img = getPresentableImage(imgIdx); auto imgAvail = img->getAvailability(); if (imgAvail < minAvailability) { minAvailability = imgAvail; @@ -95,10 +93,10 @@ } } - // Return the index of the image with the shortest wait and signal the semaphore and fence when it's available + // Return the index of the image with the shortest wait, + // and signal the semaphore and fence when it's available *pImageIndex = minWaitImage->_swapchainIndex; - minWaitImage->resetMetalDrawable(); - minWaitImage->signalWhenAvailable((MVKSemaphore*)semaphore, (MVKFence*)fence); + minWaitImage->acquireAndSignalWhenAvailable((MVKSemaphore*)semaphore, (MVKFence*)fence); return getHasSurfaceSizeChanged() ? VK_ERROR_OUT_OF_DATE_KHR : VK_SUCCESS; } @@ -384,7 +382,7 @@ static inline CIE1931XY VkXYColorEXTToCIE1931XY(VkXYColorEXT xy) { } for (uint32_t imgIdx = 0; imgIdx < imgCnt; imgIdx++) { - _surfaceImages.push_back(_device->createSwapchainImage(&imgInfo, this, imgIdx, NULL)); + _presentableImages.push_back(_device->createPresentableSwapchainImage(&imgInfo, this, imgIdx, NULL)); } MVKLogInfo("Created %d swapchain images with initial size (%d, %d).", imgCnt, imgExtent.width, imgExtent.height); @@ -405,7 +403,7 @@ static inline CIE1931XY VkXYColorEXTToCIE1931XY(VkXYColorEXT xy) { } MVKSwapchain::~MVKSwapchain() { - for (auto& img : _surfaceImages) { _device->destroySwapchainImage(img, NULL); } + for (auto& img : _presentableImages) { _device->destroyPresentableSwapchainImage(img, NULL); } if (_licenseWatermark) { _licenseWatermark->destroy(); } [this->_layerObserver release];