drm: implement hrtimer-based vblank for proper frame pacing by IraSkyx · Pull Request #562 · DisplayLink/evdi · GitHub
Skip to content

drm: implement hrtimer-based vblank for proper frame pacing - #562

Open
IraSkyx wants to merge 5 commits into
DisplayLink:mainfrom
IraSkyx:vblank-hrtimer
Open

IraSkyx wants to merge 5 commits into
DisplayLink:mainfrom
IraSkyx:vblank-hrtimer

Conversation

@IraSkyx

@IraSkyx IraSkyx commented Mar 21, 2026 •

Copy link
Copy Markdown

Fixes #512, partially addresses #511.

enable_vblank returns 1 so drm_atomic_helper_wait_for_vblanks skips the CRTC on every commit. No frame pacing, tearing on compositors that rely on it.
The painter-based event path (evdi_painter_set_vblank) doesn't help either: when commits outpace grabpix, the previous event gets flushed during the current atomic flush.

This adds an hrtimer driving drm_crtc_handle_vblank at the mode's refresh rate and switches to drm_crtc_arm_vblank_event in the atomic flush, same pattern as vkms. UPDATE_READY is unchanged.

Tested on Hyprland, 1080p@60Hz DisplayLink (Dell D6000), kernel 7.2. No more tearing, properly paced, clean hotplug and module unload.

@mkaspryk-synaptics

Copy link
Copy Markdown
Contributor

@IraSkyx

IraSkyx commented Oct 15, 2026

Copy link
Copy Markdown
Author

@mkaspryk-synaptics Thanks for your comment, all your points are addressed and I also fixed a few more things I ran into.

hrtimer_setup() replaced hrtimer_init() plus a separate .function
assignment in 6.13. Add an EVDI_HAVE_HRTIMER_SETUP probe so the vblank
timer setup can pick the right API at compile time.
The .enable_vblank and .disable_vblank hooks were guarded by
EVDI_HAVE_CRTC_ATOMIC_STATE_ARG, which is false on 7.2 and later where the
callbacks moved to struct drm_atomic_commit. On those kernels the hooks
were dropped and vblank could never be enabled. Guard them with
EVDI_HAVE_IOSYS_MAP or EVDI_HAVE_DMA_BUF_MAP (5.11+) instead, the same
condition evdi_drm_drv.c uses for its driver level vblank hooks. The hooks
still return 1 here so this change is behaviour neutral on its own.
evdi faked vblank by returning 1 from enable_vblank and never delivering
timing to userspace, so compositors had no frame pacing. Drive
drm_crtc_handle_vblank() from an hrtimer whose period comes from the mode
timings (vtotal * htotal * 1e6 / clock, 60 Hz default). Start the timer in
enable_vblank, cancel it in disable_vblank and retune the period on mode
change.

Use div_u64 for the period so the 64-bit divide links on 32-bit ARM, and
READ_ONCE/WRITE_ONCE around vblank_period which is written on mode change
and read from the timer callback. Detect hrtimer_setup() via the new probe
and fall back to hrtimer_init() plus .function before 6.13. Cancel the
timer in evdi_drm_device_release_cb too and guard evdi_crtc_destroy
because drmm cleanup frees evdi before drm_mode_config_cleanup runs.
The painter used to hold the page-flip event until userspace grabbed
pixels, then sent it immediately, so flips completed off any real vblank.
Arm the event with drm_crtc_arm_vblank_event() so it fires on the next
tick, falling back to an immediate send if drm_crtc_vblank_get() fails.
Take event_lock with spin_lock_irq because the vblank hrtimer callback
takes it in hard irq context. Drop the now unused painter vblank path.
Cover the period math (default 60 Hz, a 144 Hz mode and a clock of 0
keeping the previous period), that the timer arms and is scheduled within
one period after drm_crtc_vblank_on and that it stops and the vblank count
freezes after drm_crtc_vblank_off.
Comment thread module/evdi_modeset.c

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thank you for working on this.
I think that we need to sort out when to handle vblank when userspace client won't manage to grab pixels in time. This might happen on low performance machines which displays 4k content, or when the machine is busy.
Maybe it is enough to defer the vblank to next vblank_period.
On the other hand sometimes it is better to drop the frame when there is new one and show the new one earlier. Can you experiment with that?

We should test it how this works with Gnome/Mutter, Gnome/Xorg and weston.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@IraSkyx
Please also add regression coverage for the late-grabpix case: when a newer frame arrives before an older vblank event is consumed, confirm the behavior matches the intended policy — either coalesce to the next vblank or drop the stale event in favor of the newest frame. This is the key correctness boundary for the hrtimer-based pacing change

This branch has not been deployed

No deployments
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.

evdi_painter: vblank timing incorrect

3 participants