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`.
This commit is contained in:
Chip Davis 2023-06-23 01:15:32 -07:00
parent 591eb6bdaa
commit 4e3ac69305
9 changed files with 97 additions and 3 deletions

View File

@ -84,6 +84,9 @@ double mvkGetTimestampPeriod();
*/ */
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. */ /** Ensures the block is executed on the main thread. */
void mvkDispatchToMainAndWait(dispatch_block_t block); void mvkDispatchToMainAndWait(dispatch_block_t block);

View File

@ -40,6 +40,7 @@ MVKOSVersion mvkOSVersion() {
static uint64_t _mvkTimestampBase; static uint64_t _mvkTimestampBase;
static double _mvkTimestampPeriod; static double _mvkTimestampPeriod;
static mach_timebase_info_data_t _mvkMachTimebase;
uint64_t mvkGetTimestamp() { return mach_absolute_time() - _mvkTimestampBase; } 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; return (double)(endTimestamp - startTimestamp) * _mvkTimestampPeriod / 1e6;
} }
uint64_t mvkGetAbsoluteTime() { return mach_continuous_time() * _mvkMachTimebase.numer / _mvkMachTimebase.denom; }
// Initialize timestamping capabilities on app startup. // Initialize timestamping capabilities on app startup.
//Called automatically when the framework is loaded and initialized. //Called automatically when the framework is loaded and initialized.
static bool _mvkTimestampsInitialized = false; static bool _mvkTimestampsInitialized = false;
@ -58,9 +61,8 @@ __attribute__((constructor)) static void MVKInitTimestamps() {
_mvkTimestampsInitialized = true; _mvkTimestampsInitialized = true;
_mvkTimestampBase = mach_absolute_time(); _mvkTimestampBase = mach_absolute_time();
mach_timebase_info_data_t timebase; mach_timebase_info(&_mvkMachTimebase);
mach_timebase_info(&timebase); _mvkTimestampPeriod = (double)_mvkMachTimebase.numer / (double)_mvkMachTimebase.denom;
_mvkTimestampPeriod = (double)timebase.numer / (double)timebase.denom;
} }
void mvkDispatchToMainAndWait(dispatch_block_t block) { void mvkDispatchToMainAndWait(dispatch_block_t block) {

View File

@ -352,6 +352,7 @@ In addition to core *Vulkan* functionality, **MoltenVK** also supports the foll
- `VK_KHR_variable_pointers` - `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_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_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_marker`
- `VK_EXT_debug_report` - `VK_EXT_debug_report`
- `VK_EXT_debug_utils` - `VK_EXT_debug_utils`

View File

@ -20,6 +20,7 @@ Released TBD
- Add support for extensions: - Add support for extensions:
- `VK_EXT_4444_formats` - `VK_EXT_4444_formats`
- `VK_EXT_calibrated_timestamps`
- `VK_EXT_shader_demote_to_helper_invocation` - `VK_EXT_shader_demote_to_helper_invocation`
- Ensure non-dispatch compute commands don't interfere with compute encoding state used by dispatch commands. - 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. - Support `VK_PRESENT_MODE_IMMEDIATE_KHR` if `VkPresentTimeGOOGLE::desiredPresentTime` is zero.

View File

@ -175,6 +175,9 @@ public:
void getExternalSemaphoreProperties(const VkPhysicalDeviceExternalSemaphoreInfo* pExternalSemaphoreInfo, void getExternalSemaphoreProperties(const VkPhysicalDeviceExternalSemaphoreInfo* pExternalSemaphoreInfo,
VkExternalSemaphoreProperties* pExternalSemaphoreProperties); VkExternalSemaphoreProperties* pExternalSemaphoreProperties);
/** Returns the supported time domains for calibration on this device. */
VkResult getCalibrateableTimeDomains(uint32_t* pTimeDomainCount, VkTimeDomainEXT* pTimeDomains);
#pragma mark Surfaces #pragma mark Surfaces
/** /**
@ -515,6 +518,11 @@ public:
const void* pHostPointer, const void* pHostPointer,
VkMemoryHostPointerPropertiesEXT* pMemHostPtrProps); 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 #pragma mark Object lifecycle

View File

@ -78,6 +78,8 @@ static const uint32_t kAMDRadeonRX6700DeviceId = 0x73df;
static const VkExtent2D kMetalSamplePositionGridSize = { 1, 1 }; static const VkExtent2D kMetalSamplePositionGridSize = { 1, 1 };
static const VkExtent2D kMetalSamplePositionGridSizeNotSupported = { 0, 0 }; static const VkExtent2D kMetalSamplePositionGridSizeNotSupported = { 0, 0 };
static const uint32_t kMaxTimeDomains = 2;
#pragma clang diagnostic pop #pragma clang diagnostic pop
@ -1120,6 +1122,22 @@ void MVKPhysicalDevice::getExternalSemaphoreProperties(const VkPhysicalDeviceExt
pExternalSemaphoreProperties->pNext = next; 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 #pragma mark Surfaces
@ -3544,6 +3562,36 @@ VkResult MVKDevice::getMemoryHostPointerProperties(VkExternalMemoryHandleTypeFla
return VK_SUCCESS; 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 #pragma mark Object lifecycle

View File

@ -736,6 +736,8 @@ void MVKInstance::initProcAddrs() {
ADD_DVC_EXT2_ENTRY_POINT(vkGetDeviceGroupSurfacePresentModesKHR, KHR_SWAPCHAIN, KHR_DEVICE_GROUP); 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(vkGetPhysicalDevicePresentRectanglesKHR, KHR_SWAPCHAIN, KHR_DEVICE_GROUP);
ADD_DVC_EXT2_ENTRY_POINT(vkAcquireNextImage2KHR, 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(vkDebugMarkerSetObjectTagEXT, EXT_DEBUG_MARKER);
ADD_DVC_EXT_ENTRY_POINT(vkDebugMarkerSetObjectNameEXT, EXT_DEBUG_MARKER); ADD_DVC_EXT_ENTRY_POINT(vkDebugMarkerSetObjectNameEXT, EXT_DEBUG_MARKER);
ADD_DVC_EXT_ENTRY_POINT(vkCmdDebugMarkerBeginEXT, EXT_DEBUG_MARKER); ADD_DVC_EXT_ENTRY_POINT(vkCmdDebugMarkerBeginEXT, EXT_DEBUG_MARKER);

View File

@ -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(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_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_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_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_report, EXT_DEBUG_REPORT, INSTANCE, 10.11, 8.0)
MVK_EXTENSION(EXT_debug_utils, EXT_DEBUG_UTILS, INSTANCE, 10.11, 8.0) MVK_EXTENSION(EXT_debug_utils, EXT_DEBUG_UTILS, INSTANCE, 10.11, 8.0)

View File

@ -3116,6 +3116,34 @@ MVK_PUBLIC_VULKAN_CORE_ALIAS(vkWaitSemaphores, KHR);
MVK_PUBLIC_VULKAN_CORE_ALIAS(vkGetBufferDeviceAddress, EXT); 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 -
#pragma mark VK_EXT_debug_report extension #pragma mark VK_EXT_debug_report extension