[PATCH vkd3d v2] vkd3d: Implement ID3D12CommandQueue_GetClockCalibration().

Conor McCarthy cmccarthy at codeweavers.com
Sun Jan 16 22:14:22 CST 2022


Extends vkd3d_instance_create_info with struct vkd3d_host_time_domain_info
to allow host ticks per second to be changed from the default 10000000.

Signed-off-by: Conor McCarthy <cmccarthy at codeweavers.com>
---
v2: Do not use a Vk MAX_ENUM value, rearrange a conditional to avoid
indentation, use a loop to select host time domain, and introduce an
optional instance parameter for host ticks per second.
---
 include/vkd3d.h            | 12 +++++++
 libs/vkd3d/command.c       | 59 +++++++++++++++++++++++++++++--
 libs/vkd3d/device.c        | 71 ++++++++++++++++++++++++++++++++++++++
 libs/vkd3d/vkd3d_private.h |  4 +++
 libs/vkd3d/vulkan_procs.h  |  6 ++++
 tests/d3d12.c              |  4 ---
 6 files changed, 150 insertions(+), 6 deletions(-)

diff --git a/include/vkd3d.h b/include/vkd3d.h
index 61ccf6c3..a2c70116 100644
--- a/include/vkd3d.h
+++ b/include/vkd3d.h
@@ -48,6 +48,9 @@ enum vkd3d_structure_type
     VKD3D_STRUCTURE_TYPE_OPTIONAL_DEVICE_EXTENSIONS_INFO,
     VKD3D_STRUCTURE_TYPE_APPLICATION_INFO,
 
+    /* 1.3 */
+    VKD3D_STRUCTURE_TYPE_HOST_TIME_DOMAIN_INFO,
+
     VKD3D_FORCE_32_BIT_ENUM(VKD3D_STRUCTURE_TYPE),
 };
 
@@ -109,6 +112,15 @@ struct vkd3d_application_info
     enum vkd3d_api_version api_version;
 };
 
+/* Extends vkd3d_instance_create_info. Available since 1.3. */
+struct vkd3d_host_time_domain_info
+{
+    enum vkd3d_structure_type type;
+    const void *next;
+
+    uint64_t ticks_per_second;
+};
+
 struct vkd3d_device_create_info
 {
     enum vkd3d_structure_type type;
diff --git a/libs/vkd3d/command.c b/libs/vkd3d/command.c
index 3e252aee..353a61cd 100644
--- a/libs/vkd3d/command.c
+++ b/libs/vkd3d/command.c
@@ -6238,13 +6238,68 @@ static HRESULT STDMETHODCALLTYPE d3d12_command_queue_GetTimestampFrequency(ID3D1
     return S_OK;
 }
 
+#define NANOSECONDS_IN_A_SECOND 1000000000
+
 static HRESULT STDMETHODCALLTYPE d3d12_command_queue_GetClockCalibration(ID3D12CommandQueue *iface,
         UINT64 *gpu_timestamp, UINT64 *cpu_timestamp)
 {
-    FIXME("iface %p, gpu_timestamp %p, cpu_timestamp %p stub!\n",
+    struct d3d12_command_queue *command_queue = impl_from_ID3D12CommandQueue(iface);
+    struct d3d12_device *device = command_queue->device;
+    const struct vkd3d_vk_device_procs *vk_procs;
+    VkCalibratedTimestampInfoEXT infos[2];
+    uint64_t timestamps[2];
+    uint64_t deviations[2];
+    VkResult vr;
+
+    TRACE("iface %p, gpu_timestamp %p, cpu_timestamp %p.\n",
             iface, gpu_timestamp, cpu_timestamp);
 
-    return E_NOTIMPL;
+    if (!command_queue->vkd3d_queue->timestamp_bits)
+    {
+        WARN("Timestamp queries not supported.\n");
+        return E_FAIL;
+    }
+
+    if (!gpu_timestamp || !cpu_timestamp)
+        return E_INVALIDARG;
+
+    if (!device->vk_info.EXT_calibrated_timestamps || device->vk_host_time_domain == -1)
+    {
+        WARN(!device->vk_info.EXT_calibrated_timestamps
+                ? "VK_EXT_calibrated_timestamps was not found. Setting timestamps to zero.\n"
+                : "Device and/or host time domain is not available. Setting timestamps to zero.\n");
+        *gpu_timestamp = 0;
+        *cpu_timestamp = 0;
+        return S_OK;
+    }
+
+    vk_procs = &device->vk_procs;
+
+    infos[0].sType = VK_STRUCTURE_TYPE_CALIBRATED_TIMESTAMP_INFO_EXT;
+    infos[0].pNext = NULL;
+    infos[0].timeDomain = VK_TIME_DOMAIN_DEVICE_EXT;
+    infos[1].sType = VK_STRUCTURE_TYPE_CALIBRATED_TIMESTAMP_INFO_EXT;
+    infos[1].pNext = NULL;
+    infos[1].timeDomain = device->vk_host_time_domain;
+
+    if ((vr = VK_CALL(vkGetCalibratedTimestampsEXT(command_queue->device->vk_device,
+            ARRAY_SIZE(infos), infos, timestamps, deviations))) < 0)
+    {
+        WARN("Failed to get calibrated timestamps, vr %d.\n", vr);
+        return E_FAIL;
+    }
+
+    if (infos[1].timeDomain == VK_TIME_DOMAIN_CLOCK_MONOTONIC_RAW_EXT
+            || infos[1].timeDomain == VK_TIME_DOMAIN_CLOCK_MONOTONIC_EXT)
+    {
+        /* Convert monotonic clock to match Wine's RtlQueryPerformanceFrequency(). */
+        timestamps[1] /= NANOSECONDS_IN_A_SECOND / device->vkd3d_instance->host_ticks_per_second;
+    }
+
+    *gpu_timestamp = timestamps[0];
+    *cpu_timestamp = timestamps[1];
+
+    return S_OK;
 }
 
 static D3D12_COMMAND_QUEUE_DESC * STDMETHODCALLTYPE d3d12_command_queue_GetDesc(ID3D12CommandQueue *iface,
diff --git a/libs/vkd3d/device.c b/libs/vkd3d/device.c
index 661ca1d9..4af26074 100644
--- a/libs/vkd3d/device.c
+++ b/libs/vkd3d/device.c
@@ -130,6 +130,7 @@ static const struct vkd3d_optional_extension_info optional_device_extensions[] =
     VK_EXTENSION(KHR_PUSH_DESCRIPTOR, KHR_push_descriptor),
     VK_EXTENSION(KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE, KHR_sampler_mirror_clamp_to_edge),
     /* EXT extensions */
+    VK_EXTENSION(EXT_CALIBRATED_TIMESTAMPS, EXT_calibrated_timestamps),
     VK_EXTENSION(EXT_CONDITIONAL_RENDERING, EXT_conditional_rendering),
     VK_EXTENSION(EXT_DEBUG_MARKER, EXT_debug_marker),
     VK_EXTENSION(EXT_DEPTH_CLIP_ENABLE, EXT_depth_clip_enable),
@@ -447,12 +448,16 @@ static uint64_t vkd3d_init_config_flags(void)
     return config_flags;
 }
 
+/* TICKSPERSEC from Wine */
+#define VKD3D_DEFAULT_HOST_TICKS_PER_SECOND 10000000
+
 static HRESULT vkd3d_instance_init(struct vkd3d_instance *instance,
         const struct vkd3d_instance_create_info *create_info)
 {
     const struct vkd3d_vk_global_procs *vk_global_procs = &instance->vk_global_procs;
     const struct vkd3d_optional_instance_extensions_info *optional_extensions;
     const struct vkd3d_application_info *vkd3d_application_info;
+    const struct vkd3d_host_time_domain_info *time_domain_info;
     bool *user_extension_supported = NULL;
     VkApplicationInfo application_info;
     VkInstanceCreateInfo instance_info;
@@ -578,6 +583,11 @@ static HRESULT vkd3d_instance_init(struct vkd3d_instance *instance,
         return hr;
     }
 
+    if ((time_domain_info = vkd3d_find_struct(create_info->next, HOST_TIME_DOMAIN_INFO)))
+        instance->host_ticks_per_second = time_domain_info->ticks_per_second;
+    else
+        instance->host_ticks_per_second = VKD3D_DEFAULT_HOST_TICKS_PER_SECOND;
+
     instance->vk_instance = vk_instance;
 
     TRACE("Created Vulkan instance %p.\n", vk_instance);
@@ -2349,6 +2359,66 @@ static void vkd3d_gpu_descriptor_allocator_cleanup(struct vkd3d_gpu_descriptor_a
     pthread_mutex_destroy(&allocator->mutex);
 }
 
+static bool have_vk_time_domain(VkTimeDomainEXT *domains, unsigned int count, VkTimeDomainEXT domain)
+{
+    unsigned int i;
+
+    for (i = 0; i < count; ++i)
+        if (domains[i] == domain)
+            return true;
+
+    return false;
+}
+
+static void vkd3d_time_domains_init(struct d3d12_device *device)
+{
+    static const VkTimeDomainEXT host_time_domains[] =
+    {
+        /* In order of preference */
+        VK_TIME_DOMAIN_CLOCK_MONOTONIC_RAW_EXT,
+        VK_TIME_DOMAIN_CLOCK_MONOTONIC_EXT,
+        VK_TIME_DOMAIN_QUERY_PERFORMANCE_COUNTER_EXT,
+
+    };
+    const struct vkd3d_vk_instance_procs *vk_procs = &device->vkd3d_instance->vk_procs;
+    VkTimeDomainEXT domains[8];
+    unsigned int i;
+    uint32_t count;
+    VkResult vr;
+
+    device->vk_host_time_domain = -1;
+
+    if (!device->vk_info.EXT_calibrated_timestamps)
+        return;
+
+    count = ARRAY_SIZE(domains);
+    if ((vr = VK_CALL(vkGetPhysicalDeviceCalibrateableTimeDomainsEXT(device->vk_physical_device,
+            &count, domains))) != VK_SUCCESS && vr != VK_INCOMPLETE)
+    {
+        WARN("Failed to get calibrated time domains, vr %d.\n", vr);
+        return;
+    }
+
+    if (vr == VK_INCOMPLETE)
+        FIXME("Calibrated time domain list is incomplete.\n");
+
+    if (!have_vk_time_domain(domains, count, VK_TIME_DOMAIN_DEVICE_EXT))
+    {
+        WARN("Device time domain not found. Calibrated timestamps will not be available.\n");
+        return;
+    }
+
+    for (i = 0; i < ARRAY_SIZE(host_time_domains); ++i)
+    {
+        if (!have_vk_time_domain(domains, count, host_time_domains[i]))
+            continue;
+        device->vk_host_time_domain = host_time_domains[i];
+        break;
+    }
+    if (device->vk_host_time_domain == -1)
+        WARN("Found no acceptable host time domain. Calibrated timestamps will not be available.\n");
+}
+
 /* ID3D12Device */
 static inline struct d3d12_device *impl_from_ID3D12Device(ID3D12Device *iface)
 {
@@ -3910,6 +3980,7 @@ static HRESULT d3d12_device_init(struct d3d12_device *device,
     vkd3d_render_pass_cache_init(&device->render_pass_cache);
     vkd3d_gpu_descriptor_allocator_init(&device->gpu_descriptor_allocator);
     vkd3d_gpu_va_allocator_init(&device->gpu_va_allocator);
+    vkd3d_time_domains_init(device);
 
     for (i = 0; i < ARRAY_SIZE(device->desc_mutex); ++i)
         pthread_mutex_init(&device->desc_mutex[i], NULL);
diff --git a/libs/vkd3d/vkd3d_private.h b/libs/vkd3d/vkd3d_private.h
index 047f4a29..3f5ea21c 100644
--- a/libs/vkd3d/vkd3d_private.h
+++ b/libs/vkd3d/vkd3d_private.h
@@ -108,6 +108,7 @@ struct vkd3d_vulkan_info
     bool KHR_push_descriptor;
     bool KHR_sampler_mirror_clamp_to_edge;
     /* EXT device extensions */
+    bool EXT_calibrated_timestamps;
     bool EXT_conditional_rendering;
     bool EXT_debug_marker;
     bool EXT_depth_clip_enable;
@@ -159,6 +160,8 @@ struct vkd3d_instance
 
     VkDebugReportCallbackEXT vk_debug_callback;
 
+    uint64_t host_ticks_per_second;
+
     LONG refcount;
 };
 
@@ -1195,6 +1198,7 @@ struct d3d12_device
     struct vkd3d_queue *copy_queue;
     uint32_t queue_family_indices[VKD3D_MAX_QUEUE_FAMILY_COUNT];
     unsigned int queue_family_count;
+    VkTimeDomainEXT vk_host_time_domain;
 
     struct vkd3d_instance *vkd3d_instance;
 
diff --git a/libs/vkd3d/vulkan_procs.h b/libs/vkd3d/vulkan_procs.h
index ec29eb45..60556735 100644
--- a/libs/vkd3d/vulkan_procs.h
+++ b/libs/vkd3d/vulkan_procs.h
@@ -55,6 +55,9 @@ VK_INSTANCE_EXT_PFN(vkGetPhysicalDeviceProperties2KHR)
 VK_INSTANCE_EXT_PFN(vkCreateDebugReportCallbackEXT)
 VK_INSTANCE_EXT_PFN(vkDestroyDebugReportCallbackEXT)
 
+/* VK_EXT_calibrated_timestamps */
+VK_INSTANCE_EXT_PFN(vkGetPhysicalDeviceCalibrateableTimeDomainsEXT)
+
 /* Device functions (obtained by vkGetDeviceProcAddr). */
 VK_DEVICE_PFN(vkDestroyDevice) /* Load vkDestroyDevice() first. */
 VK_DEVICE_PFN(vkAllocateCommandBuffers)
@@ -192,6 +195,9 @@ VK_DEVICE_EXT_PFN(vkGetDescriptorSetLayoutSupportKHR)
 /* VK_KHR_push_descriptor */
 VK_DEVICE_EXT_PFN(vkCmdPushDescriptorSetKHR)
 
+/* VK_EXT_calibrated_timestamps */
+VK_DEVICE_EXT_PFN(vkGetCalibratedTimestampsEXT)
+
 /* VK_EXT_conditional_rendering */
 VK_DEVICE_EXT_PFN(vkCmdBeginConditionalRenderingEXT)
 VK_DEVICE_EXT_PFN(vkCmdEndConditionalRenderingEXT)
diff --git a/tests/d3d12.c b/tests/d3d12.c
index 9d4fb7d4..a4488017 100644
--- a/tests/d3d12.c
+++ b/tests/d3d12.c
@@ -36023,19 +36023,15 @@ static void test_clock_calibration(void)
         return;
 
     hr = ID3D12CommandQueue_GetClockCalibration(context.queue, &gpu_times[0], &cpu_times[0]);
-    todo
     ok(hr == S_OK, "Failed to retrieve calibrated timestamps, hr %#x.\n", hr);
 
     vkd3d_sleep(100);
 
     hr = ID3D12CommandQueue_GetClockCalibration(context.queue, &gpu_times[1], &cpu_times[1]);
-    todo
     ok(hr == S_OK, "Failed to retrieve calibrated timestamps, hr %#x.\n", hr);
 
-    todo
     ok(gpu_times[1] > gpu_times[0], "Inconsistent GPU timestamps %"PRIu64" and %"PRIu64".\n",
             gpu_times[0], gpu_times[1]);
-    todo
     ok(cpu_times[1] > cpu_times[0], "Inconsistent CPU timestamps %"PRIu64" and %"PRIu64".\n",
             cpu_times[0], cpu_times[1]);
 
-- 
2.34.1




More information about the wine-devel mailing list