core: Add timestamps to window and device events#4562
core: Add timestamps to window and device events#4562chaynabors wants to merge 2 commits intorust-windowing:masterfrom
Conversation
|
Though the PR is effectively ready, it still needs additional testing. I'm also implementing my use case end to end behind the scenes to prove everything works as expected. I realize the diff is pretty sizable. It would be possible to split the breaking API changes from the implementation through stubs if desired, though I'd prefer to get the desktop implementations in all at once if possible. I can test on win32 and x11 but would need help testing the other implementations. I'm working on a scheduling IR + executor with sub millisecond latency requirements and I have a very strict budget. For the longest time I've simply given winit exclusive access to main and spun it as fast as possible to tag events with a timestamp, but I figured I'd give a shot to upstreaming my fork since I'm sure the Bevy folks would appreciate it among others. I had the option to pull in another dependency on x11 instead of adding the one Quartz function to ffi. I didn't check if it was a transient dep already. Alternatively, MacOS supports a libc call for timing, the name of which is eluding me right now. It's the same timer under the hood as was implemented, and still an unsafe call. None of these decisions seemed to have any meaningful tradeoffs so I stuck with the 5 line change in ffi. |
|
Just some other ideas I had: macOS provides better granularity timestamps but that requires entitlements. Windows could probably get better granularity timestamps with the addition of a dedicated thread but that feels wasteful. Improvements are incremental, what matters to me is if this API shape is sufficient for maintainers. |
Fixes #1194. Reimplements and expands on #4529 to cover all four major desktop backends.
Before reviewing: The API shape here adds real and synthesized timestamps to the events. Let me know if that isn't desired. It would be possible to make timestamps optional in cases where events might be real but that adds boilerplate without any significant gain? Open to interpretations. Possible to sacrifice some honesty for ergonomics and vice-versa.
ApplicationHandler::window_eventandApplicationHandler::device_eventtake a newtimestamp: Instantargument. The value is sourced from the OS where the platform exposes one, and fromInstant::now()at winit's receipt point otherwise. Using it in place ofInstant::now()inside the handler removes the polling-delay latency that applications pick up when their event loop is slow to poll, which matters for game input, audio scheduling, and similar latency-sensitive workloads.Instantis re-exported aswinit::Instantso cross-platform users don't need cfg-gated imports. winit substitutesweb_time::Instantonwasm32-unknown-unknown/wasm32-unknown-none, wherestd::time::Instant::now()still panics.Per backend:
NSEvent.timestampon themach_absolute_timebase, calibrated againstInstantat event-loop construction viaCACurrentMediaTimefrom QuartzCore. Events whose timestamps slightly predate the calibration sample project backward viachecked_subrather than falling back toInstant::now().xev.timelazily anchored on first observation. The anchor slides forward with each event so wrap-detection stays valid past the 49.7 dayu32wrap point and across multi-week application uptimes.timefield onwl_keyboard::key,wl_pointer::{motion, button, axis}, andwl_touch::{down, up, motion}, plus theutime_hi/utime_lopair onzwp_relative_pointer_v1. Tracked via two separate sliding anchors, oneu32ms and oneu64us, so the ms wrap doesn't mis-anchor the us clock.EventSinkgainspush_*_event_atalongside the existingInstant::now(), stamping helpers so call sites without a native timestamp stay concise.Instant::now()sampled insideWindowProcbefore queueing, within microseconds(?) of the OS dispatch.Future work:
Instant::now()at every dispatch site for now.AInputEvent_getEventTime,UIEvent.timestamp, andEvent.timeStampare follow-up work.QueryPerformanceCountercalibration againstMSG.timecould be added but adds a jitter floor in the order of 0-16ms? Needs more testing.changelogmodule if knowledge of this change could be valuable to users