Skip to content

Conversation

@PhialsBasement
Copy link

Implements both xrConvertWin32PerformanceCounterToTimeKHR and xrConvertTimeToWin32PerformanceCounterKHR for converting between QueryPerformanceCounter ticks and XrTime nanoseconds.

Fixes VR controller tracking in Microsoft Flight Simulator 2020, where controllers would spawn at the HMD position due to failed time conversion. Debug logs showed these functions being called every frame for pose timing.

@PhialsBasement PhialsBasement changed the title wineopenxr: Implement Win32 performance counter time conversion functions wineopenxr: Implement Win32 performance counter time conversion functions for MSFS2020 VR Controller Tracking Dec 29, 2025
@PhialsBasement
Copy link
Author

image Finally managed to build it, works perfectly

@PhialsBasement
Copy link
Author

image image

Some more photos

@p-zilla
Copy link

p-zilla commented Dec 29, 2025

I am not a reviewer, but verified this implements the methods.

@PhialsBasement
Copy link
Author

@GloriousEggroll FYI since this completes the QPC↔XrTime conversion you partially implemented in your GE fork might be worth cherry-picking for GE-Proton

@Etaash-mathamsetty
Copy link

An application cannot assume that the system’s clock and the runtime’s clock will maintain a constant relationship across frames and should avoid storing such an offset, as this may cause time drift. Applications should instead always use time interop functions to convert a relevant time point across the system’s clock and the runtime’s clock using extensions, for example, XR_KHR_win32_convert_performance_counter_time or XR_KHR_convert_timespec_time.

I don't think you can assume that the XrTime and the performance counter are correlated, which means the current approach is probably not correct.

@Etaash-mathamsetty
Copy link

Etaash-mathamsetty commented Dec 30, 2025

Performance counters use CLOCK_MONOTONIC_RAW (in proton 10) and they will probably use CLOCK_BOOTTIME (in proton 11, unless they decide to revert that change) so the timespec obtained from the corresponding timespec extension most likely isn't very helpful either since neither of those are CLOCK_MONOTONIC

@PhialsBasement
Copy link
Author

PhialsBasement commented Dec 30, 2025

An application cannot assume that the system’s clock and the runtime’s clock will maintain a constant relationship across frames and should avoid storing such an offset, as this may cause time drift. Applications should instead always use time interop functions to convert a relevant time point across the system’s clock and the runtime’s clock using extensions, for example, XR_KHR_win32_convert_performance_counter_time or XR_KHR_convert_timespec_time.

I don't think you can assume that the XrTime and the performance counter are correlated, which means the current approach is probably not correct.

Fair point on the spec language. Though in practice I had a 45 min session with no noticeable tracking drift or issues.

Since the conversion happens fresh each frame (no stored offsets), clock drift can't really accumulate. Typical clock drift between sources is like 10-100 ppm (microseconds per second). Pose prediction only needs millisecond-level accuracy. Even if the clocks aren't perfectly synced, the per-frame error would be in microseconds - way below what would affect tracking.

That said, if the proper approach is to call through to the native runtime's timespec conversion, I can look at implementing that instead. Do you know which clock source Wine's QPC maps to in Proton 10, and whether SteamVR exposes XR_KHR_convert_timespec_time?

Edit: Saw your second comment. So if QPC uses CLOCK_MONOTONIC_RAW/BOOTTIME and the timespec extension expects something else entirely, then even the passthrough approach wouldn't solve the mismatch. What would the correct implementation look like in that case?

@imol-ai
Copy link

imol-ai commented Dec 31, 2025

Using Quest 3S, with ALVR 20.13.0 and SteamVR beta 2.14.5 on NixOS 25.11, and this as proton for MSFS 2020 (build 19260129), my controllers froze up after about 15 minutes. I think I was turning my head around a lot when that happened, no idea if that correlates at all. Triggers kept working after.

@PhialsBasement
Copy link
Author

PhialsBasement commented Dec 31, 2025

Using Quest 3S, with ALVR 20.13.0 and SteamVR beta 2.14.5 on NixOS 25.11, and this as proton for MSFS 2020 (build 19260129), my controllers froze up after about 15 minutes. I think I was turning my head around a lot when that happened, no idea if that correlates at all. Triggers kept working after.

Can you post me the proton log so i can see what went wrong please? Currently from my testing this doesn't happen with Steam Link + Quest 3S on Garuda with my build

@imol-ai
Copy link

imol-ai commented Dec 31, 2025

Using Quest 3S, with ALVR 20.13.0 and SteamVR beta 2.14.5 on NixOS 25.11, and this as proton for MSFS 2020 (build 19260129), my controllers froze up after about 15 minutes. I think I was turning my head around a lot when that happened, no idea if that correlates at all. Triggers kept working after.

Can you post me the proton log so i can see what went wrong please? Currently from my testing this doesn't happen with Steam Link + Quest 3S on Garuda with my build

Sure, I'll try to reproduce it tomorrow and paste logs.

@PhialsBasement
Copy link
Author

Using Quest 3S, with ALVR 20.13.0 and SteamVR beta 2.14.5 on NixOS 25.11, and this as proton for MSFS 2020 (build 19260129), my controllers froze up after about 15 minutes. I think I was turning my head around a lot when that happened, no idea if that correlates at all. Triggers kept working after.

Can you post me the proton log so i can see what went wrong please? Currently from my testing this doesn't happen with Steam Link + Quest 3S on Garuda with my build

Sure, I'll try to reproduce it tomorrow and paste logs.

Thanks!

@PhialsBasement
Copy link
Author

Using Quest 3S, with ALVR 20.13.0 and SteamVR beta 2.14.5 on NixOS 25.11, and this as proton for MSFS 2020 (build 19260129), my controllers froze up after about 15 minutes. I think I was turning my head around a lot when that happened, no idea if that correlates at all. Triggers kept working after.

I tried really hard to replicate your error but after an hour of flying my controllers are still working fine, maybe this was an issue with ALVR?

@xytovl
Copy link

xytovl commented Dec 31, 2025

An OpenXR runtime is free to use whatever it fits as the origin of time. You should use XR_KHR_convert_timespec_time to implement the equivalent Windows version.

@PhialsBasement
Copy link
Author

An OpenXR runtime is free to use whatever it fits as the origin of time. You should use XR_KHR_convert_timespec_time to implement the equivalent Windows version.

@xytovl Looked into using XR_KHR_convert_timespec_time but the thunk infrastructure to call those functions from the Wine side doesn't exist. clock_gettime/CLOCK_MONOTONIC aren't available in the Windows DLL, would need to add Unix-side thunks for both timespec conversion functions.

Given the current implementation works in practice (tested 45+ min sessions and the actual fix itself does what it was supposed to), is that complexity worth it? Happy to add the thunks if needed but it's a significantly larger change.

@xytovl
Copy link

xytovl commented Dec 31, 2025

It's up to proton maintainers, the pull request works in practice but is not technically correct. I don't think Monado has any plan to change its time origin, and probably neither will SteamVR, so it should keep working.

Edit:
I may be wrong about the Win32 API, if performance counters are not allowed for absolute measurements, then your implementation should be just fine.

@PhialsBasement
Copy link
Author

It's up to proton maintainers, the pull request works in practice but is not technically correct. I don't think Monado has any plan to change its time origin, and probably neither will SteamVR, so it should keep working.

Edit: I may be wrong about the Win32 API, if performance counters are not allowed for absolute measurements, then your implementation should be just fine.

Yeah, QPC is explicitly not an absolute timestamp, it's relative to boot with no defined epoch. The spec just needs consistent round-trip conversion within a session, which this provides. Happy to add the full thunk infrastructure if maintainers want it, but given the complexity vs. practical benefit, seemed like overkill. Will see what they say.

@imol-ai
Copy link

imol-ai commented Dec 31, 2025

steam-1250410.log
Well, now the controller tracking was broken straight on startup. I looked through the logs, it wasn't really clear to me what the problem was, maybe you'll be able to find something.

@PhialsBasement
Copy link
Author

PhialsBasement commented Dec 31, 2025

steam-1250410.log Well, now the controller tracking was broken straight on startup. I looked through the logs, it wasn't really clear to me what the problem was, maybe you'll be able to find something.

@imol-ai can you add to the launch commands WINEDEBUG=+openxr as this will give us a deeper look at whats going on in the oxr module

@imol-ai
Copy link

imol-ai commented Dec 31, 2025

steam-1250410.log Well, now the controller tracking was broken straight on startup. I looked through the logs, it wasn't really clear to me what the problem was, maybe you'll be able to find something.

@imol-ai can you add to the launch commands WINEDEBUG=+openxr as this will give us a deeper look at whats going on in the oxr module

log.tar.gz
Here it is. Sorry, had to compress it.

@imol-ai
Copy link

imol-ai commented Dec 31, 2025

Sorry, @PhialsBasement, I was dumb, and I switched the game into VR mode, right on the loading screen. Now, I am switching it into VR mode only when it reaches the main menu, and now the controllers work again properly. I will reproduce the ~15 minute freeze later, with logs, please disregard my earlier dumb logs :)

@Etaash-mathamsetty
Copy link

It's up to proton maintainers, the pull request works in practice but is not technically correct. I don't think Monado has any plan to change its time origin, and probably neither will SteamVR, so it should keep working.

Edit:
I may be wrong about the Win32 API, if performance counters are not allowed for absolute measurements, then your implementation should be just fine.

The implementation is not just fine, XrTime can be obtained through a different API that is simply forwarded to the application. If the QPC to XrTime conversion is wrong and the application uses both XrTimes for different things then you will run into issues

@PhialsBasement
Copy link
Author

PhialsBasement commented Dec 31, 2025

Replying to #9347 (comment)

The XrTime values from the runtime (xrLocateViews, xrWaitFrame, etc.) and the converted QPC times are in the same domain. The conversion maps QPC ticks to the runtime's XrTime epoch. If both came from different epochs the entire extension would be useless since apps need to correlate their own timestamps with runtime timestamps.

@PhialsBasement
Copy link
Author

Sorry, @PhialsBasement, I was dumb, and I switched the game into VR mode, right on the loading screen. Now, I am switching it into VR mode only when it reaches the main menu, and now the controllers work again properly. I will reproduce the ~15 minute freeze later, with logs, please disregard my earlier dumb logs :)

Glad it wasn't anything with the build, let me know if you have encountered the 15min error or not.

@Etaash-mathamsetty
Copy link

Etaash-mathamsetty commented Dec 31, 2025

Replying to #9347 (comment)

I don't understand what you mean by this so I will just give an example:
QPC is 0 at the system boot time, but XrTime isn't guaranteed to be 0 at the system boot time. Therefore, there is no way that simply dividing will give you the correct result.

A single runtime must use the same epoch for all simultaneous applications. Time must be represented the same regardless of multiple processors or threads present in the system.

The period precision of time reported by the runtime is runtime-dependent, and may change. One nanosecond is the finest possible period precision. A runtime may, for example, report time progression with only microsecond-level granularity.

Time must not be assumed to correspond to a system clock time.

There is no guarantee that the epochs are the same. In fact they are encouraged to pick an epoch that is not the system clock epoch:

Runtimes should choose an epoch that minimizes the chance of overflow.

I gave it some thought and I think I found a slightly better implementation; here is some rough code I came up with

// implement xrTime -> QPC
NtQueryPerformanceCounter( &count, &freq ); // (you can run this from unix side)
clock_gettime(CLOCK_MONOTONIC, &cur_timespec); // count and cur_timespec should reference roughly the same time
xrConvertTimeToTimespecTimeKHR(instance, given_xrtime, &given_timespec);
LONG64 delta = get_timespec_delta_ns(&cur_timespec, &given_timespec) / (NSECPERSEC / freq); // cur - given
// since QPC also uses a monotonic clock (either monotonic, bootime, monotonic_raw) we can estimate the correct QPC
out->QuadPart = count.QuadPart - delta;

@PhialsBasement
Copy link
Author

PhialsBasement commented Dec 31, 2025

Replying to #9347 (comment)

You're right about the epoch assumption. In practice SteamVR and Monado both use boot-relative time so it works, but it's not guaranteed. Happy to implement the proper offset-based approach if maintainers want it, but that's a significantly larger change touching multiple files with Unix thunks that would require wayyyy larger sign off than this which is unlikely to change unless SteamVR or Monado commit to making a change that would break a hell of a lot more things than just this.

@PhialsBasement
Copy link
Author

PhialsBasement commented Dec 31, 2025

I gave it some thought and I think I found a slightly better implementation; here is some rough code I came up with

// implement xrTime -> QPC
NtQueryPerformanceCounter( &count, &freq ); // (you can run this from unix side)
clock_gettime(CLOCK_MONOTONIC, &cur_timespec); // count and cur_timespec should reference roughly the same time
xrConvertTimeToTimespecTimeKHR(instance, given_xrtime, &given_timespec);
LONG64 delta = get_timespec_delta_ns(&cur_timespec, &given_timespec) / (NSECPERSEC / freq); // cur - given
// since QPC also uses a monotonic clock (either monotonic, bootime, monotonic_raw) we can estimate the correct QPC
out->QuadPart = count.QuadPart - delta;

I actually really like this idea, I was already speculating on how I'd do it, but this is way better, so if it does end up needing to be done ill take inspiration from this

@imol-ai
Copy link

imol-ai commented Jan 1, 2026

Replying to #9347 (comment)

Yep, happened again multiple times. Log:
log.tar.gz
In this instance, the controller positions froze up after 16 minutes of the game being open. I closed the game ~30 sec after the freeze happened. There is no common 'cause' for the freeze, it happened in the menu aswell as while in flight.

@PhialsBasement
Copy link
Author

PhialsBasement commented Jan 2, 2026

Replying to #9347 (comment)

i notice at some point, the func passes 0xffffff33b52018a4(negative 877.4sec) to the waitframe, i am assuming theres an overflow, i need to build a more overflow resistant formula.

@PhialsBasement
Copy link
Author

PhialsBasement commented Jan 2, 2026

Replying to #9347 (comment)

@imol-ai i've now made a more overflow resistant formula https://github.com/PhialsBasement/Proton/releases/tag/msfs-fix-oflowres

@Etaash-mathamsetty
Copy link

Etaash-mathamsetty commented Jan 2, 2026

Replying to #9347 (comment)

fwiw I really doubt there's overflow XrTime would require 250+ years to overflow. QPC would be even longer

@PhialsBasement
Copy link
Author

PhialsBasement commented Jan 2, 2026

Replying to #9347 (comment)

You're right that XrTime itself won't overflow for 250+ years, but the overflow is in the intermediate calculation, not the stored value.

The old formula was
performanceCounter->QuadPart = (time * freq.QuadPart) / 1000000000LL;

At 10 MHz frequency, when predictedDisplayTime reaches ~922 billion ns (~15.4 minutes) which when that happens:
time * freq = 922,000,000,000 × 10,000,000 = 9.22 × 10¹⁸
and INT64_MAX = 9.22 × 10¹⁸

The multiplication overflows before the division happens, wrapping to negative. The fix splits the calculation to avoid the intermediate overflow:

secs = time / 1000000000LL;
remainder = time % 1000000000LL;
performanceCounter->QuadPart = secs * freq.QuadPart + (remainder * freq.QuadPart) / 1000000000LL;

@imol-ai
Copy link

imol-ai commented Jan 2, 2026

@PhialsBasement I've tested it extensively, it seems that your fix holds. Thank you for all of your work!

@Etaash-mathamsetty
Copy link

Etaash-mathamsetty commented Jan 2, 2026

Replying to #9347 (comment)

That's not the old formula, I asked you to change it to performanceCounter->QuadPart = time / (1000000000LL / freq.QuadPart); exactly for this reason and that's what you had committed here

@PhialsBasement
Copy link
Author

PhialsBasement commented Jan 2, 2026

Replying to #9347 (comment)

That's not the old formula, I asked you to change it to performanceCounter->QuadPart = time / (1000000000LL / freq.QuadPart); exactly for this reason and that's what you had committed here

My bad, I described the wrong formula. that was the original 0a32156 formula, not 72ae486.

Interestingly enough, @imol-ai was hitting the 15-min freeze on 72ae486, and b9b2eaa fixed it. So 72ae486 still had an issue, whether it's the freq > 1e9 edge case causing 1e9/freq = 0, precision loss from integer division, or something else we haven't pinpointed yet.

The split formula is more robust regardless, handles any frequency, preserves precision, and most importantly, it actually fixed the issue for everyone now.

@Etaash-mathamsetty
Copy link

Etaash-mathamsetty commented Jan 3, 2026

Replying to #9347 (comment)

frequency is always less than NSECPERSEC fwiw, I would like to know why the old approach didn't work. It doesn't seem like multiplying by 100 is going to cause any overflow any time soon.

@PhialsBasement
Copy link
Author

Replying to #9347 (comment)

frequency is always less than NSECPERSEC fwiw, I would like to know why the old approach didn't work. It doesn't seem like multiplying by 100 is going to cause any overflow any time soon.

Could be precision loss from the integer truncation in 1e9/freq, could be the other direction's formula, or something environment-specific with ALVR/NixOS. Didn't see anything as such in the logs to pinpoint it further.

@gofman
Copy link

gofman commented Jan 6, 2026

Hi! Thanks for the patches.

I am a Proton developer (particularly dealing with vrclient / wineopenxr in Proton) and had a look at the patch.

To do it right, I think this should be based on xrConvertTimespecTimeToTimeKHR present on Linux (instead of assuming that QPC timeline matches XrTime without any offset). You can look at Wine's dll/winevulkan/vulkan.c:convert_monotonic_timestamp() to see how we currently convert between that timespec (monotonic) time and QPC time.

Then, currently there is a hack which makes XR_KHR_win32_convert_performance_counter_time / xrConvertWin32PerformanceCounterToTimeKHR always available regardless of whether XR_KHR_convert_timespec_time is available. I guess we might want to still keep the hack, but before calling xrConvertTimespecTimeToTimeKHR and reverse we'd need to check that the native function is available (and if not, fallback to current behaviour returning error).

If updating the patches, could you please not fixup with new commits on top but instead edit the commits / squash the changes (while functionally different parts may go into different patches if possible, in a way that intermediate commits do not introduce non-working code). It is easier to look at that this way and we keep linear history without merge commits and only merge the final ones.

Would you please let me know if you are planning to continue working on this soon to make it right? If not, I might come to do it myself at some moment, or otherwise wait and review your new version.

@PhialsBasement
Copy link
Author

PhialsBasement commented Jan 6, 2026

Replying to #9347 (comment)

Thanks for the feedback! Yes, I'll continue working on this. Will implement it properly using xrConvertTimespecTimeToTimeKHR and reference convert_monotonic_timestamp() for the timespec↔QPC conversion. Will squash into clean commits when ready.

@Etaash-mathamsetty
Copy link

Etaash-mathamsetty commented Jan 6, 2026

Replying to #9347 (comment)

I just looked at convert_monotonic_timestamp, while it works for the case of calibrated timestamps (I failed to notice there's no offset with the clock_boottime path lol). it doesn't work for us (without modification), since openxr only uses the monotonic clock. Additionally, the QPC tick value we are getting in upstream wine is wrong right now, and I don't really know how to fix it (upstream now uses clock boottime, but winevulkan assumes monotonic raw is used) since boottime clock doesn't exist in VK_EXT_calibrated_timestamps. I'll open a draft PR to get some help on this, I think could be rather problematic if it doesn't get addressed.

@gofman
Copy link

gofman commented Jan 6, 2026

Yes, the difference between CLOCK_MONOTONIC, CLOCK_MONOTONIC_RAW and CLOCK_BOOTTIME is important but if the game works with the simple implementation, ignoring this difference, I'd suggest to start with such and leave a more complicated decision for later if it will be needed (maybe following the solution to be introduced for Vulkan clock adjustment).

I'd only add that even this implementation should go not to loader.c but to Unix side openxr.c at once (first adding the functions being implemented to FUNCTION_OVERRIDES in make_openxr, similar to other overridden functions like, e. g., xrGetVulkanGraphicsDeviceKHR).

@PhialsBasement PhialsBasement force-pushed the proton_10.0 branch 2 times, most recently from 0310155 to f4e4c9b Compare January 7, 2026 00:02
@PhialsBasement
Copy link
Author

@gofman Pushed the implementation to unix side with FUNCTION_OVERRIDES as requested. Uses xrConvertTimespecTimeToTimeKHR with fallback to simple conversion if native functions unavailable, built and tested it and it works as expected. Ready for review.

@PhialsBasement
Copy link
Author

Small correction: just fixed to actually use FUNCTION_OVERRIDES instead of MANUAL_LOADER_THUNKS.

Copy link

@gofman gofman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Eh, I didn't finish reviewing it when the patch got updated. So I guess I will post these comments and then look once again in the updated version which will hopefully become shorter.

ALL_XR_INSTANCE_FUNCS()
#undef USE_XR_FUNC

/* Load timespec conversion functions (not in dispatch table since XR_USE_TIMESPEC is Linux-only).
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can remove this part of comment, while leaving the second line.

const char *win32_ext, *linux_ext;
BOOL remove_original;
BOOL force_enable;
BOOL *available; /* Set to TRUE if linux extension is actually supported */
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This added field is probably not needed?

};

/* Track whether XR_KHR_convert_timespec_time is available */
static BOOL g_timespec_time_available = FALSE;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see this variable used (while it is assigned), that is probably a leftover superseded by function pointer check and should be removed?

g_timespec_time_available = TRUE;
TRACE("XR_KHR_convert_timespec_time is available.\n");
}

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above, this added block is probably not needed.

secs = performanceCounter->QuadPart / TICKSPERSEC;
remainder = performanceCounter->QuadPart % TICKSPERSEC;
ts.tv_sec = secs;
ts.tv_nsec = (remainder * NANOSECONDS_IN_A_SECOND) / TICKSPERSEC;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably "remainder * (NANOSECONDS_IN_A_SECOND / TICKSPERSEC)" would be more obvious (avoiding a possibility for overflow, while I know this is a remainder and functionally would be probably fine either way). We can use the fact that NANOSECONDS_IN_A_SECOND is divisible into TICKSPERSEC, same as in winevulkan.

/* Convert timespec to QPC ticks (Wine QPC is 10MHz = 100ns ticks).
* tv_nsec is always < 1e9, so tv_nsec * TICKSPERSEC won't overflow. */
performanceCounter->QuadPart = ts.tv_sec * TICKSPERSEC +
(ts.tv_nsec * TICKSPERSEC) / NANOSECONDS_IN_A_SECOND;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, in this case it is probably clearer to divide constants before multiplying.


/* Convert QPC ticks to timespec (Wine QPC is 10MHz = 100ns ticks).
* Split calculation to avoid overflow while maintaining precision. */
secs = performanceCounter->QuadPart / TICKSPERSEC;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can remove extra unneeded variables 'secs, remainder' and assign directly to the structure fields.

@PhialsBasement
Copy link
Author

Eh, I didn't finish reviewing it when the patch got updated. So I guess I will post these comments and then look once again in the updated version which will hopefully become shorter.

Will clean these up. Let me know if there's anything else once you finish reviewing.

… native timespec conversion

Implement xrConvertWin32PerformanceCounterToTimeKHR and
xrConvertTimeToWin32PerformanceCounterKHR by calling the native Linux
xrConvertTimespecTimeToTimeKHR/xrConvertTimeToTimespecTimeKHR functions.

This properly handles any offset between CLOCK_MONOTONIC and XrTime
rather than assuming QPC directly maps to XrTime.

Wine's QPC is based on CLOCK_MONOTONIC with 100ns (10MHz) ticks.
The conversion uses overflow-safe arithmetic for long-running systems.

Returns XR_ERROR_FUNCTION_UNSUPPORTED if the native timespec conversion
extension is not available.
@Etaash-mathamsetty
Copy link

Etaash-mathamsetty commented Jan 7, 2026

Looks good to me now, but have to wait for gofman's review ofc

Plagman pushed a commit that referenced this pull request Jan 7, 2026
@gofman
Copy link

gofman commented Jan 7, 2026

Thanks!

I've pushed the patch to Proton Experimental with the following additional cleanups:

  • rebased on top of Proton Experimental;
  • removed more comments (we tend to avoid too many comments);
  • removed typedefs for functions (defining those at once instead);
  • removed 'LL' suffix from constants (we avoid those) and added a cast to (ULONGLONG) in one place instead;
  • edited commit message.

I wasn't checking if that works with MSFS.

The commit is currently available in Proton Experimental bleeding-edge branch (https://github.com/ValveSoftware/Proton/wiki/Proton-Versions#proton-bleeding-edge), the source is here: https://github.com/ValveSoftware/Proton/commits/experimental-bleeding-edge-10.0-290998-20260107-peaca45-wa21b86f-df76b0e-vc60d88/.

Now I think we can address the clock mismatch as a separate patch. I think we can call NtQueryPerformanceCounter( &counter, NULL), and then clock_gettime( CLOCK_MONOTONIC, &ts ) and convert it to QPC units with 'ts.tv_sec * (ULONGLONG)TICKSPERSEC + ts.tv_nsec / 100'. Then, add or subtract the difference in corresponding functions. That has to be done in each xrConvert... call as the difference may change any time and thus we can't precompute it. I think we can ignore an inaccuracy introduced by the two calls performed at a slightly different time, those calls are very quick and the inaccuracy should be neglectable in this case. Calling NtQueryPerformanceCounter instead of clock_gettime( ) will spare us from knowledge which Unix side timer is backing NtQueryPerformanceCounter (and that will change in Proton 11 indeed, and in theory may change once again).

If you want to do that please ping me once you open a PR with that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants