evdi_painter: vblank timing incorrect
The expected behavior from DRM_EVENT_FLIP_COMPLETE is that it fires once the new content is presented, and will naturally be throttled to happen at an interval no shorter than the configured display refresh rate.
Currently, evdi fires vblank immediately after the user-space driver (i.e., displaylink) called DRM_EVDI_GRABPIX to obtain a sampled buffer, meaning that the minimum time between commit and vblank is the time it takes evdi_painter to perform a memcpy. This in turn will generally result in the drm master immediately starting to render a new frame, thinking the display is fast enough to want it, which in turn leads to new update ready events, causing displaylink to request the new frame leading to yet another immediate vblank event...
The result is wasted CPU/GPU on useless frames that get thrown away anyway, and as this event also drives the user-space visible vblank signals (e.g., Wayland's frame callbacks), it also causes user-space applications to run far too fast, wasting resources.
This also seems to lead to tearing, with the displaylink driver (or hardware) not performing appropriate double-buffering.
On top of this, if multiple commits are issued, the previous vblank event will fire immediately.
This could be fixed a few ways:
- Have
evdiqueue the pageflip event on a timer set to the display refresh rate. New commits should be rejected withEBUSYuntil it fires. - Have
displaylinkthrottle calls toDRM_EVDI_GRABPIXaccording to the display refresh rate. This could also be extended to haveDRM_EVDI_GRABPIXbe a blocking call that first fires any past vblank events, and then blocks on a condition that is raised when a new commit completes that can be copied, such that a vblank event correctly portrays that the user-space driver is ready to accept the next frame.
Hi @kennylevinsen, Thank you for your comments.
Userspace displaylink driver is using double buffering and the reason it sends vblank right after memcpy in DRM_EVDI_GRABPIX is to allow compositor to prepare next frame when previous is in its internal render pipeline. Perhaps triggering vblank on timer rether than on grab would save some power. I agree this could be done better.
I was analyzing that behaviour recently with Gnome/mutter wayland implementation and evdi driver never gets new commit until vblank event was sent for the last one. Also it was sending commits in a expected pace of the set refresh rate.
Did you saw mentioned multiple commits issue in other than Gnome/mutter implementations? Can you say something more about the setup you see that problems?
Userspace displaylink driver is using double buffering and the reason it sends vblank right after memcpy in DRM_EVDI_GRABPIX is to allow compositor to prepare next frame when previous is in its internal render pipeline.
This is not how the flip event is meant to fire, and will cause different problems based on display server rendering strategy.
DRM_EVENT_FLIP_COMPLETED is used to:
- Throttle rendering so it happens no faster than the display can update content
- Time presentation to estimate the timing of the next presentation, synchronizing render timing as close to the calculated upcoming presentation as possible to minimize perceived latency
- Provide presentation timings required to correctly time audio and video frames in multimedia content
If hardware feedback is not present in the displaylink platform (even within the black box driver), two of these use-cases are out the window and a timer is the only real option.
When rendering full-screen content this fast, tearing artifacts occur. Given that evdi had synchronization issues reported in another thread, and given that the displaylink driver and its hardware is a black box, I'm not sure where the issue lies. Rendering this fast certainly isn't helping.
Did you saw mentioned multiple commits issue in other than Gnome/mutter implementations? Can you say something more about the setup you see that problems?
I tested both sway (I made wlroots work with displaylink some time back) and niri (where support for render-less display devices like displaylink is currently being prototyped) with weston-simple-egl as test client, powering a 4k@30Hz display using some bogus Dell DisplayLink dock I have laying around for test purposes.
Due to evdi firing vblank immediately after DRM_EVDI_GRABPIX, sway and niri were both driving their render loops at around 120fps instead of 30fps, and also telling weston-simple-egl to render that fast. At this speed, the display was visibly tearing in a somewhat unusual way, with wide rows of interchangeably old and new content - niri devs reproduced this with different test hardware.
I'd expect every display server with standard frame pacing to have this issue, and servers with smart frame pacing will do undefined things as they're trying to calculate the time between pageflip events and pick a point as close to the estimated next page flip event as possible to render.
I don't use mutter, maybe it's running its own synthetic frame pacing timer with some special handling of invalid frame timings?
I can confirm I can see it on Weston with westin-simple-egl. On mutter it is fine.