From 4e3ac6930523349099a2bd348ac70a143835c39a Mon Sep 17 00:00:00 2001 From: Chip Davis Date: Fri, 23 Jun 2023 01:15:32 -0700 Subject: [PATCH] Support the `VK_EXT_calibrated_timestamps` extension. This extension has a direct Metal equivalent in the `-[MTLDevice sampleTimestamps:gpuTimestamp:]` method. However, that method returns CPU timestamps in the Mach absolute time domain, which is *not* that of `CLOCK_MONOTONIC_RAW` but of `CLOCK_UPTIME_RAW`. The function that corresponds to `CLOCK_MONOTONIC_RAW` is `mach_continuous_time()`. Therefore, this implementation uses the `mach_continuous_time()` function for the CPU timestamp. Perhaps we should lobby the WG for `VK_TIME_DOMAIN_CLOCK_UPTIME_RAW_EXT`. --- Common/MVKOSExtensions.h | 3 ++ Common/MVKOSExtensions.mm | 8 ++-- Docs/MoltenVK_Runtime_UserGuide.md | 1 + Docs/Whats_New.md | 1 + MoltenVK/MoltenVK/GPUObjects/MVKDevice.h | 8 ++++ MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm | 48 +++++++++++++++++++++ MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm | 2 + MoltenVK/MoltenVK/Layers/MVKExtensions.def | 1 + MoltenVK/MoltenVK/Vulkan/vulkan.mm | 28 ++++++++++++ 9 files changed, 97 insertions(+), 3 deletions(-) diff --git a/Common/MVKOSExtensions.h b/Common/MVKOSExtensions.h index 23708325..ff96991c 100644 --- a/Common/MVKOSExtensions.h +++ b/Common/MVKOSExtensions.h @@ -84,6 +84,9 @@ double mvkGetTimestampPeriod(); */ 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); diff --git a/Common/MVKOSExtensions.mm b/Common/MVKOSExtensions.mm index ef98f0b5..672335d7 100644 --- a/Common/MVKOSExtensions.mm +++ b/Common/MVKOSExtensions.mm @@ -40,6 +40,7 @@ MVKOSVersion mvkOSVersion() { static uint64_t _mvkTimestampBase; static double _mvkTimestampPeriod; +static mach_timebase_info_data_t _mvkMachTimebase; uint64_t mvkGetTimestamp() { return mach_absolute_time() - _mvkTimestampBase; } @@ -50,6 +51,8 @@ double mvkGetElapsedMilliseconds(uint64_t startTimestamp, uint64_t endTimestamp) return (double)(endTimestamp - startTimestamp) * _mvkTimestampPeriod / 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. static bool _mvkTimestampsInitialized = false; @@ -58,9 +61,8 @@ __attribute__((constructor)) static void MVKInitTimestamps() { _mvkTimestampsInitialized = true; _mvkTimestampBase = mach_absolute_time(); - mach_timebase_info_data_t timebase; - mach_timebase_info(&timebase); - _mvkTimestampPeriod = (double)timebase.numer / (double)timebase.denom; + mach_timebase_info(&_mvkMachTimebase); + _mvkTimestampPeriod = (double)_mvkMachTimebase.numer / (double)_mvkMachTimebase.denom; } void mvkDispatchToMainAndWait(dispatch_block_t block) { diff --git a/Docs/MoltenVK_Runtime_UserGuide.md b/Docs/MoltenVK_Runtime_UserGuide.md index 3775a749..aee9bfa9 100644 --- a/Docs/MoltenVK_Runtime_UserGuide.md +++ b/Docs/MoltenVK_Runtime_UserGuide.md @@ -352,6 +352,7 @@ In addition to core *Vulkan* functionality, **MoltenVK** also supports the foll - `VK_KHR_variable_pointers` - `VK_EXT_4444_formats` *(requires 16-bit formats and either native texture swizzling or manual swizzling to be enabled)* - `VK_EXT_buffer_device_address` *(requires GPU Tier 2 argument buffers support)* +- `VK_EXT_calibrated_timestamps` *(requires Metal 2.2)* - `VK_EXT_debug_marker` - `VK_EXT_debug_report` - `VK_EXT_debug_utils` diff --git a/Docs/Whats_New.md b/Docs/Whats_New.md index 1a1284e0..565c96a1 100644 --- a/Docs/Whats_New.md +++ b/Docs/Whats_New.md @@ -20,6 +20,7 @@ Released TBD - Add support for extensions: - `VK_EXT_4444_formats` + - `VK_EXT_calibrated_timestamps` - `VK_EXT_shader_demote_to_helper_invocation` - Ensure non-dispatch compute commands don't interfere with compute encoding state used by dispatch commands. - Support `VK_PRESENT_MODE_IMMEDIATE_KHR` if `VkPresentTimeGOOGLE::desiredPresentTime` is zero. diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h index d136d7d5..5186fe4a 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h @@ -175,6 +175,9 @@ public: void getExternalSemaphoreProperties(const VkPhysicalDeviceExternalSemaphoreInfo* pExternalSemaphoreInfo, VkExternalSemaphoreProperties* pExternalSemaphoreProperties); + /** Returns the supported time domains for calibration on this device. */ + VkResult getCalibrateableTimeDomains(uint32_t* pTimeDomainCount, VkTimeDomainEXT* pTimeDomains); + #pragma mark Surfaces /** @@ -515,6 +518,11 @@ public: const void* pHostPointer, VkMemoryHostPointerPropertiesEXT* pMemHostPtrProps); + /** Samples timestamps from the specified domains and returns the sampled values. */ + void getCalibratedTimestamps(uint32_t timestampCount, + const VkCalibratedTimestampInfoEXT* pTimestampInfos, + uint64_t* pTimestamps, + uint64_t* pMaxDeviation); #pragma mark Object lifecycle diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm index e7186fe1..f18470bc 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm @@ -78,6 +78,8 @@ static const uint32_t kAMDRadeonRX6700DeviceId = 0x73df; static const VkExtent2D kMetalSamplePositionGridSize = { 1, 1 }; static const VkExtent2D kMetalSamplePositionGridSizeNotSupported = { 0, 0 }; +static const uint32_t kMaxTimeDomains = 2; + #pragma clang diagnostic pop @@ -1120,6 +1122,22 @@ void MVKPhysicalDevice::getExternalSemaphoreProperties(const VkPhysicalDeviceExt pExternalSemaphoreProperties->pNext = next; } +VkResult MVKPhysicalDevice::getCalibrateableTimeDomains(uint32_t* pTimeDomainCount, VkTimeDomainEXT* pTimeDomains) { + if (!pTimeDomains) { + *pTimeDomainCount = kMaxTimeDomains; + return VK_SUCCESS; + } + // XXX CLOCK_MONOTONIC_RAW is mach_continuous_time(), but + // -[MTLDevice sampleTimestamps:gpuTimestamp:] returns the CPU + // timestamp in the mach_absolute_time() domain, which is CLOCK_UPTIME_RAW + // (cf. Libc/gen/clock_gettime.c). + static const VkTimeDomainEXT domains[] = { VK_TIME_DOMAIN_DEVICE_EXT, VK_TIME_DOMAIN_CLOCK_MONOTONIC_RAW_EXT }; + std::copy_n(domains, min(*pTimeDomainCount, kMaxTimeDomains), pTimeDomains); + if (*pTimeDomainCount < kMaxTimeDomains) { return VK_INCOMPLETE; } + *pTimeDomainCount = kMaxTimeDomains; + return VK_SUCCESS; +} + #pragma mark Surfaces @@ -3544,6 +3562,36 @@ VkResult MVKDevice::getMemoryHostPointerProperties(VkExternalMemoryHandleTypeFla return VK_SUCCESS; } +void MVKDevice::getCalibratedTimestamps(uint32_t timestampCount, + const VkCalibratedTimestampInfoEXT* pTimestampInfos, + uint64_t* pTimestamps, + uint64_t* pMaxDeviation) { + MTLTimestamp cpuStamp, gpuStamp; + uint64_t cpuStart, cpuEnd; + + cpuStart = mvkGetAbsoluteTime(); + [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(); + for (uint32_t tsIdx = 0; tsIdx < timestampCount; ++tsIdx) { + switch (pTimestampInfos[tsIdx].timeDomain) { + case VK_TIME_DOMAIN_DEVICE_EXT: + pTimestamps[tsIdx] = gpuStamp; + break; + // XXX Should be VK_TIME_DOMAIN_CLOCK_UPTIME_RAW_EXT + case VK_TIME_DOMAIN_CLOCK_MONOTONIC_RAW_EXT: + pTimestamps[tsIdx] = cpuStart; + break; + default: + continue; + } + } + *pMaxDeviation = cpuEnd - cpuStart; +} #pragma mark Object lifecycle diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm b/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm index 240e1cfe..5c6e6cb9 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm @@ -736,6 +736,8 @@ void MVKInstance::initProcAddrs() { ADD_DVC_EXT2_ENTRY_POINT(vkGetDeviceGroupSurfacePresentModesKHR, KHR_SWAPCHAIN, KHR_DEVICE_GROUP); ADD_DVC_EXT2_ENTRY_POINT(vkGetPhysicalDevicePresentRectanglesKHR, KHR_SWAPCHAIN, KHR_DEVICE_GROUP); ADD_DVC_EXT2_ENTRY_POINT(vkAcquireNextImage2KHR, KHR_SWAPCHAIN, KHR_DEVICE_GROUP); + ADD_DVC_EXT_ENTRY_POINT(vkGetCalibratedTimestampsEXT, EXT_CALIBRATED_TIMESTAMPS); + ADD_DVC_EXT_ENTRY_POINT(vkGetPhysicalDeviceCalibrateableTimeDomainsEXT, EXT_CALIBRATED_TIMESTAMPS); ADD_DVC_EXT_ENTRY_POINT(vkDebugMarkerSetObjectTagEXT, EXT_DEBUG_MARKER); ADD_DVC_EXT_ENTRY_POINT(vkDebugMarkerSetObjectNameEXT, EXT_DEBUG_MARKER); ADD_DVC_EXT_ENTRY_POINT(vkCmdDebugMarkerBeginEXT, EXT_DEBUG_MARKER); diff --git a/MoltenVK/MoltenVK/Layers/MVKExtensions.def b/MoltenVK/MoltenVK/Layers/MVKExtensions.def index 4fc20898..37461e62 100644 --- a/MoltenVK/MoltenVK/Layers/MVKExtensions.def +++ b/MoltenVK/MoltenVK/Layers/MVKExtensions.def @@ -95,6 +95,7 @@ MVK_EXTENSION(KHR_variable_pointers, KHR_VARIABLE_POINTERS, MVK_EXTENSION(KHR_vulkan_memory_model, KHR_VULKAN_MEMORY_MODEL, DEVICE, MVK_NA, MVK_NA) MVK_EXTENSION(EXT_4444_formats, EXT_4444_FORMATS, DEVICE, 11.0, 13.0) MVK_EXTENSION(EXT_buffer_device_address, EXT_BUFFER_DEVICE_ADDRESS, DEVICE, 13.0, 16.0) +MVK_EXTENSION(EXT_calibrated_timestamps, EXT_CALIBRATED_TIMESTAMPS, DEVICE, 10.15, 14.0) MVK_EXTENSION(EXT_debug_marker, EXT_DEBUG_MARKER, DEVICE, 10.11, 8.0) MVK_EXTENSION(EXT_debug_report, EXT_DEBUG_REPORT, INSTANCE, 10.11, 8.0) MVK_EXTENSION(EXT_debug_utils, EXT_DEBUG_UTILS, INSTANCE, 10.11, 8.0) diff --git a/MoltenVK/MoltenVK/Vulkan/vulkan.mm b/MoltenVK/MoltenVK/Vulkan/vulkan.mm index 39f8b02e..27c0166f 100644 --- a/MoltenVK/MoltenVK/Vulkan/vulkan.mm +++ b/MoltenVK/MoltenVK/Vulkan/vulkan.mm @@ -3116,6 +3116,34 @@ MVK_PUBLIC_VULKAN_CORE_ALIAS(vkWaitSemaphores, KHR); MVK_PUBLIC_VULKAN_CORE_ALIAS(vkGetBufferDeviceAddress, EXT); +#pragma mark - +#pragma mark VK_EXT_calibrated_timestamps extension + +MVK_PUBLIC_VULKAN_SYMBOL VkResult vkGetPhysicalDeviceCalibrateableTimeDomainsEXT( + VkPhysicalDevice physicalDevice, + uint32_t* pTimeDomainCount, + VkTimeDomainEXT* pTimeDomains) { + MVKTraceVulkanCallStart(); + MVKPhysicalDevice* mvkPD = MVKPhysicalDevice::getMVKPhysicalDevice(physicalDevice); + mvkPD->getCalibrateableTimeDomains(pTimeDomainCount, pTimeDomains); + MVKTraceVulkanCallEnd(); + return VK_SUCCESS; +} + +MVK_PUBLIC_VULKAN_SYMBOL VkResult vkGetCalibratedTimestampsEXT( + VkDevice device, + uint32_t timestampCount, + const VkCalibratedTimestampInfoEXT* pTimestampInfos, + uint64_t* pTimestamps, + uint64_t* pMaxDeviation) { + MVKTraceVulkanCallStart(); + MVKDevice* mvkDev = MVKDevice::getMVKDevice(device); + mvkDev->getCalibratedTimestamps(timestampCount, pTimestampInfos, pTimestamps, pMaxDeviation); + MVKTraceVulkanCallEnd(); + return VK_SUCCESS; +} + + #pragma mark - #pragma mark VK_EXT_debug_report extension