One of the most heinous visual artifacts modern displays are capable of is tearing, on the desktop, there are two major potential sources of this:
- A compositor redraw during screen refresh – this leads to global artifacts such as application windows breaking up while being repositioned.
- Applications redrawing while the compositor is reading their content – the contents of a single window will be displayed in an indeterminate state.
Under X, application redraws are tricky to do without tearing because content can be updated at any chosen time with no clear feedback as to when the compositor will read it. EFL uses some clever tricks to this end (check out the state of the art X redraw timing for yourself), but it’s difficult to get right in all cases. For a lot of people this just works, or they’re not sensitive to the issue when it doesn’t.
Wayland Does Application Redraws Better
For the rest of us, Wayland has a different approach; prepare the buffer, attach it to a surface, and commit it for display. If you want the compositor to help with timing, you can request a frame callback before committing the surface; the compositor will display the frame on the next vertical retrace and will send a callback event at some point in the future. This callback informs the program that if it submits a buffer now, it has a reasonable chance to hit the upcoming vertical retrace.
Pro-tip: You can request a frame callback without submitting a buffer as well, and the compositor will still send a callback (which may be immediate) when it thinks you should commit a buffer for display.
The client shouldn’t expect the frame callback to be generated when the client is obscured or otherwise invisible. That is, the frame callback only tells you to draw when it’s useful for you to draw. This means the frame callback is not useful as a general purpose timing mechanism, it’s only for display.
Once the buffer is committed to the compositor, the compositor owns it. The Wayland compositor will send the buffer back when it’s done with it via a release event; until this point you’re simply not allowed to draw into it again. The result is undefined, and can cause your program to terminate as punishment for being poorly written.
Because of this commit/release paradigm, it’s very difficult to cause tearing artifacts under Wayland (it requires out of spec behavior that can lead to application termination).
Some people (notably gamers) will complain that synchronizing display with monitor refresh like this introduces input lag, and while I’m not going to wander into that firefight, I’d be remiss if I didn’t mention there’s no obligation to use frame callbacks for timing. If your goal is to render your frames as temporally proximal as possible to the screen retrace, then standard triple buffering is easily accomplished without using frame callbacks.
The client can render and commit frames as quickly as the CPU/GPU allows, and the compositor will release many of them undisplayed as soon as they’re replaced with a newer frame. Only the most recently-submitted frame at the time the compositor redraws the screen will be used. When an application doesn’t need twitchy response times though, using frame callbacks results in smooth animation with no wasted rendering.
Bringing This Improvement to EFL
New for the upcoming EFL 1.21 release (TBA soon, we promise) EFL applications now (finally!) drive animations directly from these frame callbacks. This has been a long time coming as it required major changes to our core timing code. Previously, we used a separate timer source that ran continuously in a thread, sending frame times through a pipe to the main thread. These “ticks” were then gated by a clever hack that prevented a redraw from occurring between buffer submission time and frame callback time. Prior to this, we simply triple buffered and threw away a few frames.
The most obvious immediate benefit of doing this properly has been that when the compositor blanks the screen, many clients stop consuming CPU cycles entirely. This is a departure from our previous timing implementation which burned many CPU cycles sending timestamps nothing cared about. It’s also a radical change from the behavior under X where animations continue despite the black screen, while X, enlightenment, and the client all continue to work needlessly.
So, here we have yet another case where Wayland does less than X.