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@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]);