Asynchronous frame updates

Pekka Paalanen ppaalanen at
Mon Mar 2 12:47:30 UTC 2020

On Mon, 2 Mar 2020 13:10:45 +0100
Guillermo Rodriguez < at> wrote:

> Hi all,
> This is a followup question to an earlier thread on this ML ("Thread
> safety when rendering on a separate thread")
> The story was like this:
> > > Hi all,
> > >
> > > I have a Wayland client where the main loop runs on a thread and
> > > rendering is done in a separate thread. The main loop simply uses
> > > wl_display_dispatch:
> > >
> > > while (wl_display_dispatch(globals.display) != -1) {
> > >     [...process user input...]
> > > }
> > >
> > > This is the only place where events are processed.
> > >
> > > Rendering however is done on a separate thread, which eventually ends
> > > up calling:
> > >
> > > wl_surface_attach(surface, buffer, 0, 0);
> > > wl_surface_damage(surface, x, y, width, height);
> > > wl_surface_commit(surface);
> > > wl_display_flush(display);
> > >
> > > Is this safe or do I need to do any additional locking / synchronization?  
> >
> > Hi,
> >
> > that depends.
> >
> > First thing is if you have event handlers, for example wl_callback.done
> > for wl_surface.frame which you really should be using to throttle your
> > rendering. Event callbacks get dispatched from where the dispatch
> > function is called, so that could race with your rendering thread.  
> [...]
> Most of the examples I can find that use wl_callback.done assume that
> the application is ready to draw whenever it gets the callback (so in
> the callback, the application simply draws the next frame).
> But how would this be handled if the application needs to draw at
> application defined times? e.g. for an animation loop, or when an
> external event happens? When wl_callback.done runs, there is nothing
> new to draw yet.


simply don't draw from the event handler, but store a flag that says
"it's ok to draw whenever I have a reason to".

> And at some later time, the application wants to
> update the display. Should it then simply draw as follows:
> wl_surface_attach(surface, buffer, 0, 0);
> wl_surface_damage(surface, x, y, width, height);
> wl_surface_commit(surface);
> wl_display_flush(display);
> (note: this would happen on a thread which is not the same thread
> where the main loop runs -- see above).


Essentially, there are three conditions to be filled before you
can/should draw:
a) You have something new to show.
b) The previous drawing has been shown already (frame callback is done).
c) You have an available buffer to draw into.

Condition a) is up to you. Rather than something new to show, it could
also be just a desire to keep the feedback loop going. Or maybe you are
running an animation, in which case you always draw once for a frame
callback, but compute the animation state based on the current (or
predicted) time. Driving your animations by the compositor events tends
to produce more fluent motion than driving by a timer.

Condition b) is for throttling. You don't want to draw frames that will
only get discarded by the compositor, that would be wasted work. But,
sometimes you have other reasons to ignore condition b), e.g. if your
window state changes and you want to update ASAP. Then discarding the
old frame could make sense.

Condition c) is for limiting resource usage. If you don't have any
buffers available, you can always allocate a new one. At some point
though, it starts making sense to limit your memory usage and instead
wait for the compositor to release some of your old buffers to re-use
them. Four buffers is a limit that should be practically always enough.

None of this is related to asynchronicity. The conditions apply equally
to synchronous and asynchronous drawing loops.

-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 833 bytes
Desc: OpenPGP digital signature
URL: <>

More information about the wayland-devel mailing list