From 90eb1af19f2351329d7025e8c121dda3fd7a15ca Mon Sep 17 00:00:00 2001 From: Bill Hollings Date: Sat, 2 Dec 2023 19:20:31 -0500 Subject: [PATCH] Add support for extension VK_EXT_headless_surface. - Consolidate info about CAMetalLayer and headless in MVKSurface. - MVKSwapchainImage remove getCAMetalDrawable() and focus on abstracting getMTLTexture(). - MVKPresentableSwapchainImage::getCAMetalDrawable() return nil if headless. - Add MVKPresentableSwapchainImage::_mtlTextureHeadless to support a fixed MTLTexture that is not retrieved from a CAMetalDrawable. - MVKPresentableSwapchainImage refactor signalling semaphores and fences. - MVKPresentableSwapchainImage don't lock when signalling semaphores and fences. - If no present occurs, actualPresentTime will be zero. Set it to current time, instead of to desiredPresentTime, since it's more accurate. - Rework timestamps: - Remove _mvkTimestampBase so mvkGetTimestamp() is equal to mach_absolute_time(), which is used in presentation timing. - Add mvkGetRuntimeNanoseconds(). - Rename mvkGetAbsoluteTime() to mvkGetContinuousNanoseconds(). - Remove mvkGetTimestampPeriod() as unused. - MVKSemaphoreMTLEvent::encodeDeferredSignal remove redundant nil test (unrelated). - Fix swapchain and surface bugs when windowing system is accessed from off the main thread (unrelated). - Log warning when deprecated functions vkCreateMacOSSurfaceMVK() or vkCreateIOSSurfaceMVK() are used (unrelated). - Remove documentation for visionos, as support is not ready (unrelated). --- Common/MVKOSExtensions.h | 42 ++++--- Common/MVKOSExtensions.mm | 43 +++---- Docs/MoltenVK_Runtime_UserGuide.md | 3 + Docs/Whats_New.md | 2 + MoltenVK/MoltenVK.xcodeproj/project.pbxproj | 20 ++-- MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm | 27 ++--- MoltenVK/MoltenVK/GPUObjects/MVKImage.h | 20 +--- MoltenVK/MoltenVK/GPUObjects/MVKImage.mm | 106 ++++++++++-------- MoltenVK/MoltenVK/GPUObjects/MVKInstance.h | 3 + MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm | 8 +- .../MoltenVK/GPUObjects/MVKPixelFormats.h | 2 +- MoltenVK/MoltenVK/GPUObjects/MVKSurface.h | 26 +++-- MoltenVK/MoltenVK/GPUObjects/MVKSurface.mm | 56 +++++++-- MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.h | 24 ++-- MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.mm | 102 +++++++++-------- MoltenVK/MoltenVK/GPUObjects/MVKSync.mm | 2 +- MoltenVK/MoltenVK/Layers/MVKExtensions.def | 1 + MoltenVK/MoltenVK/OS/CAMetalLayer+MoltenVK.h | 13 ++- ...er+MoltenVK.m => CAMetalLayer+MoltenVK.mm} | 8 ++ MoltenVK/MoltenVK/Vulkan/vulkan.mm | 20 ++++ README.md | 20 +--- 21 files changed, 312 insertions(+), 236 deletions(-) rename MoltenVK/MoltenVK/OS/{CAMetalLayer+MoltenVK.m => CAMetalLayer+MoltenVK.mm} (92%) diff --git a/Common/MVKOSExtensions.h b/Common/MVKOSExtensions.h index f9faba912..2c40602a3 100644 --- a/Common/MVKOSExtensions.h +++ b/Common/MVKOSExtensions.h @@ -24,6 +24,9 @@ #include +#pragma mark - +#pragma mark Operating System versions + typedef float MVKOSVersion; /*** Constant indicating unsupported functionality in an OS. */ @@ -66,20 +69,31 @@ static inline bool mvkOSVersionIsAtLeast(MVKOSVersion macOSMinVer, #endif } + +#pragma mark - +#pragma mark Timestamps + /** - * Returns a monotonic timestamp value for use in Vulkan and performance timestamping. + * Returns a monotonic tick value for use in Vulkan and performance timestamping. * - * The returned value corresponds to the number of CPU "ticks" since the app was initialized. - * - * Calling this value twice, subtracting the first value from the second, and then multiplying - * the result by the value returned by mvkGetTimestampPeriod() will provide an indication of the - * number of nanoseconds between the two calls. The convenience function mvkGetElapsedMilliseconds() - * can be used to perform this calculation. + * The returned value corresponds to the number of CPU ticks since an arbitrary + * point in the past, and does not increment while the system is asleep. */ uint64_t mvkGetTimestamp(); -/** Returns the number of nanoseconds between each increment of the value returned by mvkGetTimestamp(). */ -double mvkGetTimestampPeriod(); +/** + * Returns the number of runtime nanoseconds since an arbitrary point in the past, + * excluding any time spent while the system is asleep. + * + * This value corresponds to the timestamps returned by Metal presentation timings. + */ +uint64_t mvkGetRuntimeNanoseconds(); + +/** + * Returns the number of nanoseconds since an arbitrary point in the past, + * including any time spent while the system is asleep. + */ +uint64_t mvkGetContinuousNanoseconds(); /** * Returns the number of nanoseconds elapsed between startTimestamp and endTimestamp, @@ -97,12 +111,6 @@ uint64_t mvkGetElapsedNanoseconds(uint64_t startTimestamp = 0, uint64_t endTimes */ double mvkGetElapsedMilliseconds(uint64_t startTimestamp = 0, uint64_t endTimestamp = 0); -/** Returns the current absolute time in nanoseconds. */ -uint64_t mvkGetAbsoluteTime(); - -/** Ensures the block is executed on the main thread. */ -void mvkDispatchToMainAndWait(dispatch_block_t block); - #pragma mark - #pragma mark Process environment @@ -141,8 +149,12 @@ uint64_t mvkGetUsedMemorySize(); /** Returns the size of a page of host memory on this platform. */ uint64_t mvkGetHostMemoryPageSize(); + #pragma mark - #pragma mark Threading /** Returns the amount of avaliable CPU cores. */ uint32_t mvkGetAvaliableCPUCores(); + +/** Ensures the block is executed on the main thread. */ +void mvkDispatchToMainAndWait(dispatch_block_t block); diff --git a/Common/MVKOSExtensions.mm b/Common/MVKOSExtensions.mm index 93025f2bc..8d33f3d48 100644 --- a/Common/MVKOSExtensions.mm +++ b/Common/MVKOSExtensions.mm @@ -29,6 +29,10 @@ using namespace std; + +#pragma mark - +#pragma mark Operating System versions + MVKOSVersion mvkOSVersion() { static MVKOSVersion _mvkOSVersion = 0; if ( !_mvkOSVersion ) { @@ -38,43 +42,35 @@ MVKOSVersion mvkOSVersion() { return _mvkOSVersion; } -static uint64_t _mvkTimestampBase; -static double _mvkTimestampPeriod; + +#pragma mark - +#pragma mark Timestamps + static mach_timebase_info_data_t _mvkMachTimebase; -uint64_t mvkGetTimestamp() { return mach_absolute_time() - _mvkTimestampBase; } +uint64_t mvkGetTimestamp() { return mach_absolute_time(); } + +uint64_t mvkGetRuntimeNanoseconds() { return mach_absolute_time() * _mvkMachTimebase.numer / _mvkMachTimebase.denom; } -double mvkGetTimestampPeriod() { return _mvkTimestampPeriod; } +uint64_t mvkGetContinuousNanoseconds() { return mach_continuous_time() * _mvkMachTimebase.numer / _mvkMachTimebase.denom; } uint64_t mvkGetElapsedNanoseconds(uint64_t startTimestamp, uint64_t endTimestamp) { if (endTimestamp == 0) { endTimestamp = mvkGetTimestamp(); } - return (endTimestamp - startTimestamp) * _mvkTimestampPeriod; + return (endTimestamp - startTimestamp) * _mvkMachTimebase.numer / _mvkMachTimebase.denom; } double mvkGetElapsedMilliseconds(uint64_t startTimestamp, uint64_t endTimestamp) { return mvkGetElapsedNanoseconds(startTimestamp, endTimestamp) / 1e6; } -uint64_t mvkGetAbsoluteTime() { return mach_continuous_time() * _mvkMachTimebase.numer / _mvkMachTimebase.denom; } - -// Initialize timestamping capabilities on app startup. -//Called automatically when the framework is loaded and initialized. +// Initialize timestamp capabilities on app startup. +// Called automatically when the framework is loaded and initialized. static bool _mvkTimestampsInitialized = false; __attribute__((constructor)) static void MVKInitTimestamps() { if (_mvkTimestampsInitialized ) { return; } _mvkTimestampsInitialized = true; - _mvkTimestampBase = mach_absolute_time(); mach_timebase_info(&_mvkMachTimebase); - _mvkTimestampPeriod = (double)_mvkMachTimebase.numer / (double)_mvkMachTimebase.denom; -} - -void mvkDispatchToMainAndWait(dispatch_block_t block) { - if (NSThread.isMainThread) { - block(); - } else { - dispatch_sync(dispatch_get_main_queue(), block); - } } @@ -145,6 +141,7 @@ uint64_t mvkGetUsedMemorySize() { uint64_t mvkGetHostMemoryPageSize() { return sysconf(_SC_PAGESIZE); } + #pragma mark - #pragma mark Threading @@ -152,3 +149,11 @@ uint64_t mvkGetUsedMemorySize() { uint32_t mvkGetAvaliableCPUCores() { return (uint32_t)[[NSProcessInfo processInfo] activeProcessorCount]; } + +void mvkDispatchToMainAndWait(dispatch_block_t block) { + if (NSThread.isMainThread) { + block(); + } else { + dispatch_sync(dispatch_get_main_queue(), block); + } +} diff --git a/Docs/MoltenVK_Runtime_UserGuide.md b/Docs/MoltenVK_Runtime_UserGuide.md index 9b00360e3..ee93c8010 100644 --- a/Docs/MoltenVK_Runtime_UserGuide.md +++ b/Docs/MoltenVK_Runtime_UserGuide.md @@ -380,6 +380,9 @@ In addition to core *Vulkan* functionality, **MoltenVK** also supports the foll - `VK_EXT_external_memory_host` - `VK_EXT_fragment_shader_interlock` - *Requires Metal 2.0 and Raster Order Groups.* +- `VK_EXT_hdr_metadata` + - *macOS only.* +- `VK_EXT_headless_surface` - `VK_EXT_host_query_reset` - `VK_EXT_image_robustness` - `VK_EXT_inline_uniform_block` diff --git a/Docs/Whats_New.md b/Docs/Whats_New.md index e0abbf708..ebafdde45 100644 --- a/Docs/Whats_New.md +++ b/Docs/Whats_New.md @@ -20,11 +20,13 @@ Released TBD - Add support for extensions: - `VK_EXT_extended_dynamic_state3` *(Metal does not support `VK_POLYGON_MODE_POINT`)* + - `VK_EXT_headless_surface` - Fix regression that broke `VK_POLYGON_MODE_LINE`. - Fix regression in marking rendering state dirty after `vkCmdClearAttachments()`. - Reduce disk space consumed after running `fetchDependencies` script by removing intermediate file caches. - Fix rare deadlock during launch via `dlopen()`. - Fix initial value of `VkPhysicalDeviceLimits::timestampPeriod` on non-Apple Silicon GPUs. +- Fix swapchain and surface bugs when windowing system is accessed from off the main thread. - Update to latest SPIRV-Cross: - MSL: Fix regression error in argument buffer runtime arrays. diff --git a/MoltenVK/MoltenVK.xcodeproj/project.pbxproj b/MoltenVK/MoltenVK.xcodeproj/project.pbxproj index 46ff50fc3..be7ca3253 100644 --- a/MoltenVK/MoltenVK.xcodeproj/project.pbxproj +++ b/MoltenVK/MoltenVK.xcodeproj/project.pbxproj @@ -117,7 +117,7 @@ 2FEA0AAF24902F9F00EEF3AD /* MVKLayers.mm in Sources */ = {isa = PBXBuildFile; fileRef = A94FB7A11C7DFB4800632CA3 /* MVKLayers.mm */; }; 2FEA0AB024902F9F00EEF3AD /* MVKFramebuffer.mm in Sources */ = {isa = PBXBuildFile; fileRef = A94FB7881C7DFB4800632CA3 /* MVKFramebuffer.mm */; }; 2FEA0AB124902F9F00EEF3AD /* MVKMTLBufferAllocation.mm in Sources */ = {isa = PBXBuildFile; fileRef = A9C96DCF1DDC20C20053187F /* MVKMTLBufferAllocation.mm */; }; - 2FEA0AB224902F9F00EEF3AD /* CAMetalLayer+MoltenVK.m in Sources */ = {isa = PBXBuildFile; fileRef = A9E53DD62100B197002781DD /* CAMetalLayer+MoltenVK.m */; }; + 2FEA0AB224902F9F00EEF3AD /* CAMetalLayer+MoltenVK.mm in Sources */ = {isa = PBXBuildFile; fileRef = A9E53DD62100B197002781DD /* CAMetalLayer+MoltenVK.mm */; }; 2FEA0AB324902F9F00EEF3AD /* MVKCmdDispatch.mm in Sources */ = {isa = PBXBuildFile; fileRef = A9096E5D1F81E16300DFBEA6 /* MVKCmdDispatch.mm */; }; 2FEA0AB424902F9F00EEF3AD /* MVKCmdDebug.mm in Sources */ = {isa = PBXBuildFile; fileRef = A99C90ED229455B300A061DA /* MVKCmdDebug.mm */; }; 45003E73214AD4E500E989CB /* MVKExtensions.def in Headers */ = {isa = PBXBuildFile; fileRef = 45003E6F214AD4C900E989CB /* MVKExtensions.def */; }; @@ -360,8 +360,8 @@ A9E53DE62100B197002781DD /* NSString+MoltenVK.mm in Sources */ = {isa = PBXBuildFile; fileRef = A9E53DD42100B197002781DD /* NSString+MoltenVK.mm */; }; A9E53DE72100B197002781DD /* MTLTextureDescriptor+MoltenVK.m in Sources */ = {isa = PBXBuildFile; fileRef = A9E53DD52100B197002781DD /* MTLTextureDescriptor+MoltenVK.m */; }; A9E53DE82100B197002781DD /* MTLTextureDescriptor+MoltenVK.m in Sources */ = {isa = PBXBuildFile; fileRef = A9E53DD52100B197002781DD /* MTLTextureDescriptor+MoltenVK.m */; }; - A9E53DE92100B197002781DD /* CAMetalLayer+MoltenVK.m in Sources */ = {isa = PBXBuildFile; fileRef = A9E53DD62100B197002781DD /* CAMetalLayer+MoltenVK.m */; }; - A9E53DEA2100B197002781DD /* CAMetalLayer+MoltenVK.m in Sources */ = {isa = PBXBuildFile; fileRef = A9E53DD62100B197002781DD /* CAMetalLayer+MoltenVK.m */; }; + A9E53DE92100B197002781DD /* CAMetalLayer+MoltenVK.mm in Sources */ = {isa = PBXBuildFile; fileRef = A9E53DD62100B197002781DD /* CAMetalLayer+MoltenVK.mm */; }; + A9E53DEA2100B197002781DD /* CAMetalLayer+MoltenVK.mm in Sources */ = {isa = PBXBuildFile; fileRef = A9E53DD62100B197002781DD /* CAMetalLayer+MoltenVK.mm */; }; A9E53DF32100B302002781DD /* MTLRenderPassDescriptor+MoltenVK.h in Headers */ = {isa = PBXBuildFile; fileRef = A9E53DEE2100B302002781DD /* MTLRenderPassDescriptor+MoltenVK.h */; }; A9E53DF42100B302002781DD /* MTLRenderPassDescriptor+MoltenVK.h in Headers */ = {isa = PBXBuildFile; fileRef = A9E53DEE2100B302002781DD /* MTLRenderPassDescriptor+MoltenVK.h */; }; A9E53DF52100B302002781DD /* MTLRenderPassDescriptor+MoltenVK.m in Sources */ = {isa = PBXBuildFile; fileRef = A9E53DF22100B302002781DD /* MTLRenderPassDescriptor+MoltenVK.m */; }; @@ -495,7 +495,7 @@ DCFD7F572A45BC6E007BBBF7 /* MVKFramebuffer.mm in Sources */ = {isa = PBXBuildFile; fileRef = A94FB7881C7DFB4800632CA3 /* MVKFramebuffer.mm */; }; DCFD7F582A45BC6E007BBBF7 /* MTLRenderPassStencilAttachmentDescriptor+MoltenVK.m in Sources */ = {isa = PBXBuildFile; fileRef = 453638302508A4C6000EFFD3 /* MTLRenderPassStencilAttachmentDescriptor+MoltenVK.m */; }; DCFD7F592A45BC6E007BBBF7 /* MVKMTLBufferAllocation.mm in Sources */ = {isa = PBXBuildFile; fileRef = A9C96DCF1DDC20C20053187F /* MVKMTLBufferAllocation.mm */; }; - DCFD7F5A2A45BC6E007BBBF7 /* CAMetalLayer+MoltenVK.m in Sources */ = {isa = PBXBuildFile; fileRef = A9E53DD62100B197002781DD /* CAMetalLayer+MoltenVK.m */; }; + DCFD7F5A2A45BC6E007BBBF7 /* CAMetalLayer+MoltenVK.mm in Sources */ = {isa = PBXBuildFile; fileRef = A9E53DD62100B197002781DD /* CAMetalLayer+MoltenVK.mm */; }; DCFD7F5B2A45BC6E007BBBF7 /* MVKCmdDispatch.mm in Sources */ = {isa = PBXBuildFile; fileRef = A9096E5D1F81E16300DFBEA6 /* MVKCmdDispatch.mm */; }; DCFD7F5C2A45BC6E007BBBF7 /* MVKCmdDebug.mm in Sources */ = {isa = PBXBuildFile; fileRef = A99C90ED229455B300A061DA /* MVKCmdDebug.mm */; }; /* End PBXBuildFile section */ @@ -691,7 +691,7 @@ A9E53DD32100B197002781DD /* MTLSamplerDescriptor+MoltenVK.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MTLSamplerDescriptor+MoltenVK.h"; sourceTree = ""; }; A9E53DD42100B197002781DD /* NSString+MoltenVK.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSString+MoltenVK.mm"; sourceTree = ""; }; A9E53DD52100B197002781DD /* MTLTextureDescriptor+MoltenVK.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MTLTextureDescriptor+MoltenVK.m"; sourceTree = ""; }; - A9E53DD62100B197002781DD /* CAMetalLayer+MoltenVK.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "CAMetalLayer+MoltenVK.m"; sourceTree = ""; }; + A9E53DD62100B197002781DD /* CAMetalLayer+MoltenVK.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "CAMetalLayer+MoltenVK.mm"; sourceTree = ""; }; A9E53DEE2100B302002781DD /* MTLRenderPassDescriptor+MoltenVK.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MTLRenderPassDescriptor+MoltenVK.h"; sourceTree = ""; }; A9E53DF22100B302002781DD /* MTLRenderPassDescriptor+MoltenVK.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MTLRenderPassDescriptor+MoltenVK.m"; sourceTree = ""; }; A9E53DFA21064F84002781DD /* MTLRenderPipelineDescriptor+MoltenVK.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MTLRenderPipelineDescriptor+MoltenVK.m"; sourceTree = ""; }; @@ -889,7 +889,7 @@ isa = PBXGroup; children = ( A9E53DD12100B197002781DD /* CAMetalLayer+MoltenVK.h */, - A9E53DD62100B197002781DD /* CAMetalLayer+MoltenVK.m */, + A9E53DD62100B197002781DD /* CAMetalLayer+MoltenVK.mm */, 453638312508A4C7000EFFD3 /* MTLRenderPassDepthAttachmentDescriptor+MoltenVK.h */, 4536382F2508A4C6000EFFD3 /* MTLRenderPassDepthAttachmentDescriptor+MoltenVK.m */, A9E53DEE2100B302002781DD /* MTLRenderPassDescriptor+MoltenVK.h */, @@ -1703,7 +1703,7 @@ 2FEA0AAF24902F9F00EEF3AD /* MVKLayers.mm in Sources */, 2FEA0AB024902F9F00EEF3AD /* MVKFramebuffer.mm in Sources */, 2FEA0AB124902F9F00EEF3AD /* MVKMTLBufferAllocation.mm in Sources */, - 2FEA0AB224902F9F00EEF3AD /* CAMetalLayer+MoltenVK.m in Sources */, + 2FEA0AB224902F9F00EEF3AD /* CAMetalLayer+MoltenVK.mm in Sources */, 2FEA0AB324902F9F00EEF3AD /* MVKCmdDispatch.mm in Sources */, 2FEA0AB424902F9F00EEF3AD /* MVKCmdDebug.mm in Sources */, ); @@ -1763,7 +1763,7 @@ A94FB7EE1C7DFB4800632CA3 /* MVKFramebuffer.mm in Sources */, 453638382508A4C7000EFFD3 /* MTLRenderPassStencilAttachmentDescriptor+MoltenVK.m in Sources */, A9C96DD21DDC20C20053187F /* MVKMTLBufferAllocation.mm in Sources */, - A9E53DE92100B197002781DD /* CAMetalLayer+MoltenVK.m in Sources */, + A9E53DE92100B197002781DD /* CAMetalLayer+MoltenVK.mm in Sources */, A9096E5E1F81E16300DFBEA6 /* MVKCmdDispatch.mm in Sources */, A99C90F0229455B300A061DA /* MVKCmdDebug.mm in Sources */, ); @@ -1823,7 +1823,7 @@ A94FB7EF1C7DFB4800632CA3 /* MVKFramebuffer.mm in Sources */, 4536383A2508A4C7000EFFD3 /* MTLRenderPassStencilAttachmentDescriptor+MoltenVK.m in Sources */, A9C96DD31DDC20C20053187F /* MVKMTLBufferAllocation.mm in Sources */, - A9E53DEA2100B197002781DD /* CAMetalLayer+MoltenVK.m in Sources */, + A9E53DEA2100B197002781DD /* CAMetalLayer+MoltenVK.mm in Sources */, A9096E5F1F81E16300DFBEA6 /* MVKCmdDispatch.mm in Sources */, A99C90F1229455B300A061DA /* MVKCmdDebug.mm in Sources */, ); @@ -1883,7 +1883,7 @@ DCFD7F572A45BC6E007BBBF7 /* MVKFramebuffer.mm in Sources */, DCFD7F582A45BC6E007BBBF7 /* MTLRenderPassStencilAttachmentDescriptor+MoltenVK.m in Sources */, DCFD7F592A45BC6E007BBBF7 /* MVKMTLBufferAllocation.mm in Sources */, - DCFD7F5A2A45BC6E007BBBF7 /* CAMetalLayer+MoltenVK.m in Sources */, + DCFD7F5A2A45BC6E007BBBF7 /* CAMetalLayer+MoltenVK.mm in Sources */, DCFD7F5B2A45BC6E007BBBF7 /* MVKCmdDispatch.mm in Sources */, DCFD7F5C2A45BC6E007BBBF7 /* MVKCmdDebug.mm in Sources */, ); diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm index cbbbfab4e..401c880f9 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm @@ -1204,8 +1204,8 @@ isHeadless = getMTLDevice().isHeadless; #endif - // If this device is headless or the surface does not have a CAMetalLayer, it is not supported. - *pSupported = !(isHeadless || (surface->getCAMetalLayer() == nil)); + // If this device is headless, the surface must be headless. + *pSupported = isHeadless ? surface->isHeadless() : wasConfigurationSuccessful(); return *pSupported ? VK_SUCCESS : surface->getConfigurationResult(); } @@ -1264,13 +1264,12 @@ // The CAlayer underlying the surface must be a CAMetalLayer. MVKSurface* surface = (MVKSurface*)pSurfaceInfo->surface; - CAMetalLayer* mtlLayer = surface->getCAMetalLayer(); - if ( !mtlLayer ) { return surface->getConfigurationResult(); } + if ( !surface->wasConfigurationSuccessful() ) { return surface->getConfigurationResult(); } VkSurfaceCapabilitiesKHR& surfCaps = pSurfaceCapabilities->surfaceCapabilities; surfCaps.minImageCount = _metalFeatures.minSwapchainImageCount; surfCaps.maxImageCount = _metalFeatures.maxSwapchainImageCount; - surfCaps.currentExtent = mvkGetNaturalExtent(mtlLayer); + surfCaps.currentExtent = surface->getNaturalExtent(); surfCaps.minImageExtent = { 1, 1 }; surfCaps.maxImageExtent = { _properties.limits.maxImageDimension2D, _properties.limits.maxImageDimension2D }; surfCaps.maxImageArrayLayers = 1; @@ -1349,9 +1348,7 @@ uint32_t* pCount, VkSurfaceFormatKHR* pSurfaceFormats) { - // The layer underlying the surface view must be a CAMetalLayer. - CAMetalLayer* mtlLayer = surface->getCAMetalLayer(); - if ( !mtlLayer ) { return surface->getConfigurationResult(); } + if ( !surface->wasConfigurationSuccessful() ) { return surface->getConfigurationResult(); } #define addSurfFmt(MTL_FMT) \ do { \ @@ -1474,9 +1471,7 @@ uint32_t* pCount, VkPresentModeKHR* pPresentModes) { - // The layer underlying the surface view must be a CAMetalLayer. - CAMetalLayer* mtlLayer = surface->getCAMetalLayer(); - if ( !mtlLayer ) { return surface->getConfigurationResult(); } + if ( !surface->wasConfigurationSuccessful() ) { return surface->getConfigurationResult(); } #define ADD_VK_PRESENT_MODE(VK_PM) \ do { \ @@ -1504,9 +1499,7 @@ uint32_t* pRectCount, VkRect2D* pRects) { - // The layer underlying the surface view must be a CAMetalLayer. - CAMetalLayer* mtlLayer = surface->getCAMetalLayer(); - if ( !mtlLayer ) { return surface->getConfigurationResult(); } + if ( !surface->wasConfigurationSuccessful() ) { return surface->getConfigurationResult(); } if ( !pRects ) { *pRectCount = 1; @@ -1518,7 +1511,7 @@ *pRectCount = 1; pRects[0].offset = { 0, 0 }; - pRects[0].extent = mvkGetNaturalExtent(mtlLayer); + pRects[0].extent = surface->getNaturalExtent(); return VK_SUCCESS; } @@ -3666,14 +3659,14 @@ static uint32_t mvkGetEntryProperty(io_registry_entry_t entry, CFStringRef prope MTLTimestamp cpuStamp, gpuStamp; uint64_t cpuStart, cpuEnd; - cpuStart = mvkGetAbsoluteTime(); + cpuStart = mvkGetContinuousNanoseconds(); [getMTLDevice() sampleTimestamps: &cpuStamp gpuTimestamp: &gpuStamp]; // Sample again to calculate the maximum deviation. Note that the // -[MTLDevice sampleTimestamps:gpuTimestamp:] method guarantees that CPU // timestamps are in nanoseconds. We don't want to call the method again, // because that could result in an expensive syscall to query the GPU time- // stamp. - cpuEnd = mvkGetAbsoluteTime(); + cpuEnd = mvkGetContinuousNanoseconds(); for (uint32_t tsIdx = 0; tsIdx < timestampCount; ++tsIdx) { switch (pTimestampInfos[tsIdx].timeDomain) { case VK_TIME_DOMAIN_DEVICE_EXT: diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.h b/MoltenVK/MoltenVK/GPUObjects/MVKImage.h index 900b10ffa..058876d6e 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.h @@ -378,14 +378,8 @@ class MVKSwapchainImage : public MVKImage { public: - /** Binds this resource to the specified offset within the specified memory allocation. */ VkResult bindDeviceMemory(MVKDeviceMemory* mvkMem, VkDeviceSize memOffset, uint8_t planeIndex) override; -#pragma mark Metal - - /** Returns the Metal texture used by the CAMetalDrawable underlying this image. */ - id getMTLTexture(uint8_t planeIndex) override; - #pragma mark Construction @@ -399,7 +393,6 @@ class MVKSwapchainImage : public MVKImage { protected: friend class MVKPeerSwapchainImage; - virtual id getCAMetalDrawable() = 0; void detachSwapchain(); std::mutex _detachmentLock; @@ -445,6 +438,8 @@ class MVKPresentableSwapchainImage : public MVKSwapchainImage { #pragma mark Metal + id getMTLTexture(uint8_t planeIndex) override; + /** Presents the contained drawable to the OS. */ VkResult presentCAMetalDrawable(id mtlCmdBuff, MVKImagePresentInfo presentInfo); @@ -468,16 +463,16 @@ class MVKPresentableSwapchainImage : public MVKSwapchainImage { protected: friend MVKSwapchain; - id getCAMetalDrawable() override; + id getCAMetalDrawable(); void addPresentedHandler(id mtlDrawable, MVKImagePresentInfo presentInfo, MVKSwapchainSignaler signaler); void releaseMetalDrawable(); MVKSwapchainImageAvailability getAvailability(); - void makeAvailable(const MVKSwapchainSignaler& signaler); void makeAvailable(); VkResult acquireAndSignalWhenAvailable(MVKSemaphore* semaphore, MVKFence* fence); MVKSwapchainSignaler getPresentationSignaler(); id _mtlDrawable = nil; + id _mtlTextureHeadless = nil; MVKSwapchainImageAvailability _availability; MVKSmallVector _availabilitySignalers; MVKSwapchainSignaler _preSignaler = {}; @@ -494,7 +489,8 @@ class MVKPeerSwapchainImage : public MVKSwapchainImage { public: - /** Binds this resource according to the specified bind information. */ + id getMTLTexture(uint8_t planeIndex) override; + VkResult bindDeviceMemory2(const VkBindImageMemoryInfo* pBindInfo) override; @@ -504,10 +500,6 @@ class MVKPeerSwapchainImage : public MVKSwapchainImage { const VkImageCreateInfo* pCreateInfo, MVKSwapchain* swapchain, uint32_t swapchainIndex); - -protected: - id getCAMetalDrawable() override; - }; diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm index 323918f20..1c132a4d5 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm @@ -25,8 +25,10 @@ #include "MVKFoundation.h" #include "MVKOSExtensions.h" #include "MVKCodec.h" + #import "MTLTextureDescriptor+MoltenVK.h" #import "MTLSamplerDescriptor+MoltenVK.h" +#import "CAMetalLayer+MoltenVK.h" using namespace std; using namespace SPIRV_CROSS_NAMESPACE; @@ -1169,12 +1171,6 @@ } -#pragma mark Metal - -// Overridden to always retrieve the MTLTexture directly from the CAMetalDrawable. -id MVKSwapchainImage::getMTLTexture(uint8_t planeIndex) { return [getCAMetalDrawable() texture]; } - - #pragma mark Construction MVKSwapchainImage::MVKSwapchainImage(MVKDevice* device, @@ -1212,39 +1208,33 @@ return _availability; } -// If present, signal the semaphore for the first waiter for the given image. -static void signalPresentationSemaphore(const MVKSwapchainSignaler& signaler, id mtlCmdBuff) { - if (signaler.semaphore) { signaler.semaphore->encodeDeferredSignal(mtlCmdBuff, signaler.semaphoreSignalToken); } +// Tell the semaphore and fence that they are being tracked for future signaling. +static void track(const MVKSwapchainSignaler& signaler) { + if (signaler.semaphore) { signaler.semaphore->retain(); } + if (signaler.fence) { signaler.fence->retain(); } } -// Signal either or both of the semaphore and fence in the specified tracker pair. -static void signal(const MVKSwapchainSignaler& signaler, id mtlCmdBuff) { - if (signaler.semaphore) { signaler.semaphore->encodeDeferredSignal(mtlCmdBuff, signaler.semaphoreSignalToken); } - if (signaler.fence) { signaler.fence->signal(); } +static void signal(MVKSemaphore* semaphore, uint64_t semaphoreSignalToken, id mtlCmdBuff) { + if (semaphore) { semaphore->encodeDeferredSignal(mtlCmdBuff, semaphoreSignalToken); } } -// Tell the semaphore and fence that they are being tracked for future signaling. -static void markAsTracked(const MVKSwapchainSignaler& signaler) { - if (signaler.semaphore) { signaler.semaphore->retain(); } - if (signaler.fence) { signaler.fence->retain(); } +static void signal(MVKFence* fence) { + if (fence) { fence->signal(); } } -// Tell the semaphore and fence that they are no longer being tracked for future signaling. -static void unmarkAsTracked(const MVKSwapchainSignaler& signaler) { +// Signal the semaphore and fence and tell them that they are no longer being tracked for future signaling. +static void signalAndUntrack(const MVKSwapchainSignaler& signaler) { + signal(signaler.semaphore, signaler.semaphoreSignalToken, nil); if (signaler.semaphore) { signaler.semaphore->release(); } - if (signaler.fence) { signaler.fence->release(); } -} -static void signalAndUnmarkAsTracked(const MVKSwapchainSignaler& signaler) { - signal(signaler, nil); - unmarkAsTracked(signaler); + signal(signaler.fence); + if (signaler.fence) { signaler.fence->release(); } } VkResult MVKPresentableSwapchainImage::acquireAndSignalWhenAvailable(MVKSemaphore* semaphore, MVKFence* fence) { // 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. - // This may trigger a delayed presentation callback, which uses the _availabilityLock, also used below. releaseMetalDrawable(); lock_guard lock(_availabilityLock); @@ -1267,7 +1257,8 @@ static void signalAndUnmarkAsTracked(const MVKSwapchainSignaler& signaler) { mtlCmdBuff = _device->getAnyQueue()->getMTLCommandBuffer(kMVKCommandUseAcquireNextImage); if ( !mtlCmdBuff ) { setConfigurationResult(VK_ERROR_OUT_OF_POOL_MEMORY); } } - signal(signaler, mtlCmdBuff); + signal(signaler.semaphore, signaler.semaphoreSignalToken, mtlCmdBuff); + signal(signaler.fence); [mtlCmdBuff commit]; } @@ -1275,7 +1266,7 @@ static void signalAndUnmarkAsTracked(const MVKSwapchainSignaler& signaler) { } else { _availabilitySignalers.push_back(signaler); } - markAsTracked(signaler); + track(signaler); return getConfigurationResult(); } @@ -1284,6 +1275,9 @@ static void signalAndUnmarkAsTracked(const MVKSwapchainSignaler& signaler) { // Attempt several times to retrieve a good drawable, and set an error to trigger the // swapchain to be re-established if one cannot be retrieved. id MVKPresentableSwapchainImage::getCAMetalDrawable() { + + if (_mtlTextureHeadless) { return nil; } // If headless, there is no drawable. + if ( !_mtlDrawable ) { @autoreleasepool { bool hasInvalidFormat = false; @@ -1305,6 +1299,11 @@ static void signalAndUnmarkAsTracked(const MVKSwapchainSignaler& signaler) { return _mtlDrawable; } +// If not headless, retrieve the MTLTexture directly from the CAMetalDrawable. +id MVKPresentableSwapchainImage::getMTLTexture(uint8_t planeIndex) { + return _mtlTextureHeadless ? _mtlTextureHeadless : getCAMetalDrawable().texture; +} + // Present the drawable and make myself available only once the command buffer has completed. // Pass MVKImagePresentInfo by value because it may not exist when the callback runs. VkResult MVKPresentableSwapchainImage::presentCAMetalDrawable(id mtlCmdBuff, @@ -1343,15 +1342,13 @@ static void signalAndUnmarkAsTracked(const MVKSwapchainSignaler& signaler) { auto* fence = presentInfo.fence; if (fence) { fence->retain(); } [mtlCmdBuff addCompletedHandler: ^(id mcb) { - if (fence) { - fence->signal(); - fence->release(); - } + signal(fence); + if (fence) { fence->release(); } [mtlDrwbl release]; release(); }]; - signalPresentationSemaphore(signaler, mtlCmdBuff); + signal(signaler.semaphore, signaler.semaphoreSignalToken, mtlCmdBuff); return getConfigurationResult(); } @@ -1408,6 +1405,13 @@ static void signalAndUnmarkAsTracked(const MVKSwapchainSignaler& signaler) { void MVKPresentableSwapchainImage::endPresentation(const MVKImagePresentInfo& presentInfo, const MVKSwapchainSignaler& signaler, uint64_t actualPresentTime) { + + // If the presentation time is not available, use the current nanosecond runtime clock, + // which should be reasonably accurate (sub-ms) to the presentation time. The presentation + // time will not be available if the presentation did not actually happen, such as when + // running headless, or on a test harness that is not attached to the windowing system. + if (actualPresentTime == 0) { actualPresentTime = mvkGetRuntimeNanoseconds(); } + { // Scope to avoid deadlock if release() is run within detachment lock // If I have become detached from the swapchain, it means the swapchain, and possibly the // VkDevice, have been destroyed by the time of this callback, so do not reference them. @@ -1415,7 +1419,11 @@ static void signalAndUnmarkAsTracked(const MVKSwapchainSignaler& signaler) { if (_device) { _device->addPerformanceInterval(_device->_performanceStatistics.queue.presentSwapchains, _presentationStartTime); } if (_swapchain) { _swapchain->endPresentation(presentInfo, actualPresentTime); } } - makeAvailable(signaler); + + // 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. + signalAndUntrack(signaler); release(); } @@ -1425,15 +1433,6 @@ static void signalAndUnmarkAsTracked(const MVKSwapchainSignaler& signaler) { _mtlDrawable = nil; } -// 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 MVKPresentableSwapchainImage::makeAvailable(const MVKSwapchainSignaler& signaler) { - lock_guard lock(_availabilityLock); - - signalAndUnmarkAsTracked(signaler); -} - // Signal, untrack, and release any signalers that are tracking. // Release the drawable before the lock, as it may trigger completion callback. void MVKPresentableSwapchainImage::makeAvailable() { @@ -1441,9 +1440,9 @@ static void signalAndUnmarkAsTracked(const MVKSwapchainSignaler& signaler) { lock_guard lock(_availabilityLock); if ( !_availability.isAvailable ) { - signalAndUnmarkAsTracked(_preSignaler); + signalAndUntrack(_preSignaler); for (auto& sig : _availabilitySignalers) { - signalAndUnmarkAsTracked(sig); + signalAndUntrack(sig); } _availabilitySignalers.clear(); _availability.isAvailable = true; @@ -1460,11 +1459,26 @@ static void signalAndUnmarkAsTracked(const MVKSwapchainSignaler& signaler) { _availability.acquisitionID = _swapchain->getNextAcquisitionID(); _availability.isAvailable = true; + + if (swapchain->isHeadless()) { + @autoreleasepool { + MTLTextureDescriptor* mtlTexDesc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat: getMTLPixelFormat() + width: pCreateInfo->extent.width + height: pCreateInfo->extent.height + mipmapped: NO]; + mtlTexDesc.usageMVK = MTLTextureUsageRenderTarget; + mtlTexDesc.storageModeMVK = MTLStorageModePrivate; + + _mtlTextureHeadless = [[getMTLDevice() newTextureWithDescriptor: mtlTexDesc] retain]; // retained + } + } } void MVKPresentableSwapchainImage::destroy() { releaseMetalDrawable(); + [_mtlTextureHeadless release]; + _mtlTextureHeadless = nil; MVKSwapchainImage::destroy(); } @@ -1498,8 +1512,8 @@ static void signalAndUnmarkAsTracked(const MVKSwapchainSignaler& signaler) { #pragma mark Metal -id MVKPeerSwapchainImage::getCAMetalDrawable() { - return ((MVKSwapchainImage*)_swapchain->getPresentableImage(_swapchainIndex))->getCAMetalDrawable(); +id MVKPeerSwapchainImage::getMTLTexture(uint8_t planeIndex) { + return ((MVKSwapchainImage*)_swapchain->getPresentableImage(_swapchainIndex))->getMTLTexture(planeIndex); } diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKInstance.h b/MoltenVK/MoltenVK/GPUObjects/MVKInstance.h index f6ca8e783..6a2fa92f6 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKInstance.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKInstance.h @@ -117,6 +117,9 @@ class MVKInstance : public MVKDispatchableVulkanAPIObject { MVKSurface* createSurface(const VkMetalSurfaceCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator); + MVKSurface* createSurface(const VkHeadlessSurfaceCreateInfoEXT* pCreateInfo, + const VkAllocationCallbacks* pAllocator); + MVKSurface* createSurface(const Vk_PLATFORM_SurfaceCreateInfoMVK* pCreateInfo, const VkAllocationCallbacks* pAllocator); diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm b/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm index 543a1fe76..de9ad021a 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm @@ -102,6 +102,11 @@ return new MVKSurface(this, pCreateInfo, pAllocator); } +MVKSurface* MVKInstance::createSurface(const VkHeadlessSurfaceCreateInfoEXT* pCreateInfo, + const VkAllocationCallbacks* pAllocator) { + return new MVKSurface(this, pCreateInfo, pAllocator); +} + MVKSurface* MVKInstance::createSurface(const Vk_PLATFORM_SurfaceCreateInfoMVK* pCreateInfo, const VkAllocationCallbacks* pAllocator) { return new MVKSurface(this, pCreateInfo, pAllocator); @@ -426,6 +431,8 @@ ADD_INST_EXT_ENTRY_POINT(vkGetPhysicalDeviceSurfacePresentModesKHR, KHR_SURFACE); ADD_INST_EXT_ENTRY_POINT(vkGetPhysicalDeviceSurfaceCapabilities2KHR, KHR_GET_SURFACE_CAPABILITIES_2); ADD_INST_EXT_ENTRY_POINT(vkGetPhysicalDeviceSurfaceFormats2KHR, KHR_GET_SURFACE_CAPABILITIES_2); + ADD_INST_EXT_ENTRY_POINT(vkCreateHeadlessSurfaceEXT, EXT_HEADLESS_SURFACE); + ADD_INST_EXT_ENTRY_POINT(vkCreateMetalSurfaceEXT, EXT_METAL_SURFACE); ADD_INST_EXT_ENTRY_POINT(vkCreateDebugReportCallbackEXT, EXT_DEBUG_REPORT); ADD_INST_EXT_ENTRY_POINT(vkDestroyDebugReportCallbackEXT, EXT_DEBUG_REPORT); ADD_INST_EXT_ENTRY_POINT(vkDebugReportMessageEXT, EXT_DEBUG_REPORT); @@ -441,7 +448,6 @@ ADD_INST_EXT_ENTRY_POINT(vkCreateDebugUtilsMessengerEXT, EXT_DEBUG_UTILS); ADD_INST_EXT_ENTRY_POINT(vkDestroyDebugUtilsMessengerEXT, EXT_DEBUG_UTILS); ADD_INST_EXT_ENTRY_POINT(vkSubmitDebugUtilsMessageEXT, EXT_DEBUG_UTILS); - ADD_INST_EXT_ENTRY_POINT(vkCreateMetalSurfaceEXT, EXT_METAL_SURFACE); #ifdef VK_USE_PLATFORM_IOS_MVK ADD_INST_EXT_ENTRY_POINT(vkCreateIOSSurfaceMVK, MVK_IOS_SURFACE); diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.h b/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.h index 479965b48..5d23225b3 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.h @@ -20,7 +20,7 @@ #include "MVKBaseObject.h" #include "MVKOSExtensions.h" -#include "mvk_datatypes.h" +#include "mvk_datatypes.hpp" #include #include diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKSurface.h b/MoltenVK/MoltenVK/GPUObjects/MVKSurface.h index 5746bfbf9..453eac661 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKSurface.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKSurface.h @@ -24,16 +24,6 @@ #import #import -#ifdef VK_USE_PLATFORM_IOS_MVK -# define PLATFORM_VIEW_CLASS UIView -# import -#endif - -#ifdef VK_USE_PLATFORM_MACOS_MVK -# define PLATFORM_VIEW_CLASS NSView -# import -#endif - class MVKInstance; class MVKSwapchain; @@ -59,6 +49,14 @@ class MVKSurface : public MVKVulkanAPIObject { /** Returns the CAMetalLayer underlying this surface. */ CAMetalLayer* getCAMetalLayer(); + /** Returns the extent of this surface. */ + VkExtent2D getExtent(); + + /** Returns the extent for which the underlying CAMetalLayer will not need to be scaled when composited. */ + VkExtent2D getNaturalExtent(); + + /** Returns whether this surface is headless. */ + bool isHeadless() { return !_mtlCAMetalLayer && wasConfigurationSuccessful(); } #pragma mark Construction @@ -66,6 +64,10 @@ class MVKSurface : public MVKVulkanAPIObject { const VkMetalSurfaceCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator); + MVKSurface(MVKInstance* mvkInstance, + const VkHeadlessSurfaceCreateInfoEXT* pCreateInfo, + const VkAllocationCallbacks* pAllocator); + MVKSurface(MVKInstance* mvkInstance, const Vk_PLATFORM_SurfaceCreateInfoMVK* pCreateInfo, const VkAllocationCallbacks* pAllocator); @@ -76,7 +78,8 @@ class MVKSurface : public MVKVulkanAPIObject { friend class MVKSwapchain; void propagateDebugName() override {} - void initLayer(CAMetalLayer* mtlLayer, const char* vkFuncName); + void setActiveSwapchain(MVKSwapchain* swapchain); + void initLayer(CAMetalLayer* mtlLayer, const char* vkFuncName, bool isHeadless); void releaseLayer(); std::mutex _layerLock; @@ -84,5 +87,6 @@ class MVKSurface : public MVKVulkanAPIObject { CAMetalLayer* _mtlCAMetalLayer = nil; MVKBlockObserver* _layerObserver = nil; MVKSwapchain* _activeSwapchain = nullptr; + VkExtent2D _headlessExtent = {0xFFFFFFFF, 0xFFFFFFFF}; }; diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKSurface.mm b/MoltenVK/MoltenVK/GPUObjects/MVKSurface.mm index 3899ab69f..048557185 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKSurface.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKSurface.mm @@ -17,11 +17,26 @@ */ #include "MVKSurface.h" +#include "MVKSwapchain.h" #include "MVKInstance.h" #include "MVKFoundation.h" #include "MVKOSExtensions.h" +#include "mvk_datatypes.hpp" + +#import "CAMetalLayer+MoltenVK.h" #import "MVKBlockObserver.h" +#ifdef VK_USE_PLATFORM_IOS_MVK +# define PLATFORM_VIEW_CLASS UIView +# import +#endif + +#ifdef VK_USE_PLATFORM_MACOS_MVK +# define PLATFORM_VIEW_CLASS NSView +# import +#endif + + // We need to double-dereference the name to first convert to the platform symbol, then to a string. #define STR_PLATFORM(NAME) #NAME #define STR(NAME) STR_PLATFORM(NAME) @@ -34,38 +49,55 @@ return _mtlCAMetalLayer; } +VkExtent2D MVKSurface::getExtent() { + return _mtlCAMetalLayer ? mvkVkExtent2DFromCGSize(_mtlCAMetalLayer.drawableSize) : _headlessExtent; +} + +VkExtent2D MVKSurface::getNaturalExtent() { + return _mtlCAMetalLayer ? mvkVkExtent2DFromCGSize(_mtlCAMetalLayer.naturalDrawableSizeMVK) : _headlessExtent; +} + +// Per spec, headless surface extent is set from the swapchain. +void MVKSurface::setActiveSwapchain(MVKSwapchain* swapchain) { + _activeSwapchain = swapchain; + _headlessExtent = swapchain->getImageExtent(); +} + MVKSurface::MVKSurface(MVKInstance* mvkInstance, const VkMetalSurfaceCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator) : _mvkInstance(mvkInstance) { - initLayer((CAMetalLayer*)pCreateInfo->pLayer, "vkCreateMetalSurfaceEXT"); + initLayer((CAMetalLayer*)pCreateInfo->pLayer, "vkCreateMetalSurfaceEXT", false); +} + +MVKSurface::MVKSurface(MVKInstance* mvkInstance, + const VkHeadlessSurfaceCreateInfoEXT* pCreateInfo, + const VkAllocationCallbacks* pAllocator) : _mvkInstance(mvkInstance) { + initLayer(nil, "vkCreateHeadlessSurfaceEXT", true); } // pCreateInfo->pView can be either a CAMetalLayer or a view (NSView/UIView). MVKSurface::MVKSurface(MVKInstance* mvkInstance, const Vk_PLATFORM_SurfaceCreateInfoMVK* pCreateInfo, const VkAllocationCallbacks* pAllocator) : _mvkInstance(mvkInstance) { + MVKLogWarn("%s() is deprecated. Use vkCreateMetalSurfaceEXT() from the VK_EXT_metal_surface extension.", STR(vkCreate_PLATFORM_SurfaceMVK)); // Get the platform object contained in pView - id obj = (id)pCreateInfo->pView; - // If it's a view (NSView/UIView), extract the layer, otherwise assume it's already a CAMetalLayer. + id obj = (id)pCreateInfo->pView; if ([obj isKindOfClass: [PLATFORM_VIEW_CLASS class]]) { - obj = ((PLATFORM_VIEW_CLASS*)obj).layer; - if ( !NSThread.isMainThread ) { - MVKLogWarn("%s(): You are not calling this function from the main thread. %s should only be accessed from the main thread. When using this function outside the main thread, consider passing the CAMetalLayer itself in %s::pView, instead of the %s.", - STR(vkCreate_PLATFORM_SurfaceMVK), STR(PLATFORM_VIEW_CLASS), STR(Vk_PLATFORM_SurfaceCreateInfoMVK), STR(PLATFORM_VIEW_CLASS)); - } + __block id layer; + mvkDispatchToMainAndWait(^{ layer = ((PLATFORM_VIEW_CLASS*)obj).layer; }); + obj = layer; } // Confirm that we were provided with a CAMetalLayer - initLayer([obj isKindOfClass: CAMetalLayer.class] ? (CAMetalLayer*)obj : nil, - STR(vkCreate_PLATFORM_SurfaceMVK)); + initLayer([obj isKindOfClass: CAMetalLayer.class] ? (CAMetalLayer*)obj : nil, STR(vkCreate_PLATFORM_SurfaceMVK), false); } -void MVKSurface::initLayer(CAMetalLayer* mtlLayer, const char* vkFuncName) { +void MVKSurface::initLayer(CAMetalLayer* mtlLayer, const char* vkFuncName, bool isHeadless) { _mtlCAMetalLayer = [mtlLayer retain]; // retained - if ( !_mtlCAMetalLayer ) { setConfigurationResult(reportError(VK_ERROR_SURFACE_LOST_KHR, "%s(): On-screen rendering requires a layer of type CAMetalLayer.", vkFuncName)); } + if ( !_mtlCAMetalLayer && !isHeadless ) { setConfigurationResult(reportError(VK_ERROR_SURFACE_LOST_KHR, "%s(): On-screen rendering requires a layer of type CAMetalLayer.", vkFuncName)); } // Sometimes, the owning view can replace its CAMetalLayer. // When that happens, the app needs to recreate the surface. diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.h b/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.h index cd418bd1a..d8eb535f8 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.h @@ -23,7 +23,6 @@ #include "MVKSmallVector.h" #include -#import "CAMetalLayer+MoltenVK.h" #import class MVKWatermark; @@ -46,9 +45,15 @@ class MVKSwapchain : public MVKVulkanAPIDeviceObject { /** Returns the CAMetalLayer underlying the surface used by this swapchain. */ CAMetalLayer* getCAMetalLayer(); + /** Returns whether the surface is headless. */ + bool isHeadless(); + /** Returns the number of images in this swapchain. */ uint32_t getImageCount() { return (uint32_t)_presentableImages.size(); } + /** Returns the size of the images in this swapchain. */ + VkExtent2D getImageExtent() { return _imageExtent; } + /** Returns the image at the specified index. */ MVKPresentableSwapchainImage* getPresentableImage(uint32_t index) { return _presentableImages[index]; } @@ -126,7 +131,7 @@ class MVKSwapchain : public MVKVulkanAPIDeviceObject { std::atomic _currentAcquisitionID = 0; std::mutex _presentHistoryLock; uint64_t _lastFrameTime = 0; - VkExtent2D _mtlLayerDrawableExtent = {0, 0}; + VkExtent2D _imageExtent = {0, 0}; std::atomic _unpresentedImageCount = 0; uint32_t _currentPerfLogFrameCount = 0; uint32_t _presentHistoryCount = 0; @@ -134,18 +139,3 @@ class MVKSwapchain : public MVKVulkanAPIDeviceObject { uint32_t _presentHistoryHeadIndex = 0; bool _isDeliberatelyScaled = false; }; - - -#pragma mark - -#pragma mark Support functions - -/** - * Returns the natural extent of the CAMetalLayer. - * - * The natural extent is the size of the bounds property of the layer, - * multiplied by the contentsScale property of the layer, rounded - * to nearest integer using half-to-even rounding. - */ -static inline VkExtent2D mvkGetNaturalExtent(CAMetalLayer* mtlLayer) { - return mvkVkExtent2DFromCGSize(mtlLayer.naturalDrawableSizeMVK); -} diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.mm b/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.mm index 63c3ac783..5beeee047 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.mm @@ -26,9 +26,11 @@ #include "MVKWatermarkTextureContent.h" #include "MVKWatermarkShaderSource.h" #include "mvk_datatypes.hpp" +#include + +#import "CAMetalLayer+MoltenVK.h" #import "MVKBlockObserver.h" -#include using namespace std; @@ -49,6 +51,8 @@ CAMetalLayer* MVKSwapchain::getCAMetalLayer() { return _surface->getCAMetalLayer(); } +bool MVKSwapchain::isHeadless() { return _surface->isHeadless(); } + VkResult MVKSwapchain::getImages(uint32_t* pCount, VkImage* pSwapchainImages) { // Get the number of surface images @@ -124,16 +128,15 @@ return VK_SUCCESS; } -// This swapchain is optimally sized for the surface if the app has specified deliberate -// swapchain scaling, or the CAMetalLayer drawableSize has not changed since the swapchain -// was created, and the CAMetalLayer will not need to be scaled when composited. +// This swapchain is optimally sized for the surface if the app has specified +// deliberate swapchain scaling, or the surface extent has not changed since the +// swapchain was created, and the surface will not need to be scaled when composited. bool MVKSwapchain::hasOptimalSurface() { if (_isDeliberatelyScaled) { return true; } - auto* mtlLayer = getCAMetalLayer(); - VkExtent2D drawExtent = mvkVkExtent2DFromCGSize(mtlLayer.drawableSize); - return (mvkVkExtent2DsAreEqual(drawExtent, _mtlLayerDrawableExtent) && - mvkVkExtent2DsAreEqual(drawExtent, mvkGetNaturalExtent(mtlLayer))); + VkExtent2D surfExtent = _surface->getExtent(); + return (mvkVkExtent2DsAreEqual(surfExtent, _imageExtent) && + mvkVkExtent2DsAreEqual(surfExtent, _surface->getNaturalExtent())); } @@ -187,30 +190,29 @@ VkResult MVKSwapchain::getRefreshCycleDuration(VkRefreshCycleDurationGOOGLE *pRefreshCycleDuration) { if (_device->getConfigurationResult() != VK_SUCCESS) { return _device->getConfigurationResult(); } - auto* mtlLayer = getCAMetalLayer(); -#if MVK_VISIONOS - // TODO: See if this can be obtained from OS instead - NSInteger framesPerSecond = 90; + auto* screen = getCAMetalLayer().screenMVK; // Will be nil if headless +#if MVK_MACOS && !MVK_MACCAT + double framesPerSecond = 60; + if (screen) { + CGDirectDisplayID displayId = [[[screen deviceDescription] objectForKey:@"NSScreenNumber"] unsignedIntValue]; + CGDisplayModeRef mode = CGDisplayCopyDisplayMode(displayId); + framesPerSecond = CGDisplayModeGetRefreshRate(mode); + CGDisplayModeRelease(mode); +#if MVK_XCODE_13 + if (framesPerSecond == 0 && [screen respondsToSelector: @selector(maximumFramesPerSecond)]) + framesPerSecond = [screen maximumFramesPerSecond]; +#endif + // Builtin panels, e.g., on MacBook, report a zero refresh rate. + if (framesPerSecond == 0) + framesPerSecond = 60.0; + } #elif MVK_IOS_OR_TVOS || MVK_MACCAT NSInteger framesPerSecond = 60; - UIScreen* screen = mtlLayer.screenMVK; if ([screen respondsToSelector: @selector(maximumFramesPerSecond)]) { framesPerSecond = screen.maximumFramesPerSecond; } -#elif MVK_MACOS && !MVK_MACCAT - NSScreen* screen = mtlLayer.screenMVK; - CGDirectDisplayID displayId = [[[screen deviceDescription] objectForKey:@"NSScreenNumber"] unsignedIntValue]; - CGDisplayModeRef mode = CGDisplayCopyDisplayMode(displayId); - double framesPerSecond = CGDisplayModeGetRefreshRate(mode); - CGDisplayModeRelease(mode); -#if MVK_XCODE_13 - if (framesPerSecond == 0 && [screen respondsToSelector: @selector(maximumFramesPerSecond)]) - framesPerSecond = [screen maximumFramesPerSecond]; -#endif - - // Builtin panels, e.g., on MacBook, report a zero refresh rate. - if (framesPerSecond == 0) - framesPerSecond = 60.0; +#elif MVK_VISIONOS + NSInteger framesPerSecond = 90; // TODO: See if this can be obtained from OS instead #endif pRefreshCycleDuration->refreshDuration = (uint64_t)1e9 / framesPerSecond; @@ -260,12 +262,6 @@ _presentHistoryHeadIndex = (_presentHistoryHeadIndex + 1) % kMaxPresentationHistory; } - // If actual present time is not available, use desired time instead, and if that - // hasn't been set, use the current time, which should be reasonably accurate (sub-ms), - // since we are here as part of the addPresentedHandler: callback. - if (actualPresentTime == 0) { actualPresentTime = presentInfo.desiredPresentTime; } - if (actualPresentTime == 0) { actualPresentTime = CACurrentMediaTime() * 1.0e9; } - _presentTimingHistory[_presentHistoryIndex].presentID = presentInfo.presentID; _presentTimingHistory[_presentHistoryIndex].desiredPresentTime = presentInfo.desiredPresentTime; _presentTimingHistory[_presentHistoryIndex].actualPresentTime = actualPresentTime; @@ -380,12 +376,13 @@ static inline CIE1931XY VkXYColorEXTToCIE1931XY(VkXYColorEXT xy) { MVKSwapchain::MVKSwapchain(MVKDevice* device, const VkSwapchainCreateInfoKHR* pCreateInfo) : MVKVulkanAPIDeviceObject(device), - _surface((MVKSurface*)pCreateInfo->surface) { + _surface((MVKSurface*)pCreateInfo->surface), + _imageExtent(pCreateInfo->imageExtent) { // Check if oldSwapchain is properly set auto* oldSwapchain = (MVKSwapchain*)pCreateInfo->oldSwapchain; if (oldSwapchain == _surface->_activeSwapchain) { - _surface->_activeSwapchain = this; + _surface->setActiveSwapchain(this); } else { setConfigurationResult(reportError(VK_ERROR_NATIVE_WINDOW_IN_USE_KHR, "vkCreateSwapchainKHR(): pCreateInfo->oldSwapchain does not match the VkSwapchain that is in use by the surface")); return; @@ -470,10 +467,11 @@ static CALayerContentsGravity getCALayerContentsGravity(VkSwapchainPresentScalin VkSwapchainPresentScalingCreateInfoEXT* pScalingInfo, uint32_t imgCnt) { - if ( getIsSurfaceLost() ) { return; } - auto* mtlLayer = getCAMetalLayer(); + if ( !mtlLayer || getIsSurfaceLost() ) { return; } + auto minMagFilter = mvkConfig().swapchainMinMagFilterUseNearest ? kCAFilterNearest : kCAFilterLinear; + mtlLayer.drawableSize = mvkCGSizeFromVkExtent2D(_imageExtent); mtlLayer.device = getMTLDevice(); mtlLayer.pixelFormat = getPixelFormats()->getMTLPixelFormat(pCreateInfo->imageFormat); mtlLayer.maximumDrawableCountMVK = imgCnt; @@ -491,15 +489,10 @@ static CALayerContentsGravity getCALayerContentsGravity(VkSwapchainPresentScalin // presentations on the oldSwapchain to complete and call back, but if the drawableSize // is not changing from the previous, we force those completions first. auto* oldSwapchain = (MVKSwapchain*)pCreateInfo->oldSwapchain; - if (oldSwapchain && mvkVkExtent2DsAreEqual(pCreateInfo->imageExtent, mvkVkExtent2DFromCGSize(mtlLayer.drawableSize))) { + if (oldSwapchain && mvkVkExtent2DsAreEqual(pCreateInfo->imageExtent, _surface->getExtent())) { oldSwapchain->forceUnpresentedImageCompletion(); } - // Remember the extent to later detect if it has changed under the covers, - // and set the drawable size of the CAMetalLayer from the extent. - _mtlLayerDrawableExtent = pCreateInfo->imageExtent; - mtlLayer.drawableSize = mvkCGSizeFromVkExtent2D(_mtlLayerDrawableExtent); - if (pCreateInfo->compositeAlpha != VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR) { mtlLayer.opaque = pCreateInfo->compositeAlpha == VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; } @@ -585,14 +578,13 @@ static CALayerContentsGravity getCALayerContentsGravity(VkSwapchainPresentScalin } } - auto* mtlLayer = getCAMetalLayer(); VkExtent2D imgExtent = pCreateInfo->imageExtent; VkImageCreateInfo imgInfo = { .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, .pNext = VK_NULL_HANDLE, .imageType = VK_IMAGE_TYPE_2D, - .format = getPixelFormats()->getVkFormat(mtlLayer.pixelFormat), - .extent = { imgExtent.width, imgExtent.height, 1 }, + .format = pCreateInfo->imageFormat, + .extent = mvkVkExtent3DFromVkExtent2D(imgExtent), .mipLevels = 1, .arrayLayers = 1, .samples = VK_SAMPLE_COUNT_1_BIT, @@ -618,14 +610,20 @@ static CALayerContentsGravity getCALayerContentsGravity(VkSwapchainPresentScalin _presentableImages.push_back(_device->createPresentableSwapchainImage(&imgInfo, this, imgIdx, nullptr)); } - NSString* screenName = @"Main Screen"; + auto* mtlLayer = getCAMetalLayer(); + if (mtlLayer) { + NSString* screenName = @"Main Screen"; #if MVK_MACOS && !MVK_MACCAT - if ([mtlLayer.screenMVK respondsToSelector:@selector(localizedName)]) { - screenName = mtlLayer.screenMVK.localizedName; - } + auto* screen = mtlLayer.screenMVK; + if ([screen respondsToSelector:@selector(localizedName)]) { + screenName = screen.localizedName; + } #endif - MVKLogInfo("Created %d swapchain images with size (%d, %d) and contents scale %.1f in layer %s (%p) on screen %s.", - imgCnt, imgExtent.width, imgExtent.height, mtlLayer.contentsScale, mtlLayer.name.UTF8String, mtlLayer, screenName.UTF8String); + MVKLogInfo("Created %d swapchain images with size (%d, %d) and contents scale %.1f in layer %s (%p) on screen %s.", + imgCnt, imgExtent.width, imgExtent.height, mtlLayer.contentsScale, mtlLayer.name.UTF8String, mtlLayer, screenName.UTF8String); + } else { + MVKLogInfo("Created %d swapchain images with size (%d, %d) on headless surface.", imgCnt, imgExtent.width, imgExtent.height); + } } void MVKSwapchain::destroy() { diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKSync.mm b/MoltenVK/MoltenVK/GPUObjects/MVKSync.mm index b7a4a64de..dfb536bb8 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKSync.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKSync.mm @@ -128,7 +128,7 @@ } void MVKSemaphoreMTLEvent::encodeDeferredSignal(id mtlCmdBuff, uint64_t deferToken) { - if (mtlCmdBuff) { [mtlCmdBuff encodeSignalEvent: _mtlEvent value: deferToken]; } + [mtlCmdBuff encodeSignalEvent: _mtlEvent value: deferToken]; } MVKSemaphoreMTLEvent::MVKSemaphoreMTLEvent(MVKDevice* device, diff --git a/MoltenVK/MoltenVK/Layers/MVKExtensions.def b/MoltenVK/MoltenVK/Layers/MVKExtensions.def index d8c222bdd..777d7252f 100644 --- a/MoltenVK/MoltenVK/Layers/MVKExtensions.def +++ b/MoltenVK/MoltenVK/Layers/MVKExtensions.def @@ -109,6 +109,7 @@ MVK_EXTENSION(EXT_extended_dynamic_state3, EXT_EXTENDED_DYNAMIC_STATE MVK_EXTENSION(EXT_external_memory_host, EXT_EXTERNAL_MEMORY_HOST, DEVICE, 10.11, 8.0, 1.0) MVK_EXTENSION(EXT_fragment_shader_interlock, EXT_FRAGMENT_SHADER_INTERLOCK, DEVICE, 10.13, 11.0, 1.0) MVK_EXTENSION(EXT_hdr_metadata, EXT_HDR_METADATA, DEVICE, 10.15, MVK_NA, MVK_NA) +MVK_EXTENSION(EXT_headless_surface, EXT_HEADLESS_SURFACE, INSTANCE, 10.11, 8.0, 1.0) MVK_EXTENSION(EXT_host_query_reset, EXT_HOST_QUERY_RESET, DEVICE, 10.11, 8.0, 1.0) MVK_EXTENSION(EXT_image_robustness, EXT_IMAGE_ROBUSTNESS, DEVICE, 10.11, 8.0, 1.0) MVK_EXTENSION(EXT_inline_uniform_block, EXT_INLINE_UNIFORM_BLOCK, DEVICE, 10.11, 8.0, 1.0) diff --git a/MoltenVK/MoltenVK/OS/CAMetalLayer+MoltenVK.h b/MoltenVK/MoltenVK/OS/CAMetalLayer+MoltenVK.h index 61a5c4396..c78128bae 100644 --- a/MoltenVK/MoltenVK/OS/CAMetalLayer+MoltenVK.h +++ b/MoltenVK/MoltenVK/OS/CAMetalLayer+MoltenVK.h @@ -23,12 +23,10 @@ #import #if MVK_IOS_OR_TVOS || MVK_MACCAT -# define PLATFORM_SCREEN_CLASS UIScreen # include #endif #if MVK_MACOS && !MVK_MACCAT -# define PLATFORM_SCREEN_CLASS NSScreen # include #endif @@ -76,9 +74,16 @@ */ @property(nonatomic, readwrite) CFStringRef colorspaceNameMVK; -#if !MVK_VISIONOS +#if MVK_IOS_OR_TVOS || MVK_MACCAT +/** Returns the screen on which this layer is rendering. */ +@property(nonatomic, readonly) UIScreen* screenMVK; +#endif + +#if MVK_MACOS && !MVK_MACCAT /** Returns the screen on which this layer is rendering. */ -@property(nonatomic, readonly) PLATFORM_SCREEN_CLASS* screenMVK; +@property(nonatomic, readonly) NSScreen* screenMVK; + +@property(nonatomic, readonly) NSScreen* privateScreenMVKImpl; #endif @end diff --git a/MoltenVK/MoltenVK/OS/CAMetalLayer+MoltenVK.m b/MoltenVK/MoltenVK/OS/CAMetalLayer+MoltenVK.mm similarity index 92% rename from MoltenVK/MoltenVK/OS/CAMetalLayer+MoltenVK.m rename to MoltenVK/MoltenVK/OS/CAMetalLayer+MoltenVK.mm index 9a8b10d8d..380a91503 100644 --- a/MoltenVK/MoltenVK/OS/CAMetalLayer+MoltenVK.m +++ b/MoltenVK/MoltenVK/OS/CAMetalLayer+MoltenVK.mm @@ -18,6 +18,7 @@ #include "CAMetalLayer+MoltenVK.h" +#include "MVKOSExtensions.h" #if MVK_MACOS && !MVK_MACCAT # include @@ -88,6 +89,13 @@ -(UIScreen*) screenMVK { #if MVK_MACOS && !MVK_MACCAT -(NSScreen*) screenMVK { + __block NSScreen* screen; + mvkDispatchToMainAndWait(^{ screen = self.privateScreenMVKImpl; }); + return screen; +} + +// Search for the screen currently displaying the layer, and default to the main screen if it can't be found. +-(NSScreen*) privateScreenMVKImpl { // If this layer has a delegate that is an NSView, and the view is in a window, retrieve the screen from the window. if ([self.delegate isKindOfClass: NSView.class]) { NSWindow* window = ((NSView*)self.delegate).window; diff --git a/MoltenVK/MoltenVK/Vulkan/vulkan.mm b/MoltenVK/MoltenVK/Vulkan/vulkan.mm index c08c5b3a5..293826a33 100644 --- a/MoltenVK/MoltenVK/Vulkan/vulkan.mm +++ b/MoltenVK/MoltenVK/Vulkan/vulkan.mm @@ -3869,6 +3869,26 @@ MVK_PUBLIC_VULKAN_SYMBOL void vkSetHdrMetadataEXT( } +#pragma mark - +#pragma mark VK_EXT_headless_surface extension + +MVK_PUBLIC_VULKAN_SYMBOL VkResult vkCreateHeadlessSurfaceEXT( + VkInstance instance, + const VkHeadlessSurfaceCreateInfoEXT* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSurfaceKHR* pSurface) { + + MVKTraceVulkanCallStart(); + MVKInstance* mvkInst = MVKInstance::getMVKInstance(instance); + MVKSurface* mvkSrfc = mvkInst->createSurface(pCreateInfo, pAllocator); + *pSurface = (VkSurfaceKHR)mvkSrfc; + VkResult rslt = mvkSrfc->getConfigurationResult(); + if (rslt < 0) { *pSurface = VK_NULL_HANDLE; mvkInst->destroySurface(mvkSrfc, pAllocator); } + MVKTraceVulkanCallEnd(); + return rslt; +} + + #pragma mark - #pragma mark VK_EXT_host_query_reset extension diff --git a/README.md b/README.md index 5c205b08b..2fa365260 100644 --- a/README.md +++ b/README.md @@ -149,21 +149,14 @@ for which to build the external libraries. The platform choices include: --maccat --tvos --tvossim - --visionos - --visionossim - -The `visionos` and `visionossim` selections require Xcode 15+. You can specify multiple of these selections. The result is a single `XCFramework` for each external dependency library, with each `XCFramework` containing binaries for each of the requested platforms. -The `--all` selection is the same as entering all of the other platform choices, except -`--visionos` and `--visionossim`, and will result in a single `XCFramework` for each -external dependency library, with each `XCFramework` containing binaries for all supported -platforms and simulators. The `--visionos` and `--visionossim` selections must be invoked -with a separate invocation of `fetchDependencies`, because those selections require -Xcode 15+, and will cause a multi-platform build on older versions of Xcode to abort. +The `--all` selection is the same as entering all of the other platform choices, +and will result in a single `XCFramework` for each external dependency library, +with each `XCFramework` containing binaries for all supported platforms and simulators. Running `fetchDependencies` repeatedly with different platforms will accumulate targets in the `XCFramework`, if the `--keep-cache` option is used on each invocation. @@ -263,8 +256,6 @@ from the command line. The following `make` targets are provided: make maccat make tvos make tvossim - make visionos - make visionossim make all-debug make macos-debug @@ -273,15 +264,12 @@ from the command line. The following `make` targets are provided: make maccat-debug make tvos-debug make tvossim-debug - make visionos-debug - make visionossim-debug make clean make install - Running `make` repeatedly with different targets will accumulate binaries for these different targets. -- The `all` target executes all platform targets, except `visionos` and `visionossim`, as these require - Xcode 15+, and will abort a multi-platform build on older versions of Xcode. +- The `all` target executes all platform targets. - The `all` target is the default target. Running `make` with no arguments is the same as running `make all`. - The `*-debug` targets build the binaries using the **_Debug_** configuration. - The `install` target will copy the most recently built `MoltenVK.xcframework` into the