[PATCH v6 0/1] MR9859: Draft: winevulkan: Use correct time domain for calibrated timestamps
I am not sure how to address this fully, but the following patch should at least make it accurate before the machine suspends. -- v6: winevulkan: Implement monotonic to performance counter conversion. https://gitlab.winehq.org/wine/wine/-/merge_requests/9859
From: Etaash Mathamsetty <etaash.mathamsetty@gmail.com> --- dlls/winevulkan/vulkan.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index 0f49148ee15..ffbfb30264b 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -585,11 +585,7 @@ void wine_vkGetPhysicalDeviceExternalFencePropertiesKHR(VkPhysicalDevice client_ static inline VkTimeDomainEXT get_performance_counter_time_domain(void) { #if !defined(__APPLE__) && defined(HAVE_CLOCK_GETTIME) -# ifdef CLOCK_MONOTONIC_RAW - return VK_TIME_DOMAIN_CLOCK_MONOTONIC_RAW_EXT; -# else return VK_TIME_DOMAIN_CLOCK_MONOTONIC_EXT; -# endif #else FIXME("No mapping for VK_TIME_DOMAIN_QUERY_PERFORMANCE_COUNTER_EXT on this platform.\n"); return VK_TIME_DOMAIN_QUERY_PERFORMANCE_COUNTER_EXT; @@ -607,7 +603,16 @@ static VkTimeDomainEXT map_to_host_time_domain(VkTimeDomainEXT domain) static inline uint64_t convert_monotonic_timestamp(uint64_t value) { - return value / (NANOSECONDS_IN_A_SECOND / TICKSPERSEC); + ULONG64 offset = 0; +#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_BOOTTIME) && !defined(__APPLE__) + LARGE_INTEGER ticks; + struct timespec ts; + NtQueryPerformanceCounter(&ticks, NULL); + clock_gettime(CLOCK_MONOTONIC, &ts); + /* ticks spent during suspend */ + offset = ticks.QuadPart - ((ULONG64)ts.tv_sec * TICKSPERSEC + ts.tv_nsec / 100); +#endif + return value / (NANOSECONDS_IN_A_SECOND / TICKSPERSEC) + offset; } static inline uint64_t convert_timestamp(VkTimeDomainEXT host_domain, VkTimeDomainEXT target_domain, uint64_t value) @@ -616,7 +621,7 @@ static inline uint64_t convert_timestamp(VkTimeDomainEXT host_domain, VkTimeDoma return value; /* Convert between MONOTONIC time in ns -> QueryPerformanceCounter */ - if ((host_domain == VK_TIME_DOMAIN_CLOCK_MONOTONIC_RAW_EXT || host_domain == VK_TIME_DOMAIN_CLOCK_MONOTONIC_EXT) + if (host_domain == VK_TIME_DOMAIN_CLOCK_MONOTONIC_EXT && target_domain == VK_TIME_DOMAIN_QUERY_PERFORMANCE_COUNTER_EXT) return convert_monotonic_timestamp(value); @@ -663,7 +668,7 @@ static VkResult wine_vk_get_time_domains(struct vulkan_physical_device *physical VkTimeDomainEXT *time_domains, VkResult (*get_domains)(VkPhysicalDevice, uint32_t *, VkTimeDomainEXT *)) { - BOOL supports_device = FALSE, supports_monotonic = FALSE, supports_monotonic_raw = FALSE; + BOOL supports_device = FALSE, supports_monotonic = FALSE; const VkTimeDomainEXT performance_counter_domain = get_performance_counter_time_domain(); VkTimeDomainEXT *host_time_domains; uint32_t host_time_domain_count; @@ -693,8 +698,6 @@ static VkResult wine_vk_get_time_domains(struct vulkan_physical_device *physical supports_device = TRUE; else if (host_time_domains[i] == VK_TIME_DOMAIN_CLOCK_MONOTONIC_EXT) supports_monotonic = TRUE; - else if (host_time_domains[i] == VK_TIME_DOMAIN_CLOCK_MONOTONIC_RAW_EXT) - supports_monotonic_raw = TRUE; else FIXME("Unknown time domain %d\n", host_time_domains[i]); } @@ -704,9 +707,7 @@ static VkResult wine_vk_get_time_domains(struct vulkan_physical_device *physical out_time_domain_count = 0; /* Map our monotonic times -> QPC */ - if (supports_monotonic_raw && performance_counter_domain == VK_TIME_DOMAIN_CLOCK_MONOTONIC_RAW_EXT) - out_time_domains[out_time_domain_count++] = VK_TIME_DOMAIN_QUERY_PERFORMANCE_COUNTER_EXT; - else if (supports_monotonic && performance_counter_domain == VK_TIME_DOMAIN_CLOCK_MONOTONIC_EXT) + if (supports_monotonic && performance_counter_domain == VK_TIME_DOMAIN_CLOCK_MONOTONIC_EXT) out_time_domains[out_time_domain_count++] = VK_TIME_DOMAIN_QUERY_PERFORMANCE_COUNTER_EXT; else FIXME("VK_TIME_DOMAIN_QUERY_PERFORMANCE_COUNTER_EXT not supported on this platform.\n"); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9859
v2 implements a conversion by accounting for the amount of time the system has been suspended for. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9859#note_126533
How critical is the breakage here? Do we want https://gitlab.winehq.org/wine/wine/-/merge_requests/9859 for Wine 11? -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9859#note_126623
On Mon Jan 12 09:02:42 2026 +0000, Rémi Bernon wrote:
How critical is the breakage here? Do we want https://gitlab.winehq.org/wine/wine/-/merge_requests/9859 for Wine 11? I'm not sure, but thought it was worth raising attention about. Such accuracy issues likely matter for present timing implementation I wanted to work on. It also probably matters for calibrated timestamps but I wouldn't know where to find a test client
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/9859#note_126624
Marc-Aurel Zent (@mzent) commented about dlls/winevulkan/vulkan.c:
static inline uint64_t convert_monotonic_timestamp(uint64_t value) { - return value / (NANOSECONDS_IN_A_SECOND / TICKSPERSEC); + ULONG64 offset = 0;
With https://gitlab.winehq.org/wine/wine/-/merge_requests/9875 something like this should simplify the change a lot: ``` static inline uint64_t convert_monotonic_timestamp(uint64_t value) { KUSER_SHARED_DATA *user_shared_data = (void *)0x7ffe0000; return value / (NANOSECONDS_IN_A_SECOND / TICKSPERSEC) + user_shared_data->InterruptTimeBias; } ``` -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9859#note_126688
On Tue Jan 13 14:22:39 2026 +0000, Marc-Aurel Zent wrote:
With https://gitlab.winehq.org/wine/wine/-/merge_requests/9875 something like this should simplify the change a lot: ``` static inline uint64_t convert_monotonic_timestamp(uint64_t value) { KUSER_SHARED_DATA *user_shared_data = (void *)0x7ffe0000; return value / (NANOSECONDS_IN_A_SECOND / TICKSPERSEC) + user_shared_data->InterruptTimeBias; } ``` Reading the current timestamp at an arbitrary time doesn't look right. These extensions are supposed to return deterministic and correlated GPU / CPU timestamps with a specific maximum deviation. We have to rely on the host extension to do that right, or we will have to re-implement the full GPU / CPU time correlation ourselves and that doesn't seem possible to do in a reliable way.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/9859#note_126691
On Tue Jan 13 15:10:13 2026 +0000, Rémi Bernon wrote:
Reading the current timestamp at an arbitrary time doesn't look right. These extensions are supposed to return deterministic and correlated GPU / CPU timestamps with a specific maximum deviation. We have to rely on the host extension to do that right, or we will have to re-implement the full GPU / CPU time correlation ourselves and that doesn't seem possible to do in a reliable way. Correction, I misread and thought this was reading timestamp from USD when it's reading some bias, but still nevertheless, the bias doesn't seem very reliable and only updated when wineserver decides so.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/9859#note_126692
participants (4)
-
Etaash Mathamsetty -
Etaash Mathamsetty (@etaash.mathamsetty) -
Marc-Aurel Zent (@mzent) -
Rémi Bernon (@rbernon)