On Mon, 2 Mar 2020 13:10:45 +0100 Guillermo Rodriguez <guillerodriguez....@gmail.com> 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. Hi, 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). Sure. 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. Thanks, pq
pgp8dchBGUGpI.pgp
Description: OpenPGP digital signature
_______________________________________________ wayland-devel mailing list wayland-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/wayland-devel