Merge pull request #2086 from billhollings/VK_EXT_headless_surface

Add support for extension VK_EXT_headless_surface.
This commit is contained in:
Bill Hollings 2023-12-05 09:11:50 -05:00 committed by GitHub
commit e6a3886313
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 313 additions and 237 deletions

View File

@ -24,6 +24,9 @@
#include <limits> #include <limits>
#pragma mark -
#pragma mark Operating System versions
typedef float MVKOSVersion; typedef float MVKOSVersion;
/*** Constant indicating unsupported functionality in an OS. */ /*** Constant indicating unsupported functionality in an OS. */
@ -66,20 +69,31 @@ static inline bool mvkOSVersionIsAtLeast(MVKOSVersion macOSMinVer,
#endif #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. * 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.
* 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.
*/ */
uint64_t mvkGetTimestamp(); 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, * 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); 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 -
#pragma mark Process environment #pragma mark Process environment
@ -141,8 +149,12 @@ uint64_t mvkGetUsedMemorySize();
/** Returns the size of a page of host memory on this platform. */ /** Returns the size of a page of host memory on this platform. */
uint64_t mvkGetHostMemoryPageSize(); uint64_t mvkGetHostMemoryPageSize();
#pragma mark - #pragma mark -
#pragma mark Threading #pragma mark Threading
/** Returns the amount of avaliable CPU cores. */ /** Returns the amount of avaliable CPU cores. */
uint32_t mvkGetAvaliableCPUCores(); uint32_t mvkGetAvaliableCPUCores();
/** Ensures the block is executed on the main thread. */
void mvkDispatchToMainAndWait(dispatch_block_t block);

View File

@ -29,6 +29,10 @@
using namespace std; using namespace std;
#pragma mark -
#pragma mark Operating System versions
MVKOSVersion mvkOSVersion() { MVKOSVersion mvkOSVersion() {
static MVKOSVersion _mvkOSVersion = 0; static MVKOSVersion _mvkOSVersion = 0;
if ( !_mvkOSVersion ) { if ( !_mvkOSVersion ) {
@ -38,43 +42,35 @@ MVKOSVersion mvkOSVersion() {
return _mvkOSVersion; return _mvkOSVersion;
} }
static uint64_t _mvkTimestampBase;
static double _mvkTimestampPeriod; #pragma mark -
#pragma mark Timestamps
static mach_timebase_info_data_t _mvkMachTimebase; 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) { uint64_t mvkGetElapsedNanoseconds(uint64_t startTimestamp, uint64_t endTimestamp) {
if (endTimestamp == 0) { endTimestamp = mvkGetTimestamp(); } if (endTimestamp == 0) { endTimestamp = mvkGetTimestamp(); }
return (endTimestamp - startTimestamp) * _mvkTimestampPeriod; return (endTimestamp - startTimestamp) * _mvkMachTimebase.numer / _mvkMachTimebase.denom;
} }
double mvkGetElapsedMilliseconds(uint64_t startTimestamp, uint64_t endTimestamp) { double mvkGetElapsedMilliseconds(uint64_t startTimestamp, uint64_t endTimestamp) {
return mvkGetElapsedNanoseconds(startTimestamp, endTimestamp) / 1e6; return mvkGetElapsedNanoseconds(startTimestamp, endTimestamp) / 1e6;
} }
uint64_t mvkGetAbsoluteTime() { return mach_continuous_time() * _mvkMachTimebase.numer / _mvkMachTimebase.denom; } // Initialize timestamp capabilities on app startup.
// Called automatically when the framework is loaded and initialized.
// Initialize timestamping capabilities on app startup.
//Called automatically when the framework is loaded and initialized.
static bool _mvkTimestampsInitialized = false; static bool _mvkTimestampsInitialized = false;
__attribute__((constructor)) static void MVKInitTimestamps() { __attribute__((constructor)) static void MVKInitTimestamps() {
if (_mvkTimestampsInitialized ) { return; } if (_mvkTimestampsInitialized ) { return; }
_mvkTimestampsInitialized = true; _mvkTimestampsInitialized = true;
_mvkTimestampBase = mach_absolute_time();
mach_timebase_info(&_mvkMachTimebase); 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); } uint64_t mvkGetHostMemoryPageSize() { return sysconf(_SC_PAGESIZE); }
#pragma mark - #pragma mark -
#pragma mark Threading #pragma mark Threading
@ -152,3 +149,11 @@ uint64_t mvkGetHostMemoryPageSize() { return sysconf(_SC_PAGESIZE); }
uint32_t mvkGetAvaliableCPUCores() { uint32_t mvkGetAvaliableCPUCores() {
return (uint32_t)[[NSProcessInfo processInfo] activeProcessorCount]; return (uint32_t)[[NSProcessInfo processInfo] activeProcessorCount];
} }
void mvkDispatchToMainAndWait(dispatch_block_t block) {
if (NSThread.isMainThread) {
block();
} else {
dispatch_sync(dispatch_get_main_queue(), block);
}
}

View File

@ -380,6 +380,9 @@ In addition to core *Vulkan* functionality, **MoltenVK** also supports the foll
- `VK_EXT_external_memory_host` - `VK_EXT_external_memory_host`
- `VK_EXT_fragment_shader_interlock` - `VK_EXT_fragment_shader_interlock`
- *Requires Metal 2.0 and Raster Order Groups.* - *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_host_query_reset`
- `VK_EXT_image_robustness` - `VK_EXT_image_robustness`
- `VK_EXT_inline_uniform_block` - `VK_EXT_inline_uniform_block`

View File

@ -20,11 +20,13 @@ Released TBD
- Add support for extensions: - Add support for extensions:
- `VK_EXT_extended_dynamic_state3` *(Metal does not support `VK_POLYGON_MODE_POINT`)* - `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 that broke `VK_POLYGON_MODE_LINE`.
- Fix regression in marking rendering state dirty after `vkCmdClearAttachments()`. - Fix regression in marking rendering state dirty after `vkCmdClearAttachments()`.
- Reduce disk space consumed after running `fetchDependencies` script by removing intermediate file caches. - Reduce disk space consumed after running `fetchDependencies` script by removing intermediate file caches.
- Fix rare deadlock during launch via `dlopen()`. - Fix rare deadlock during launch via `dlopen()`.
- Fix initial value of `VkPhysicalDeviceLimits::timestampPeriod` on non-Apple Silicon GPUs. - 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: - Update to latest SPIRV-Cross:
- MSL: Fix regression error in argument buffer runtime arrays. - MSL: Fix regression error in argument buffer runtime arrays.
- MSL: Work around broken cube texture gradients on Apple Silicon. - MSL: Work around broken cube texture gradients on Apple Silicon.

View File

@ -117,7 +117,7 @@
2FEA0AAF24902F9F00EEF3AD /* MVKLayers.mm in Sources */ = {isa = PBXBuildFile; fileRef = A94FB7A11C7DFB4800632CA3 /* MVKLayers.mm */; }; 2FEA0AAF24902F9F00EEF3AD /* MVKLayers.mm in Sources */ = {isa = PBXBuildFile; fileRef = A94FB7A11C7DFB4800632CA3 /* MVKLayers.mm */; };
2FEA0AB024902F9F00EEF3AD /* MVKFramebuffer.mm in Sources */ = {isa = PBXBuildFile; fileRef = A94FB7881C7DFB4800632CA3 /* MVKFramebuffer.mm */; }; 2FEA0AB024902F9F00EEF3AD /* MVKFramebuffer.mm in Sources */ = {isa = PBXBuildFile; fileRef = A94FB7881C7DFB4800632CA3 /* MVKFramebuffer.mm */; };
2FEA0AB124902F9F00EEF3AD /* MVKMTLBufferAllocation.mm in Sources */ = {isa = PBXBuildFile; fileRef = A9C96DCF1DDC20C20053187F /* MVKMTLBufferAllocation.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 */; }; 2FEA0AB324902F9F00EEF3AD /* MVKCmdDispatch.mm in Sources */ = {isa = PBXBuildFile; fileRef = A9096E5D1F81E16300DFBEA6 /* MVKCmdDispatch.mm */; };
2FEA0AB424902F9F00EEF3AD /* MVKCmdDebug.mm in Sources */ = {isa = PBXBuildFile; fileRef = A99C90ED229455B300A061DA /* MVKCmdDebug.mm */; }; 2FEA0AB424902F9F00EEF3AD /* MVKCmdDebug.mm in Sources */ = {isa = PBXBuildFile; fileRef = A99C90ED229455B300A061DA /* MVKCmdDebug.mm */; };
45003E73214AD4E500E989CB /* MVKExtensions.def in Headers */ = {isa = PBXBuildFile; fileRef = 45003E6F214AD4C900E989CB /* MVKExtensions.def */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; A9E53DE92100B197002781DD /* CAMetalLayer+MoltenVK.mm in Sources */ = {isa = PBXBuildFile; fileRef = A9E53DD62100B197002781DD /* CAMetalLayer+MoltenVK.mm */; };
A9E53DEA2100B197002781DD /* CAMetalLayer+MoltenVK.m in Sources */ = {isa = PBXBuildFile; fileRef = A9E53DD62100B197002781DD /* CAMetalLayer+MoltenVK.m */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; DCFD7F572A45BC6E007BBBF7 /* MVKFramebuffer.mm in Sources */ = {isa = PBXBuildFile; fileRef = A94FB7881C7DFB4800632CA3 /* MVKFramebuffer.mm */; };
DCFD7F582A45BC6E007BBBF7 /* MTLRenderPassStencilAttachmentDescriptor+MoltenVK.m in Sources */ = {isa = PBXBuildFile; fileRef = 453638302508A4C6000EFFD3 /* MTLRenderPassStencilAttachmentDescriptor+MoltenVK.m */; }; DCFD7F582A45BC6E007BBBF7 /* MTLRenderPassStencilAttachmentDescriptor+MoltenVK.m in Sources */ = {isa = PBXBuildFile; fileRef = 453638302508A4C6000EFFD3 /* MTLRenderPassStencilAttachmentDescriptor+MoltenVK.m */; };
DCFD7F592A45BC6E007BBBF7 /* MVKMTLBufferAllocation.mm in Sources */ = {isa = PBXBuildFile; fileRef = A9C96DCF1DDC20C20053187F /* MVKMTLBufferAllocation.mm */; }; 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 */; }; DCFD7F5B2A45BC6E007BBBF7 /* MVKCmdDispatch.mm in Sources */ = {isa = PBXBuildFile; fileRef = A9096E5D1F81E16300DFBEA6 /* MVKCmdDispatch.mm */; };
DCFD7F5C2A45BC6E007BBBF7 /* MVKCmdDebug.mm in Sources */ = {isa = PBXBuildFile; fileRef = A99C90ED229455B300A061DA /* MVKCmdDebug.mm */; }; DCFD7F5C2A45BC6E007BBBF7 /* MVKCmdDebug.mm in Sources */ = {isa = PBXBuildFile; fileRef = A99C90ED229455B300A061DA /* MVKCmdDebug.mm */; };
/* End PBXBuildFile section */ /* 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; A9E53DFA21064F84002781DD /* MTLRenderPipelineDescriptor+MoltenVK.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MTLRenderPipelineDescriptor+MoltenVK.m"; sourceTree = "<group>"; };
@ -889,7 +889,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
A9E53DD12100B197002781DD /* CAMetalLayer+MoltenVK.h */, A9E53DD12100B197002781DD /* CAMetalLayer+MoltenVK.h */,
A9E53DD62100B197002781DD /* CAMetalLayer+MoltenVK.m */, A9E53DD62100B197002781DD /* CAMetalLayer+MoltenVK.mm */,
453638312508A4C7000EFFD3 /* MTLRenderPassDepthAttachmentDescriptor+MoltenVK.h */, 453638312508A4C7000EFFD3 /* MTLRenderPassDepthAttachmentDescriptor+MoltenVK.h */,
4536382F2508A4C6000EFFD3 /* MTLRenderPassDepthAttachmentDescriptor+MoltenVK.m */, 4536382F2508A4C6000EFFD3 /* MTLRenderPassDepthAttachmentDescriptor+MoltenVK.m */,
A9E53DEE2100B302002781DD /* MTLRenderPassDescriptor+MoltenVK.h */, A9E53DEE2100B302002781DD /* MTLRenderPassDescriptor+MoltenVK.h */,
@ -1703,7 +1703,7 @@
2FEA0AAF24902F9F00EEF3AD /* MVKLayers.mm in Sources */, 2FEA0AAF24902F9F00EEF3AD /* MVKLayers.mm in Sources */,
2FEA0AB024902F9F00EEF3AD /* MVKFramebuffer.mm in Sources */, 2FEA0AB024902F9F00EEF3AD /* MVKFramebuffer.mm in Sources */,
2FEA0AB124902F9F00EEF3AD /* MVKMTLBufferAllocation.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 */, 2FEA0AB324902F9F00EEF3AD /* MVKCmdDispatch.mm in Sources */,
2FEA0AB424902F9F00EEF3AD /* MVKCmdDebug.mm in Sources */, 2FEA0AB424902F9F00EEF3AD /* MVKCmdDebug.mm in Sources */,
); );
@ -1763,7 +1763,7 @@
A94FB7EE1C7DFB4800632CA3 /* MVKFramebuffer.mm in Sources */, A94FB7EE1C7DFB4800632CA3 /* MVKFramebuffer.mm in Sources */,
453638382508A4C7000EFFD3 /* MTLRenderPassStencilAttachmentDescriptor+MoltenVK.m in Sources */, 453638382508A4C7000EFFD3 /* MTLRenderPassStencilAttachmentDescriptor+MoltenVK.m in Sources */,
A9C96DD21DDC20C20053187F /* MVKMTLBufferAllocation.mm in Sources */, A9C96DD21DDC20C20053187F /* MVKMTLBufferAllocation.mm in Sources */,
A9E53DE92100B197002781DD /* CAMetalLayer+MoltenVK.m in Sources */, A9E53DE92100B197002781DD /* CAMetalLayer+MoltenVK.mm in Sources */,
A9096E5E1F81E16300DFBEA6 /* MVKCmdDispatch.mm in Sources */, A9096E5E1F81E16300DFBEA6 /* MVKCmdDispatch.mm in Sources */,
A99C90F0229455B300A061DA /* MVKCmdDebug.mm in Sources */, A99C90F0229455B300A061DA /* MVKCmdDebug.mm in Sources */,
); );
@ -1823,7 +1823,7 @@
A94FB7EF1C7DFB4800632CA3 /* MVKFramebuffer.mm in Sources */, A94FB7EF1C7DFB4800632CA3 /* MVKFramebuffer.mm in Sources */,
4536383A2508A4C7000EFFD3 /* MTLRenderPassStencilAttachmentDescriptor+MoltenVK.m in Sources */, 4536383A2508A4C7000EFFD3 /* MTLRenderPassStencilAttachmentDescriptor+MoltenVK.m in Sources */,
A9C96DD31DDC20C20053187F /* MVKMTLBufferAllocation.mm in Sources */, A9C96DD31DDC20C20053187F /* MVKMTLBufferAllocation.mm in Sources */,
A9E53DEA2100B197002781DD /* CAMetalLayer+MoltenVK.m in Sources */, A9E53DEA2100B197002781DD /* CAMetalLayer+MoltenVK.mm in Sources */,
A9096E5F1F81E16300DFBEA6 /* MVKCmdDispatch.mm in Sources */, A9096E5F1F81E16300DFBEA6 /* MVKCmdDispatch.mm in Sources */,
A99C90F1229455B300A061DA /* MVKCmdDebug.mm in Sources */, A99C90F1229455B300A061DA /* MVKCmdDebug.mm in Sources */,
); );
@ -1883,7 +1883,7 @@
DCFD7F572A45BC6E007BBBF7 /* MVKFramebuffer.mm in Sources */, DCFD7F572A45BC6E007BBBF7 /* MVKFramebuffer.mm in Sources */,
DCFD7F582A45BC6E007BBBF7 /* MTLRenderPassStencilAttachmentDescriptor+MoltenVK.m in Sources */, DCFD7F582A45BC6E007BBBF7 /* MTLRenderPassStencilAttachmentDescriptor+MoltenVK.m in Sources */,
DCFD7F592A45BC6E007BBBF7 /* MVKMTLBufferAllocation.mm in Sources */, DCFD7F592A45BC6E007BBBF7 /* MVKMTLBufferAllocation.mm in Sources */,
DCFD7F5A2A45BC6E007BBBF7 /* CAMetalLayer+MoltenVK.m in Sources */, DCFD7F5A2A45BC6E007BBBF7 /* CAMetalLayer+MoltenVK.mm in Sources */,
DCFD7F5B2A45BC6E007BBBF7 /* MVKCmdDispatch.mm in Sources */, DCFD7F5B2A45BC6E007BBBF7 /* MVKCmdDispatch.mm in Sources */,
DCFD7F5C2A45BC6E007BBBF7 /* MVKCmdDebug.mm in Sources */, DCFD7F5C2A45BC6E007BBBF7 /* MVKCmdDebug.mm in Sources */,
); );

View File

@ -1202,8 +1202,8 @@ VkResult MVKPhysicalDevice::getSurfaceSupport(uint32_t queueFamilyIndex,
isHeadless = getMTLDevice().isHeadless; isHeadless = getMTLDevice().isHeadless;
#endif #endif
// If this device is headless or the surface does not have a CAMetalLayer, it is not supported. // If this device is headless, the surface must be headless.
*pSupported = !(isHeadless || (surface->getCAMetalLayer() == nil)); *pSupported = isHeadless ? surface->isHeadless() : wasConfigurationSuccessful();
return *pSupported ? VK_SUCCESS : surface->getConfigurationResult(); return *pSupported ? VK_SUCCESS : surface->getConfigurationResult();
} }
@ -1262,13 +1262,12 @@ VkResult MVKPhysicalDevice::getSurfaceCapabilities( const VkPhysicalDeviceSurfac
// The CAlayer underlying the surface must be a CAMetalLayer. // The CAlayer underlying the surface must be a CAMetalLayer.
MVKSurface* surface = (MVKSurface*)pSurfaceInfo->surface; MVKSurface* surface = (MVKSurface*)pSurfaceInfo->surface;
CAMetalLayer* mtlLayer = surface->getCAMetalLayer(); if ( !surface->wasConfigurationSuccessful() ) { return surface->getConfigurationResult(); }
if ( !mtlLayer ) { return surface->getConfigurationResult(); }
VkSurfaceCapabilitiesKHR& surfCaps = pSurfaceCapabilities->surfaceCapabilities; VkSurfaceCapabilitiesKHR& surfCaps = pSurfaceCapabilities->surfaceCapabilities;
surfCaps.minImageCount = _metalFeatures.minSwapchainImageCount; surfCaps.minImageCount = _metalFeatures.minSwapchainImageCount;
surfCaps.maxImageCount = _metalFeatures.maxSwapchainImageCount; surfCaps.maxImageCount = _metalFeatures.maxSwapchainImageCount;
surfCaps.currentExtent = mvkGetNaturalExtent(mtlLayer); surfCaps.currentExtent = surface->getNaturalExtent();
surfCaps.minImageExtent = { 1, 1 }; surfCaps.minImageExtent = { 1, 1 };
surfCaps.maxImageExtent = { _properties.limits.maxImageDimension2D, _properties.limits.maxImageDimension2D }; surfCaps.maxImageExtent = { _properties.limits.maxImageDimension2D, _properties.limits.maxImageDimension2D };
surfCaps.maxImageArrayLayers = 1; surfCaps.maxImageArrayLayers = 1;
@ -1347,9 +1346,7 @@ VkResult MVKPhysicalDevice::getSurfaceFormats(MVKSurface* surface,
uint32_t* pCount, uint32_t* pCount,
VkSurfaceFormatKHR* pSurfaceFormats) { VkSurfaceFormatKHR* pSurfaceFormats) {
// The layer underlying the surface view must be a CAMetalLayer. if ( !surface->wasConfigurationSuccessful() ) { return surface->getConfigurationResult(); }
CAMetalLayer* mtlLayer = surface->getCAMetalLayer();
if ( !mtlLayer ) { return surface->getConfigurationResult(); }
#define addSurfFmt(MTL_FMT) \ #define addSurfFmt(MTL_FMT) \
do { \ do { \
@ -1472,9 +1469,7 @@ VkResult MVKPhysicalDevice::getSurfacePresentModes(MVKSurface* surface,
uint32_t* pCount, uint32_t* pCount,
VkPresentModeKHR* pPresentModes) { VkPresentModeKHR* pPresentModes) {
// The layer underlying the surface view must be a CAMetalLayer. if ( !surface->wasConfigurationSuccessful() ) { return surface->getConfigurationResult(); }
CAMetalLayer* mtlLayer = surface->getCAMetalLayer();
if ( !mtlLayer ) { return surface->getConfigurationResult(); }
#define ADD_VK_PRESENT_MODE(VK_PM) \ #define ADD_VK_PRESENT_MODE(VK_PM) \
do { \ do { \
@ -1502,9 +1497,7 @@ VkResult MVKPhysicalDevice::getPresentRectangles(MVKSurface* surface,
uint32_t* pRectCount, uint32_t* pRectCount,
VkRect2D* pRects) { VkRect2D* pRects) {
// The layer underlying the surface view must be a CAMetalLayer. if ( !surface->wasConfigurationSuccessful() ) { return surface->getConfigurationResult(); }
CAMetalLayer* mtlLayer = surface->getCAMetalLayer();
if ( !mtlLayer ) { return surface->getConfigurationResult(); }
if ( !pRects ) { if ( !pRects ) {
*pRectCount = 1; *pRectCount = 1;
@ -1516,7 +1509,7 @@ VkResult MVKPhysicalDevice::getPresentRectangles(MVKSurface* surface,
*pRectCount = 1; *pRectCount = 1;
pRects[0].offset = { 0, 0 }; pRects[0].offset = { 0, 0 };
pRects[0].extent = mvkGetNaturalExtent(mtlLayer); pRects[0].extent = surface->getNaturalExtent();
return VK_SUCCESS; return VK_SUCCESS;
} }
@ -3672,14 +3665,14 @@ void MVKDevice::getCalibratedTimestamps(uint32_t timestampCount,
MTLTimestamp cpuStamp, gpuStamp; MTLTimestamp cpuStamp, gpuStamp;
uint64_t cpuStart, cpuEnd; uint64_t cpuStart, cpuEnd;
cpuStart = mvkGetAbsoluteTime(); cpuStart = mvkGetContinuousNanoseconds();
[getMTLDevice() sampleTimestamps: &cpuStamp gpuTimestamp: &gpuStamp]; [getMTLDevice() sampleTimestamps: &cpuStamp gpuTimestamp: &gpuStamp];
// Sample again to calculate the maximum deviation. Note that the // Sample again to calculate the maximum deviation. Note that the
// -[MTLDevice sampleTimestamps:gpuTimestamp:] method guarantees that CPU // -[MTLDevice sampleTimestamps:gpuTimestamp:] method guarantees that CPU
// timestamps are in nanoseconds. We don't want to call the method again, // 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- // because that could result in an expensive syscall to query the GPU time-
// stamp. // stamp.
cpuEnd = mvkGetAbsoluteTime(); cpuEnd = mvkGetContinuousNanoseconds();
for (uint32_t tsIdx = 0; tsIdx < timestampCount; ++tsIdx) { for (uint32_t tsIdx = 0; tsIdx < timestampCount; ++tsIdx) {
switch (pTimestampInfos[tsIdx].timeDomain) { switch (pTimestampInfos[tsIdx].timeDomain) {
case VK_TIME_DOMAIN_DEVICE_EXT: case VK_TIME_DOMAIN_DEVICE_EXT:

View File

@ -378,14 +378,8 @@ class MVKSwapchainImage : public MVKImage {
public: public:
/** Binds this resource to the specified offset within the specified memory allocation. */
VkResult bindDeviceMemory(MVKDeviceMemory* mvkMem, VkDeviceSize memOffset, uint8_t planeIndex) override; 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 #pragma mark Construction
@ -399,7 +393,6 @@ public:
protected: protected:
friend class MVKPeerSwapchainImage; friend class MVKPeerSwapchainImage;
virtual id<CAMetalDrawable> getCAMetalDrawable() = 0;
void detachSwapchain(); void detachSwapchain();
std::mutex _detachmentLock; std::mutex _detachmentLock;
@ -445,6 +438,8 @@ public:
#pragma mark Metal #pragma mark Metal
id<MTLTexture> getMTLTexture(uint8_t planeIndex) override;
/** Presents the contained drawable to the OS. */ /** Presents the contained drawable to the OS. */
VkResult presentCAMetalDrawable(id<MTLCommandBuffer> mtlCmdBuff, MVKImagePresentInfo presentInfo); VkResult presentCAMetalDrawable(id<MTLCommandBuffer> mtlCmdBuff, MVKImagePresentInfo presentInfo);
@ -468,16 +463,16 @@ public:
protected: protected:
friend MVKSwapchain; friend MVKSwapchain;
id<CAMetalDrawable> getCAMetalDrawable() override; id<CAMetalDrawable> getCAMetalDrawable();
void addPresentedHandler(id<CAMetalDrawable> mtlDrawable, MVKImagePresentInfo presentInfo, MVKSwapchainSignaler signaler); void addPresentedHandler(id<CAMetalDrawable> mtlDrawable, MVKImagePresentInfo presentInfo, MVKSwapchainSignaler signaler);
void releaseMetalDrawable(); void releaseMetalDrawable();
MVKSwapchainImageAvailability getAvailability(); MVKSwapchainImageAvailability getAvailability();
void makeAvailable(const MVKSwapchainSignaler& signaler);
void makeAvailable(); void makeAvailable();
VkResult acquireAndSignalWhenAvailable(MVKSemaphore* semaphore, MVKFence* fence); VkResult acquireAndSignalWhenAvailable(MVKSemaphore* semaphore, MVKFence* fence);
MVKSwapchainSignaler getPresentationSignaler(); MVKSwapchainSignaler getPresentationSignaler();
id<CAMetalDrawable> _mtlDrawable = nil; id<CAMetalDrawable> _mtlDrawable = nil;
id<MTLTexture> _mtlTextureHeadless = nil;
MVKSwapchainImageAvailability _availability; MVKSwapchainImageAvailability _availability;
MVKSmallVector<MVKSwapchainSignaler, 1> _availabilitySignalers; MVKSmallVector<MVKSwapchainSignaler, 1> _availabilitySignalers;
MVKSwapchainSignaler _preSignaler = {}; MVKSwapchainSignaler _preSignaler = {};
@ -494,7 +489,8 @@ class MVKPeerSwapchainImage : public MVKSwapchainImage {
public: public:
/** Binds this resource according to the specified bind information. */ id<MTLTexture> getMTLTexture(uint8_t planeIndex) override;
VkResult bindDeviceMemory2(const VkBindImageMemoryInfo* pBindInfo) override; VkResult bindDeviceMemory2(const VkBindImageMemoryInfo* pBindInfo) override;
@ -504,10 +500,6 @@ public:
const VkImageCreateInfo* pCreateInfo, const VkImageCreateInfo* pCreateInfo,
MVKSwapchain* swapchain, MVKSwapchain* swapchain,
uint32_t swapchainIndex); uint32_t swapchainIndex);
protected:
id<CAMetalDrawable> getCAMetalDrawable() override;
}; };

View File

@ -25,8 +25,10 @@
#include "MVKFoundation.h" #include "MVKFoundation.h"
#include "MVKOSExtensions.h" #include "MVKOSExtensions.h"
#include "MVKCodec.h" #include "MVKCodec.h"
#import "MTLTextureDescriptor+MoltenVK.h" #import "MTLTextureDescriptor+MoltenVK.h"
#import "MTLSamplerDescriptor+MoltenVK.h" #import "MTLSamplerDescriptor+MoltenVK.h"
#import "CAMetalLayer+MoltenVK.h"
using namespace std; using namespace std;
using namespace SPIRV_CROSS_NAMESPACE; using namespace SPIRV_CROSS_NAMESPACE;
@ -1169,12 +1171,6 @@ VkResult MVKSwapchainImage::bindDeviceMemory(MVKDeviceMemory* mvkMem, VkDeviceSi
} }
#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 #pragma mark Construction
MVKSwapchainImage::MVKSwapchainImage(MVKDevice* device, MVKSwapchainImage::MVKSwapchainImage(MVKDevice* device,
@ -1212,39 +1208,33 @@ MVKSwapchainImageAvailability MVKPresentableSwapchainImage::getAvailability() {
return _availability; 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. // 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.semaphore) { signaler.semaphore->retain(); }
if (signaler.fence) { signaler.fence->retain(); } if (signaler.fence) { signaler.fence->retain(); }
} }
// Tell the semaphore and fence that they are no longer being tracked for future signaling. static void signal(MVKSemaphore* semaphore, uint64_t semaphoreSignalToken, id<MTLCommandBuffer> mtlCmdBuff) {
static void unmarkAsTracked(const MVKSwapchainSignaler& signaler) { if (semaphore) { semaphore->encodeDeferredSignal(mtlCmdBuff, semaphoreSignalToken); }
if (signaler.semaphore) { signaler.semaphore->release(); }
if (signaler.fence) { signaler.fence->release(); }
} }
static void signalAndUnmarkAsTracked(const MVKSwapchainSignaler& signaler) { static void signal(MVKFence* fence) {
signal(signaler, nil); if (fence) { fence->signal(); }
unmarkAsTracked(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(); }
signal(signaler.fence);
if (signaler.fence) { signaler.fence->release(); }
} }
VkResult MVKPresentableSwapchainImage::acquireAndSignalWhenAvailable(MVKSemaphore* semaphore, MVKFence* fence) { VkResult MVKPresentableSwapchainImage::acquireAndSignalWhenAvailable(MVKSemaphore* semaphore, MVKFence* fence) {
// Now that this image is being acquired, release the existing drawable and its texture. // 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 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(); releaseMetalDrawable();
lock_guard<mutex> lock(_availabilityLock); lock_guard<mutex> lock(_availabilityLock);
@ -1267,7 +1257,8 @@ VkResult MVKPresentableSwapchainImage::acquireAndSignalWhenAvailable(MVKSemaphor
mtlCmdBuff = _device->getAnyQueue()->getMTLCommandBuffer(kMVKCommandUseAcquireNextImage); mtlCmdBuff = _device->getAnyQueue()->getMTLCommandBuffer(kMVKCommandUseAcquireNextImage);
if ( !mtlCmdBuff ) { setConfigurationResult(VK_ERROR_OUT_OF_POOL_MEMORY); } if ( !mtlCmdBuff ) { setConfigurationResult(VK_ERROR_OUT_OF_POOL_MEMORY); }
} }
signal(signaler, mtlCmdBuff); signal(signaler.semaphore, signaler.semaphoreSignalToken, mtlCmdBuff);
signal(signaler.fence);
[mtlCmdBuff commit]; [mtlCmdBuff commit];
} }
@ -1275,7 +1266,7 @@ VkResult MVKPresentableSwapchainImage::acquireAndSignalWhenAvailable(MVKSemaphor
} else { } else {
_availabilitySignalers.push_back(signaler); _availabilitySignalers.push_back(signaler);
} }
markAsTracked(signaler); track(signaler);
return getConfigurationResult(); return getConfigurationResult();
} }
@ -1284,6 +1275,9 @@ VkResult MVKPresentableSwapchainImage::acquireAndSignalWhenAvailable(MVKSemaphor
// Attempt several times to retrieve a good drawable, and set an error to trigger the // 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. // swapchain to be re-established if one cannot be retrieved.
id<CAMetalDrawable> MVKPresentableSwapchainImage::getCAMetalDrawable() { id<CAMetalDrawable> MVKPresentableSwapchainImage::getCAMetalDrawable() {
if (_mtlTextureHeadless) { return nil; } // If headless, there is no drawable.
if ( !_mtlDrawable ) { if ( !_mtlDrawable ) {
@autoreleasepool { @autoreleasepool {
bool hasInvalidFormat = false; bool hasInvalidFormat = false;
@ -1305,6 +1299,11 @@ id<CAMetalDrawable> MVKPresentableSwapchainImage::getCAMetalDrawable() {
return _mtlDrawable; 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. // 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. // Pass MVKImagePresentInfo by value because it may not exist when the callback runs.
VkResult MVKPresentableSwapchainImage::presentCAMetalDrawable(id<MTLCommandBuffer> mtlCmdBuff, VkResult MVKPresentableSwapchainImage::presentCAMetalDrawable(id<MTLCommandBuffer> mtlCmdBuff,
@ -1343,15 +1342,13 @@ VkResult MVKPresentableSwapchainImage::presentCAMetalDrawable(id<MTLCommandBuffe
auto* fence = presentInfo.fence; auto* fence = presentInfo.fence;
if (fence) { fence->retain(); } if (fence) { fence->retain(); }
[mtlCmdBuff addCompletedHandler: ^(id<MTLCommandBuffer> mcb) { [mtlCmdBuff addCompletedHandler: ^(id<MTLCommandBuffer> mcb) {
if (fence) { signal(fence);
fence->signal(); if (fence) { fence->release(); }
fence->release();
}
[mtlDrwbl release]; [mtlDrwbl release];
release(); release();
}]; }];
signalPresentationSemaphore(signaler, mtlCmdBuff); signal(signaler.semaphore, signaler.semaphoreSignalToken, mtlCmdBuff);
return getConfigurationResult(); return getConfigurationResult();
} }
@ -1408,6 +1405,13 @@ void MVKPresentableSwapchainImage::beginPresentation(const MVKImagePresentInfo&
void MVKPresentableSwapchainImage::endPresentation(const MVKImagePresentInfo& presentInfo, void MVKPresentableSwapchainImage::endPresentation(const MVKImagePresentInfo& presentInfo,
const MVKSwapchainSignaler& signaler, const MVKSwapchainSignaler& signaler,
uint64_t actualPresentTime) { 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 { // 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 // 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. // VkDevice, have been destroyed by the time of this callback, so do not reference them.
@ -1415,7 +1419,11 @@ void MVKPresentableSwapchainImage::endPresentation(const MVKImagePresentInfo& pr
if (_device) { _device->addPerformanceInterval(_device->_performanceStatistics.queue.presentSwapchains, _presentationStartTime); } if (_device) { _device->addPerformanceInterval(_device->_performanceStatistics.queue.presentSwapchains, _presentationStartTime); }
if (_swapchain) { _swapchain->endPresentation(presentInfo, actualPresentTime); } 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(); release();
} }
@ -1425,15 +1433,6 @@ void MVKPresentableSwapchainImage::releaseMetalDrawable() {
_mtlDrawable = nil; _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. // Signal, untrack, and release any signalers that are tracking.
// Release the drawable before the lock, as it may trigger completion callback. // Release the drawable before the lock, as it may trigger completion callback.
void MVKPresentableSwapchainImage::makeAvailable() { void MVKPresentableSwapchainImage::makeAvailable() {
@ -1441,9 +1440,9 @@ void MVKPresentableSwapchainImage::makeAvailable() {
lock_guard<mutex> lock(_availabilityLock); lock_guard<mutex> lock(_availabilityLock);
if ( !_availability.isAvailable ) { if ( !_availability.isAvailable ) {
signalAndUnmarkAsTracked(_preSignaler); signalAndUntrack(_preSignaler);
for (auto& sig : _availabilitySignalers) { for (auto& sig : _availabilitySignalers) {
signalAndUnmarkAsTracked(sig); signalAndUntrack(sig);
} }
_availabilitySignalers.clear(); _availabilitySignalers.clear();
_availability.isAvailable = true; _availability.isAvailable = true;
@ -1460,11 +1459,26 @@ MVKPresentableSwapchainImage::MVKPresentableSwapchainImage(MVKDevice* device,
_availability.acquisitionID = _swapchain->getNextAcquisitionID(); _availability.acquisitionID = _swapchain->getNextAcquisitionID();
_availability.isAvailable = true; _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() { void MVKPresentableSwapchainImage::destroy() {
releaseMetalDrawable(); releaseMetalDrawable();
[_mtlTextureHeadless release];
_mtlTextureHeadless = nil;
MVKSwapchainImage::destroy(); MVKSwapchainImage::destroy();
} }
@ -1498,8 +1512,8 @@ VkResult MVKPeerSwapchainImage::bindDeviceMemory2(const VkBindImageMemoryInfo* p
#pragma mark Metal #pragma mark Metal
id<CAMetalDrawable> MVKPeerSwapchainImage::getCAMetalDrawable() { id<MTLTexture> MVKPeerSwapchainImage::getMTLTexture(uint8_t planeIndex) {
return ((MVKSwapchainImage*)_swapchain->getPresentableImage(_swapchainIndex))->getCAMetalDrawable(); return ((MVKSwapchainImage*)_swapchain->getPresentableImage(_swapchainIndex))->getMTLTexture(planeIndex);
} }

View File

@ -117,6 +117,9 @@ public:
MVKSurface* createSurface(const VkMetalSurfaceCreateInfoEXT* pCreateInfo, MVKSurface* createSurface(const VkMetalSurfaceCreateInfoEXT* pCreateInfo,
const VkAllocationCallbacks* pAllocator); const VkAllocationCallbacks* pAllocator);
MVKSurface* createSurface(const VkHeadlessSurfaceCreateInfoEXT* pCreateInfo,
const VkAllocationCallbacks* pAllocator);
MVKSurface* createSurface(const Vk_PLATFORM_SurfaceCreateInfoMVK* pCreateInfo, MVKSurface* createSurface(const Vk_PLATFORM_SurfaceCreateInfoMVK* pCreateInfo,
const VkAllocationCallbacks* pAllocator); const VkAllocationCallbacks* pAllocator);

View File

@ -102,6 +102,11 @@ MVKSurface* MVKInstance::createSurface(const VkMetalSurfaceCreateInfoEXT* pCreat
return new MVKSurface(this, pCreateInfo, pAllocator); 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, MVKSurface* MVKInstance::createSurface(const Vk_PLATFORM_SurfaceCreateInfoMVK* pCreateInfo,
const VkAllocationCallbacks* pAllocator) { const VkAllocationCallbacks* pAllocator) {
return new MVKSurface(this, pCreateInfo, pAllocator); return new MVKSurface(this, pCreateInfo, pAllocator);
@ -426,6 +431,8 @@ void MVKInstance::initProcAddrs() {
ADD_INST_EXT_ENTRY_POINT(vkGetPhysicalDeviceSurfacePresentModesKHR, KHR_SURFACE); ADD_INST_EXT_ENTRY_POINT(vkGetPhysicalDeviceSurfacePresentModesKHR, KHR_SURFACE);
ADD_INST_EXT_ENTRY_POINT(vkGetPhysicalDeviceSurfaceCapabilities2KHR, KHR_GET_SURFACE_CAPABILITIES_2); 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(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(vkCreateDebugReportCallbackEXT, EXT_DEBUG_REPORT);
ADD_INST_EXT_ENTRY_POINT(vkDestroyDebugReportCallbackEXT, EXT_DEBUG_REPORT); ADD_INST_EXT_ENTRY_POINT(vkDestroyDebugReportCallbackEXT, EXT_DEBUG_REPORT);
ADD_INST_EXT_ENTRY_POINT(vkDebugReportMessageEXT, EXT_DEBUG_REPORT); ADD_INST_EXT_ENTRY_POINT(vkDebugReportMessageEXT, EXT_DEBUG_REPORT);
@ -441,7 +448,6 @@ void MVKInstance::initProcAddrs() {
ADD_INST_EXT_ENTRY_POINT(vkCreateDebugUtilsMessengerEXT, EXT_DEBUG_UTILS); ADD_INST_EXT_ENTRY_POINT(vkCreateDebugUtilsMessengerEXT, EXT_DEBUG_UTILS);
ADD_INST_EXT_ENTRY_POINT(vkDestroyDebugUtilsMessengerEXT, 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(vkSubmitDebugUtilsMessageEXT, EXT_DEBUG_UTILS);
ADD_INST_EXT_ENTRY_POINT(vkCreateMetalSurfaceEXT, EXT_METAL_SURFACE);
#ifdef VK_USE_PLATFORM_IOS_MVK #ifdef VK_USE_PLATFORM_IOS_MVK
ADD_INST_EXT_ENTRY_POINT(vkCreateIOSSurfaceMVK, MVK_IOS_SURFACE); ADD_INST_EXT_ENTRY_POINT(vkCreateIOSSurfaceMVK, MVK_IOS_SURFACE);

View File

@ -20,7 +20,7 @@
#include "MVKBaseObject.h" #include "MVKBaseObject.h"
#include "MVKOSExtensions.h" #include "MVKOSExtensions.h"
#include "mvk_datatypes.h" #include "mvk_datatypes.hpp"
#include <spirv_msl.hpp> #include <spirv_msl.hpp>
#include <unordered_map> #include <unordered_map>

View File

@ -24,16 +24,6 @@
#import <Metal/Metal.h> #import <Metal/Metal.h>
#import <QuartzCore/CAMetalLayer.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 MVKInstance;
class MVKSwapchain; class MVKSwapchain;
@ -59,6 +49,14 @@ public:
/** Returns the CAMetalLayer underlying this surface. */ /** Returns the CAMetalLayer underlying this surface. */
CAMetalLayer* getCAMetalLayer(); 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 #pragma mark Construction
@ -66,6 +64,10 @@ public:
const VkMetalSurfaceCreateInfoEXT* pCreateInfo, const VkMetalSurfaceCreateInfoEXT* pCreateInfo,
const VkAllocationCallbacks* pAllocator); const VkAllocationCallbacks* pAllocator);
MVKSurface(MVKInstance* mvkInstance,
const VkHeadlessSurfaceCreateInfoEXT* pCreateInfo,
const VkAllocationCallbacks* pAllocator);
MVKSurface(MVKInstance* mvkInstance, MVKSurface(MVKInstance* mvkInstance,
const Vk_PLATFORM_SurfaceCreateInfoMVK* pCreateInfo, const Vk_PLATFORM_SurfaceCreateInfoMVK* pCreateInfo,
const VkAllocationCallbacks* pAllocator); const VkAllocationCallbacks* pAllocator);
@ -76,7 +78,8 @@ protected:
friend class MVKSwapchain; friend class MVKSwapchain;
void propagateDebugName() override {} 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(); void releaseLayer();
std::mutex _layerLock; std::mutex _layerLock;
@ -84,5 +87,6 @@ protected:
CAMetalLayer* _mtlCAMetalLayer = nil; CAMetalLayer* _mtlCAMetalLayer = nil;
MVKBlockObserver* _layerObserver = nil; MVKBlockObserver* _layerObserver = nil;
MVKSwapchain* _activeSwapchain = nullptr; MVKSwapchain* _activeSwapchain = nullptr;
VkExtent2D _headlessExtent = {0xFFFFFFFF, 0xFFFFFFFF};
}; };

View File

@ -17,11 +17,26 @@
*/ */
#include "MVKSurface.h" #include "MVKSurface.h"
#include "MVKSwapchain.h"
#include "MVKInstance.h" #include "MVKInstance.h"
#include "MVKFoundation.h" #include "MVKFoundation.h"
#include "MVKOSExtensions.h" #include "MVKOSExtensions.h"
#include "mvk_datatypes.hpp"
#import "CAMetalLayer+MoltenVK.h"
#import "MVKBlockObserver.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. // 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_PLATFORM(NAME) #NAME
#define STR(NAME) STR_PLATFORM(NAME) #define STR(NAME) STR_PLATFORM(NAME)
@ -34,38 +49,55 @@ CAMetalLayer* MVKSurface::getCAMetalLayer() {
return _mtlCAMetalLayer; 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, MVKSurface::MVKSurface(MVKInstance* mvkInstance,
const VkMetalSurfaceCreateInfoEXT* pCreateInfo, const VkMetalSurfaceCreateInfoEXT* pCreateInfo,
const VkAllocationCallbacks* pAllocator) : _mvkInstance(mvkInstance) { 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). // pCreateInfo->pView can be either a CAMetalLayer or a view (NSView/UIView).
MVKSurface::MVKSurface(MVKInstance* mvkInstance, MVKSurface::MVKSurface(MVKInstance* mvkInstance,
const Vk_PLATFORM_SurfaceCreateInfoMVK* pCreateInfo, const Vk_PLATFORM_SurfaceCreateInfoMVK* pCreateInfo,
const VkAllocationCallbacks* pAllocator) : _mvkInstance(mvkInstance) { 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 // 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. // 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]]) { if ([obj isKindOfClass: [PLATFORM_VIEW_CLASS class]]) {
obj = ((PLATFORM_VIEW_CLASS*)obj).layer; __block id<NSObject> layer;
if ( !NSThread.isMainThread ) { mvkDispatchToMainAndWait(^{ layer = ((PLATFORM_VIEW_CLASS*)obj).layer; });
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.", obj = layer;
STR(vkCreate_PLATFORM_SurfaceMVK), STR(PLATFORM_VIEW_CLASS), STR(Vk_PLATFORM_SurfaceCreateInfoMVK), STR(PLATFORM_VIEW_CLASS));
}
} }
// Confirm that we were provided with a CAMetalLayer // Confirm that we were provided with a CAMetalLayer
initLayer([obj isKindOfClass: CAMetalLayer.class] ? (CAMetalLayer*)obj : nil, initLayer([obj isKindOfClass: CAMetalLayer.class] ? (CAMetalLayer*)obj : nil, STR(vkCreate_PLATFORM_SurfaceMVK), false);
STR(vkCreate_PLATFORM_SurfaceMVK));
} }
void MVKSurface::initLayer(CAMetalLayer* mtlLayer, const char* vkFuncName) { void MVKSurface::initLayer(CAMetalLayer* mtlLayer, const char* vkFuncName, bool isHeadless) {
_mtlCAMetalLayer = [mtlLayer retain]; // retained _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. // Sometimes, the owning view can replace its CAMetalLayer.
// When that happens, the app needs to recreate the surface. // When that happens, the app needs to recreate the surface.

View File

@ -23,7 +23,6 @@
#include "MVKSmallVector.h" #include "MVKSmallVector.h"
#include <mutex> #include <mutex>
#import "CAMetalLayer+MoltenVK.h"
#import <Metal/Metal.h> #import <Metal/Metal.h>
class MVKWatermark; class MVKWatermark;
@ -46,9 +45,15 @@ public:
/** Returns the CAMetalLayer underlying the surface used by this swapchain. */ /** Returns the CAMetalLayer underlying the surface used by this swapchain. */
CAMetalLayer* getCAMetalLayer(); CAMetalLayer* getCAMetalLayer();
/** Returns whether the surface is headless. */
bool isHeadless();
/** Returns the number of images in this swapchain. */ /** Returns the number of images in this swapchain. */
uint32_t getImageCount() { return (uint32_t)_presentableImages.size(); } 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. */ /** Returns the image at the specified index. */
MVKPresentableSwapchainImage* getPresentableImage(uint32_t index) { return _presentableImages[index]; } MVKPresentableSwapchainImage* getPresentableImage(uint32_t index) { return _presentableImages[index]; }
@ -126,7 +131,7 @@ protected:
std::atomic<uint64_t> _currentAcquisitionID = 0; std::atomic<uint64_t> _currentAcquisitionID = 0;
std::mutex _presentHistoryLock; std::mutex _presentHistoryLock;
uint64_t _lastFrameTime = 0; uint64_t _lastFrameTime = 0;
VkExtent2D _mtlLayerDrawableExtent = {0, 0}; VkExtent2D _imageExtent = {0, 0};
std::atomic<uint32_t> _unpresentedImageCount = 0; std::atomic<uint32_t> _unpresentedImageCount = 0;
uint32_t _currentPerfLogFrameCount = 0; uint32_t _currentPerfLogFrameCount = 0;
uint32_t _presentHistoryCount = 0; uint32_t _presentHistoryCount = 0;
@ -134,18 +139,3 @@ protected:
uint32_t _presentHistoryHeadIndex = 0; uint32_t _presentHistoryHeadIndex = 0;
bool _isDeliberatelyScaled = false; 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);
}

View File

@ -26,9 +26,11 @@
#include "MVKWatermarkTextureContent.h" #include "MVKWatermarkTextureContent.h"
#include "MVKWatermarkShaderSource.h" #include "MVKWatermarkShaderSource.h"
#include "mvk_datatypes.hpp" #include "mvk_datatypes.hpp"
#include <libkern/OSByteOrder.h>
#import "CAMetalLayer+MoltenVK.h"
#import "MVKBlockObserver.h" #import "MVKBlockObserver.h"
#include <libkern/OSByteOrder.h>
using namespace std; using namespace std;
@ -49,6 +51,8 @@ void MVKSwapchain::propagateDebugName() {
CAMetalLayer* MVKSwapchain::getCAMetalLayer() { return _surface->getCAMetalLayer(); } CAMetalLayer* MVKSwapchain::getCAMetalLayer() { return _surface->getCAMetalLayer(); }
bool MVKSwapchain::isHeadless() { return _surface->isHeadless(); }
VkResult MVKSwapchain::getImages(uint32_t* pCount, VkImage* pSwapchainImages) { VkResult MVKSwapchain::getImages(uint32_t* pCount, VkImage* pSwapchainImages) {
// Get the number of surface images // Get the number of surface images
@ -124,16 +128,15 @@ VkResult MVKSwapchain::getSurfaceStatus() {
return VK_SUCCESS; return VK_SUCCESS;
} }
// This swapchain is optimally sized for the surface if the app has specified deliberate // This swapchain is optimally sized for the surface if the app has specified
// swapchain scaling, or the CAMetalLayer drawableSize has not changed since the swapchain // deliberate swapchain scaling, or the surface extent has not changed since the
// was created, and the CAMetalLayer will not need to be scaled when composited. // swapchain was created, and the surface will not need to be scaled when composited.
bool MVKSwapchain::hasOptimalSurface() { bool MVKSwapchain::hasOptimalSurface() {
if (_isDeliberatelyScaled) { return true; } if (_isDeliberatelyScaled) { return true; }
auto* mtlLayer = getCAMetalLayer(); VkExtent2D surfExtent = _surface->getExtent();
VkExtent2D drawExtent = mvkVkExtent2DFromCGSize(mtlLayer.drawableSize); return (mvkVkExtent2DsAreEqual(surfExtent, _imageExtent) &&
return (mvkVkExtent2DsAreEqual(drawExtent, _mtlLayerDrawableExtent) && mvkVkExtent2DsAreEqual(surfExtent, _surface->getNaturalExtent()));
mvkVkExtent2DsAreEqual(drawExtent, mvkGetNaturalExtent(mtlLayer)));
} }
@ -187,30 +190,29 @@ void MVKSwapchain::markFrameInterval() {
VkResult MVKSwapchain::getRefreshCycleDuration(VkRefreshCycleDurationGOOGLE *pRefreshCycleDuration) { VkResult MVKSwapchain::getRefreshCycleDuration(VkRefreshCycleDurationGOOGLE *pRefreshCycleDuration) {
if (_device->getConfigurationResult() != VK_SUCCESS) { return _device->getConfigurationResult(); } if (_device->getConfigurationResult() != VK_SUCCESS) { return _device->getConfigurationResult(); }
auto* mtlLayer = getCAMetalLayer(); auto* screen = getCAMetalLayer().screenMVK; // Will be nil if headless
#if MVK_VISIONOS #if MVK_MACOS && !MVK_MACCAT
// TODO: See if this can be obtained from OS instead double framesPerSecond = 60;
NSInteger framesPerSecond = 90; 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 #elif MVK_IOS_OR_TVOS || MVK_MACCAT
NSInteger framesPerSecond = 60; NSInteger framesPerSecond = 60;
UIScreen* screen = mtlLayer.screenMVK;
if ([screen respondsToSelector: @selector(maximumFramesPerSecond)]) { if ([screen respondsToSelector: @selector(maximumFramesPerSecond)]) {
framesPerSecond = screen.maximumFramesPerSecond; framesPerSecond = screen.maximumFramesPerSecond;
} }
#elif MVK_MACOS && !MVK_MACCAT #elif MVK_VISIONOS
NSScreen* screen = mtlLayer.screenMVK; NSInteger framesPerSecond = 90; // TODO: See if this can be obtained from OS instead
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;
#endif #endif
pRefreshCycleDuration->refreshDuration = (uint64_t)1e9 / framesPerSecond; pRefreshCycleDuration->refreshDuration = (uint64_t)1e9 / framesPerSecond;
@ -260,12 +262,6 @@ void MVKSwapchain::endPresentation(const MVKImagePresentInfo& presentInfo, uint6
_presentHistoryHeadIndex = (_presentHistoryHeadIndex + 1) % kMaxPresentationHistory; _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].presentID = presentInfo.presentID;
_presentTimingHistory[_presentHistoryIndex].desiredPresentTime = presentInfo.desiredPresentTime; _presentTimingHistory[_presentHistoryIndex].desiredPresentTime = presentInfo.desiredPresentTime;
_presentTimingHistory[_presentHistoryIndex].actualPresentTime = actualPresentTime; _presentTimingHistory[_presentHistoryIndex].actualPresentTime = actualPresentTime;
@ -380,12 +376,13 @@ void MVKSwapchain::setHDRMetadataEXT(const VkHdrMetadataEXT& metadata) {
MVKSwapchain::MVKSwapchain(MVKDevice* device, const VkSwapchainCreateInfoKHR* pCreateInfo) MVKSwapchain::MVKSwapchain(MVKDevice* device, const VkSwapchainCreateInfoKHR* pCreateInfo)
: MVKVulkanAPIDeviceObject(device), : MVKVulkanAPIDeviceObject(device),
_surface((MVKSurface*)pCreateInfo->surface) { _surface((MVKSurface*)pCreateInfo->surface),
_imageExtent(pCreateInfo->imageExtent) {
// Check if oldSwapchain is properly set // Check if oldSwapchain is properly set
auto* oldSwapchain = (MVKSwapchain*)pCreateInfo->oldSwapchain; auto* oldSwapchain = (MVKSwapchain*)pCreateInfo->oldSwapchain;
if (oldSwapchain == _surface->_activeSwapchain) { if (oldSwapchain == _surface->_activeSwapchain) {
_surface->_activeSwapchain = this; _surface->setActiveSwapchain(this);
} else { } 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")); 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; return;
@ -470,10 +467,11 @@ void MVKSwapchain::initCAMetalLayer(const VkSwapchainCreateInfoKHR* pCreateInfo,
VkSwapchainPresentScalingCreateInfoEXT* pScalingInfo, VkSwapchainPresentScalingCreateInfoEXT* pScalingInfo,
uint32_t imgCnt) { uint32_t imgCnt) {
if ( getIsSurfaceLost() ) { return; }
auto* mtlLayer = getCAMetalLayer(); auto* mtlLayer = getCAMetalLayer();
if ( !mtlLayer || getIsSurfaceLost() ) { return; }
auto minMagFilter = mvkConfig().swapchainMinMagFilterUseNearest ? kCAFilterNearest : kCAFilterLinear; auto minMagFilter = mvkConfig().swapchainMinMagFilterUseNearest ? kCAFilterNearest : kCAFilterLinear;
mtlLayer.drawableSize = mvkCGSizeFromVkExtent2D(_imageExtent);
mtlLayer.device = getMTLDevice(); mtlLayer.device = getMTLDevice();
mtlLayer.pixelFormat = getPixelFormats()->getMTLPixelFormat(pCreateInfo->imageFormat); mtlLayer.pixelFormat = getPixelFormats()->getMTLPixelFormat(pCreateInfo->imageFormat);
mtlLayer.maximumDrawableCountMVK = imgCnt; mtlLayer.maximumDrawableCountMVK = imgCnt;
@ -491,15 +489,10 @@ void MVKSwapchain::initCAMetalLayer(const VkSwapchainCreateInfoKHR* pCreateInfo,
// presentations on the oldSwapchain to complete and call back, but if the drawableSize // presentations on the oldSwapchain to complete and call back, but if the drawableSize
// is not changing from the previous, we force those completions first. // is not changing from the previous, we force those completions first.
auto* oldSwapchain = (MVKSwapchain*)pCreateInfo->oldSwapchain; auto* oldSwapchain = (MVKSwapchain*)pCreateInfo->oldSwapchain;
if (oldSwapchain && mvkVkExtent2DsAreEqual(pCreateInfo->imageExtent, mvkVkExtent2DFromCGSize(mtlLayer.drawableSize))) { if (oldSwapchain && mvkVkExtent2DsAreEqual(pCreateInfo->imageExtent, _surface->getExtent())) {
oldSwapchain->forceUnpresentedImageCompletion(); 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) { if (pCreateInfo->compositeAlpha != VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR) {
mtlLayer.opaque = pCreateInfo->compositeAlpha == VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; mtlLayer.opaque = pCreateInfo->compositeAlpha == VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
} }
@ -585,14 +578,13 @@ void MVKSwapchain::initSurfaceImages(const VkSwapchainCreateInfoKHR* pCreateInfo
} }
} }
auto* mtlLayer = getCAMetalLayer();
VkExtent2D imgExtent = pCreateInfo->imageExtent; VkExtent2D imgExtent = pCreateInfo->imageExtent;
VkImageCreateInfo imgInfo = { VkImageCreateInfo imgInfo = {
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.pNext = VK_NULL_HANDLE, .pNext = VK_NULL_HANDLE,
.imageType = VK_IMAGE_TYPE_2D, .imageType = VK_IMAGE_TYPE_2D,
.format = getPixelFormats()->getVkFormat(mtlLayer.pixelFormat), .format = pCreateInfo->imageFormat,
.extent = { imgExtent.width, imgExtent.height, 1 }, .extent = mvkVkExtent3DFromVkExtent2D(imgExtent),
.mipLevels = 1, .mipLevels = 1,
.arrayLayers = 1, .arrayLayers = 1,
.samples = VK_SAMPLE_COUNT_1_BIT, .samples = VK_SAMPLE_COUNT_1_BIT,
@ -618,14 +610,20 @@ void MVKSwapchain::initSurfaceImages(const VkSwapchainCreateInfoKHR* pCreateInfo
_presentableImages.push_back(_device->createPresentableSwapchainImage(&imgInfo, this, imgIdx, nullptr)); _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 MVK_MACOS && !MVK_MACCAT
if ([mtlLayer.screenMVK respondsToSelector:@selector(localizedName)]) { auto* screen = mtlLayer.screenMVK;
screenName = mtlLayer.screenMVK.localizedName; if ([screen respondsToSelector:@selector(localizedName)]) {
} screenName = screen.localizedName;
}
#endif #endif
MVKLogInfo("Created %d swapchain images with size (%d, %d) and contents scale %.1f in layer %s (%p) on screen %s.", 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); 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() { void MVKSwapchain::destroy() {

View File

@ -128,7 +128,7 @@ uint64_t MVKSemaphoreMTLEvent::deferSignal() {
} }
void MVKSemaphoreMTLEvent::encodeDeferredSignal(id<MTLCommandBuffer> mtlCmdBuff, uint64_t deferToken) { 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, MVKSemaphoreMTLEvent::MVKSemaphoreMTLEvent(MVKDevice* device,

View File

@ -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_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_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_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_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_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) MVK_EXTENSION(EXT_inline_uniform_block, EXT_INLINE_UNIFORM_BLOCK, DEVICE, 10.11, 8.0, 1.0)

View File

@ -23,12 +23,10 @@
#import <QuartzCore/QuartzCore.h> #import <QuartzCore/QuartzCore.h>
#if MVK_IOS_OR_TVOS || MVK_MACCAT #if MVK_IOS_OR_TVOS || MVK_MACCAT
# define PLATFORM_SCREEN_CLASS UIScreen
# include <UIKit/UIScreen.h> # include <UIKit/UIScreen.h>
#endif #endif
#if MVK_MACOS && !MVK_MACCAT #if MVK_MACOS && !MVK_MACCAT
# define PLATFORM_SCREEN_CLASS NSScreen
# include <AppKit/NSScreen.h> # include <AppKit/NSScreen.h>
#endif #endif
@ -76,9 +74,16 @@
*/ */
@property(nonatomic, readwrite) CFStringRef colorspaceNameMVK; @property(nonatomic, readwrite) CFStringRef colorspaceNameMVK;
#if !MVK_VISIONOS #if MVK_IOS_OR_TVOS || MVK_MACCAT
/** Returns the screen on which this layer is rendering. */ /** 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 #endif
@end @end

View File

@ -18,6 +18,7 @@
#include "CAMetalLayer+MoltenVK.h" #include "CAMetalLayer+MoltenVK.h"
#include "MVKOSExtensions.h"
#if MVK_MACOS && !MVK_MACCAT #if MVK_MACOS && !MVK_MACCAT
# include <AppKit/NSApplication.h> # include <AppKit/NSApplication.h>
@ -88,6 +89,13 @@
#if MVK_MACOS && !MVK_MACCAT #if MVK_MACOS && !MVK_MACCAT
-(NSScreen*) screenMVK { -(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 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]) { if ([self.delegate isKindOfClass: NSView.class]) {
NSWindow* window = ((NSView*)self.delegate).window; NSWindow* window = ((NSView*)self.delegate).window;

View File

@ -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 -
#pragma mark VK_EXT_host_query_reset extension #pragma mark VK_EXT_host_query_reset extension

View File

@ -149,21 +149,14 @@ for which to build the external libraries. The platform choices include:
--maccat --maccat
--tvos --tvos
--tvossim --tvossim
--visionos
--visionossim
The `visionos` and `visionossim` selections require Xcode 15+.
You can specify multiple of these selections. The result is a single `XCFramework` You can specify multiple of these selections. The result is a single `XCFramework`
for each external dependency library, with each `XCFramework` containing binaries for for each external dependency library, with each `XCFramework` containing binaries for
each of the requested platforms. each of the requested platforms.
The `--all` selection is the same as entering all of the other platform choices, except The `--all` selection is the same as entering all of the other platform choices,
`--visionos` and `--visionossim`, and will result in a single `XCFramework` for each and will result in a single `XCFramework` for each external dependency library,
external dependency library, with each `XCFramework` containing binaries for all supported with each `XCFramework` containing binaries for all supported platforms and simulators.
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.
Running `fetchDependencies` repeatedly with different platforms will accumulate targets Running `fetchDependencies` repeatedly with different platforms will accumulate targets
in the `XCFramework`, if the `--keep-cache` option is used on each invocation. 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 maccat
make tvos make tvos
make tvossim make tvossim
make visionos
make visionossim
make all-debug make all-debug
make macos-debug make macos-debug
@ -273,15 +264,12 @@ from the command line. The following `make` targets are provided:
make maccat-debug make maccat-debug
make tvos-debug make tvos-debug
make tvossim-debug make tvossim-debug
make visionos-debug
make visionossim-debug
make clean make clean
make install make install
- Running `make` repeatedly with different targets will accumulate binaries for these different targets. - 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 - The `all` target executes all platform targets.
Xcode 15+, and will abort a multi-platform build on older versions of Xcode.
- The `all` target is the default target. Running `make` with no arguments is the same as running `make all`. - 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 `*-debug` targets build the binaries using the **_Debug_** configuration.
- The `install` target will copy the most recently built `MoltenVK.xcframework` into the - The `install` target will copy the most recently built `MoltenVK.xcframework` into the