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).
diff --git a/Common/MVKOSExtensions.h b/Common/MVKOSExtensions.h
index f9faba9..2c40602 100644
--- a/Common/MVKOSExtensions.h
+++ b/Common/MVKOSExtensions.h
@@ -24,6 +24,9 @@
#include <limits>
+#pragma mark -
+#pragma mark Operating System versions
+
typedef float MVKOSVersion;
/*** Constant indicating unsupported functionality in an OS. */
@@ -66,20 +69,31 @@
#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 @@
*/
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 @@
/** 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 93025f2..8d33f3d 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 @@
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(); }
-double mvkGetTimestampPeriod() { return _mvkTimestampPeriod; }
+uint64_t mvkGetRuntimeNanoseconds() { return mach_absolute_time() * _mvkMachTimebase.numer / _mvkMachTimebase.denom; }
+
+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 mvkGetHostMemoryPageSize() { return sysconf(_SC_PAGESIZE); }
+
#pragma mark -
#pragma mark Threading
@@ -152,3 +149,11 @@
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 9b00360..ee93c80 100644
--- a/Docs/MoltenVK_Runtime_UserGuide.md
+++ b/Docs/MoltenVK_Runtime_UserGuide.md
@@ -380,6 +380,9 @@
- `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 e0abbf7..ebafdde 100644
--- a/Docs/Whats_New.md
+++ b/Docs/Whats_New.md
@@ -20,11 +20,13 @@
- 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 46ff50f..be7ca32 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 = "<group>"; };
A9E53DD42100B197002781DD /* NSString+MoltenVK.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSString+MoltenVK.mm"; sourceTree = "<group>"; };
A9E53DD52100B197002781DD /* MTLTextureDescriptor+MoltenVK.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MTLTextureDescriptor+MoltenVK.m"; sourceTree = "<group>"; };
- A9E53DD62100B197002781DD /* CAMetalLayer+MoltenVK.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "CAMetalLayer+MoltenVK.m"; sourceTree = "<group>"; };
+ A9E53DD62100B197002781DD /* CAMetalLayer+MoltenVK.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "CAMetalLayer+MoltenVK.mm"; sourceTree = "<group>"; };
A9E53DEE2100B302002781DD /* MTLRenderPassDescriptor+MoltenVK.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MTLRenderPassDescriptor+MoltenVK.h"; sourceTree = "<group>"; };
A9E53DF22100B302002781DD /* MTLRenderPassDescriptor+MoltenVK.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MTLRenderPassDescriptor+MoltenVK.m"; sourceTree = "<group>"; };
A9E53DFA21064F84002781DD /* MTLRenderPipelineDescriptor+MoltenVK.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MTLRenderPipelineDescriptor+MoltenVK.m"; sourceTree = "<group>"; };
@@ -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 cbbbfab..401c880 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 @@
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 900b10f..058876d 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.h
@@ -378,14 +378,8 @@
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<MTLTexture> getMTLTexture(uint8_t planeIndex) override;
-
#pragma mark Construction
@@ -399,7 +393,6 @@
protected:
friend class MVKPeerSwapchainImage;
- virtual id<CAMetalDrawable> getCAMetalDrawable() = 0;
void detachSwapchain();
std::mutex _detachmentLock;
@@ -445,6 +438,8 @@
#pragma mark Metal
+ id<MTLTexture> getMTLTexture(uint8_t planeIndex) override;
+
/** Presents the contained drawable to the OS. */
VkResult presentCAMetalDrawable(id<MTLCommandBuffer> mtlCmdBuff, MVKImagePresentInfo presentInfo);
@@ -468,16 +463,16 @@
protected:
friend MVKSwapchain;
- id<CAMetalDrawable> getCAMetalDrawable() override;
+ id<CAMetalDrawable> getCAMetalDrawable();
void addPresentedHandler(id<CAMetalDrawable> 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<CAMetalDrawable> _mtlDrawable = nil;
+ id<MTLTexture> _mtlTextureHeadless = nil;
MVKSwapchainImageAvailability _availability;
MVKSmallVector<MVKSwapchainSignaler, 1> _availabilitySignalers;
MVKSwapchainSignaler _preSignaler = {};
@@ -494,7 +489,8 @@
public:
- /** Binds this resource according to the specified bind information. */
+ id<MTLTexture> getMTLTexture(uint8_t planeIndex) override;
+
VkResult bindDeviceMemory2(const VkBindImageMemoryInfo* pBindInfo) override;
@@ -504,10 +500,6 @@
const VkImageCreateInfo* pCreateInfo,
MVKSwapchain* swapchain,
uint32_t swapchainIndex);
-
-protected:
- id<CAMetalDrawable> getCAMetalDrawable() override;
-
};
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
index 323918f..1c132a4 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<MTLTexture> 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<MTLCommandBuffer> mtlCmdBuff) {
- if (signaler.semaphore) { signaler.semaphore->encodeDeferredSignal(mtlCmdBuff, signaler.semaphoreSignalToken); }
-}
-
-// Signal either or both of the semaphore and fence in the specified tracker pair.
-static void signal(const MVKSwapchainSignaler& signaler, id<MTLCommandBuffer> mtlCmdBuff) {
- if (signaler.semaphore) { signaler.semaphore->encodeDeferredSignal(mtlCmdBuff, signaler.semaphoreSignalToken); }
- if (signaler.fence) { signaler.fence->signal(); }
-}
-
// Tell the semaphore and fence that they are being tracked for future signaling.
-static void markAsTracked(const MVKSwapchainSignaler& signaler) {
+static void track(const MVKSwapchainSignaler& signaler) {
if (signaler.semaphore) { signaler.semaphore->retain(); }
if (signaler.fence) { signaler.fence->retain(); }
}
-// Tell the semaphore and fence that they are no longer being tracked for future signaling.
-static void unmarkAsTracked(const MVKSwapchainSignaler& signaler) {
- if (signaler.semaphore) { signaler.semaphore->release(); }
- if (signaler.fence) { signaler.fence->release(); }
+static void signal(MVKSemaphore* semaphore, uint64_t semaphoreSignalToken, id<MTLCommandBuffer> mtlCmdBuff) {
+ if (semaphore) { semaphore->encodeDeferredSignal(mtlCmdBuff, semaphoreSignalToken); }
}
-static void signalAndUnmarkAsTracked(const MVKSwapchainSignaler& signaler) {
- signal(signaler, nil);
- unmarkAsTracked(signaler);
+static void signal(MVKFence* fence) {
+ if (fence) { fence->signal(); }
+}
+
+// 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(); }
+
+ 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<mutex> lock(_availabilityLock);
@@ -1267,7 +1257,8 @@
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 @@
} else {
_availabilitySignalers.push_back(signaler);
}
- markAsTracked(signaler);
+ track(signaler);
return getConfigurationResult();
}
@@ -1284,6 +1275,9 @@
// 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<CAMetalDrawable> MVKPresentableSwapchainImage::getCAMetalDrawable() {
+
+ if (_mtlTextureHeadless) { return nil; } // If headless, there is no drawable.
+
if ( !_mtlDrawable ) {
@autoreleasepool {
bool hasInvalidFormat = false;
@@ -1305,6 +1299,11 @@
return _mtlDrawable;
}
+// If not headless, retrieve the MTLTexture directly from the CAMetalDrawable.
+id<MTLTexture> 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<MTLCommandBuffer> mtlCmdBuff,
@@ -1343,15 +1342,13 @@
auto* fence = presentInfo.fence;
if (fence) { fence->retain(); }
[mtlCmdBuff addCompletedHandler: ^(id<MTLCommandBuffer> 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 @@
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 @@
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 @@
_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<mutex> 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 @@
lock_guard<mutex> 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 @@
_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 @@
#pragma mark Metal
-id<CAMetalDrawable> MVKPeerSwapchainImage::getCAMetalDrawable() {
- return ((MVKSwapchainImage*)_swapchain->getPresentableImage(_swapchainIndex))->getCAMetalDrawable();
+id<MTLTexture> 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 f6ca8e7..6a2fa92 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKInstance.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKInstance.h
@@ -117,6 +117,9 @@
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 543a1fe..de9ad02 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 479965b..5d23225 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 <spirv_msl.hpp>
#include <unordered_map>
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKSurface.h b/MoltenVK/MoltenVK/GPUObjects/MVKSurface.h
index 5746bfb..453eac6 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKSurface.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKSurface.h
@@ -24,16 +24,6 @@
#import <Metal/Metal.h>
#import <QuartzCore/CAMetalLayer.h>
-#ifdef VK_USE_PLATFORM_IOS_MVK
-# define PLATFORM_VIEW_CLASS UIView
-# import <UIKit/UIView.h>
-#endif
-
-#ifdef VK_USE_PLATFORM_MACOS_MVK
-# define PLATFORM_VIEW_CLASS NSView
-# import <AppKit/NSView.h>
-#endif
-
class MVKInstance;
class MVKSwapchain;
@@ -59,6 +49,14 @@
/** 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
@@ -67,6 +65,10 @@
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 @@
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 @@
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 3899ab6..0485571 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 <UIKit/UIView.h>
+#endif
+
+#ifdef VK_USE_PLATFORM_MACOS_MVK
+# define PLATFORM_VIEW_CLASS NSView
+# import <AppKit/NSView.h>
+#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<NSObject> obj = (id<NSObject>)pCreateInfo->pView;
-
// If it's a view (NSView/UIView), extract the layer, otherwise assume it's already a CAMetalLayer.
+ id<NSObject> obj = (id<NSObject>)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<NSObject> 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 cd418bd..d8eb535 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.h
@@ -23,7 +23,6 @@
#include "MVKSmallVector.h"
#include <mutex>
-#import "CAMetalLayer+MoltenVK.h"
#import <Metal/Metal.h>
class MVKWatermark;
@@ -46,9 +45,15 @@
/** 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 @@
std::atomic<uint64_t> _currentAcquisitionID = 0;
std::mutex _presentHistoryLock;
uint64_t _lastFrameTime = 0;
- VkExtent2D _mtlLayerDrawableExtent = {0, 0};
+ VkExtent2D _imageExtent = {0, 0};
std::atomic<uint32_t> _unpresentedImageCount = 0;
uint32_t _currentPerfLogFrameCount = 0;
uint32_t _presentHistoryCount = 0;
@@ -134,18 +139,3 @@
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 63c3ac7..5beeee0 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 <libkern/OSByteOrder.h>
+
+#import "CAMetalLayer+MoltenVK.h"
#import "MVKBlockObserver.h"
-#include <libkern/OSByteOrder.h>
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 @@
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 @@
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 @@
// 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 @@
}
}
- 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 @@
_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 b7a4a64..dfb536b 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKSync.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKSync.mm
@@ -128,7 +128,7 @@
}
void MVKSemaphoreMTLEvent::encodeDeferredSignal(id<MTLCommandBuffer> 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 d8c222b..777d725 100644
--- a/MoltenVK/MoltenVK/Layers/MVKExtensions.def
+++ b/MoltenVK/MoltenVK/Layers/MVKExtensions.def
@@ -109,6 +109,7 @@
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 61a5c43..c78128b 100644
--- a/MoltenVK/MoltenVK/OS/CAMetalLayer+MoltenVK.h
+++ b/MoltenVK/MoltenVK/OS/CAMetalLayer+MoltenVK.h
@@ -23,12 +23,10 @@
#import <QuartzCore/QuartzCore.h>
#if MVK_IOS_OR_TVOS || MVK_MACCAT
-# define PLATFORM_SCREEN_CLASS UIScreen
# include <UIKit/UIScreen.h>
#endif
#if MVK_MACOS && !MVK_MACCAT
-# define PLATFORM_SCREEN_CLASS NSScreen
# include <AppKit/NSScreen.h>
#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) PLATFORM_SCREEN_CLASS* screenMVK;
+@property(nonatomic, readonly) UIScreen* screenMVK;
+#endif
+
+#if MVK_MACOS && !MVK_MACCAT
+/** Returns the screen on which this layer is rendering. */
+@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 91%
rename from MoltenVK/MoltenVK/OS/CAMetalLayer+MoltenVK.m
rename to MoltenVK/MoltenVK/OS/CAMetalLayer+MoltenVK.mm
index 9a8b10d..380a915 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 <AppKit/NSApplication.h>
@@ -88,6 +89,13 @@
#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 c08c5b3..293826a 100644
--- a/MoltenVK/MoltenVK/Vulkan/vulkan.mm
+++ b/MoltenVK/MoltenVK/Vulkan/vulkan.mm
@@ -3870,6 +3870,26 @@
#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
MVK_PUBLIC_VULKAN_CORE_ALIAS(vkResetQueryPool, EXT);
diff --git a/README.md b/README.md
index 5c205b0..2fa3652 100644
--- a/README.md
+++ b/README.md
@@ -149,21 +149,14 @@
--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 @@
make maccat
make tvos
make tvossim
- make visionos
- make visionossim
make all-debug
make macos-debug
@@ -273,15 +264,12 @@
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