protocol: Define Content Update behavior

The protocol currently is in a state where we define that commits create
content updates and they are queued up until they are applied, and the
old view that commit applies the state or caches it in the parent state.

This commit moves the protocol completely to the new model which retains
the old behavior when no constraints are being used but allows for
constraints to be used to hold back a group of synchronized content
updates.

To convince yourself that this indeed retains the original behavior I
suggest to play around with a few examples and look at the resulting
graphs, as is done here:

https://gitlab.freedesktop.org/wayland/wayland/-/issues/457#note_2403135

Signed-off-by: Sebastian Wick <sebastian.wick@redhat.com>
This commit is contained in:
Sebastian Wick 2024-05-28 21:04:35 +02:00 committed by Pekka Paalanen
parent fc33162673
commit dd8afabdcc

View file

@ -1687,21 +1687,48 @@
etc.) is double-buffered. Protocol requests modify the pending state, etc.) is double-buffered. Protocol requests modify the pending state,
as opposed to the active state in use by the compositor. as opposed to the active state in use by the compositor.
A commit request atomically creates a content update from the pending
state, even if the pending state has not been touched. The content
update is placed in a queue until it becomes active. After commit, the
new pending state is as documented for each related request.
When the content update is applied, the wl_buffer is applied before all
other state. This means that all coordinates in double-buffered state
are relative to the newly attached wl_buffers, except for
wl_surface.attach itself. If there is no newly attached wl_buffer, the
coordinates are relative to the previous content update.
All requests that need a commit to become effective are documented All requests that need a commit to become effective are documented
to affect double-buffered state. to affect double-buffered state.
Other interfaces may add further double-buffered surface state. Other interfaces may add further double-buffered surface state.
A commit request atomically creates a Content Update (CU) from the
pending state, even if the pending state has not been touched. The
content update is placed at the end of a per-surface queue until it
becomes active. After commit, the new pending state is as documented for
each related request.
A CU is either a Desync Content Update (DCU) or a Sync Content Update
(SCU). If the surface is effectively synchronized at the commit request,
it is a SCU, otherwise a DCU.
When a surface transitions from effectively synchronized to effectively
desynchronized, all SCUs in its queue which are not reachable by any
DCU become DCUs and dependency edges from outside the queue to these CUs
are removed.
See wl_subsurface for the definition of 'effectively synchronized' and
'effectively desynchronized'.
When a CU is placed in the queue, the CU has a dependency on the CU in
front of it and to the SCU at end of the queue of every direct child
surface if that SCU exists and does not have another dependent. This can
form a directed acyclic graph of CUs with dependencies as edges.
In addition to surface state, the CU can have constraints that must be
satisfied before it can be applied. Other interfaces may add CU
constraints.
All DCUs which do not have a SCU in front of themselves in their queue,
are candidates. If the graph that's reachable by a candidate does not
have any unsatisfied constraints, the entire graph must be applied
atomically.
When a CU is applied, the wl_buffer is applied before all other state.
This means that all coordinates in double-buffered state are relative to
the newly attached wl_buffers, except for wl_surface.attach itself. If
there is no newly attached wl_buffer, the coordinates are relative to
the previous content update.
</description> </description>
</request> </request>
@ -3140,23 +3167,9 @@
hidden, or if a NULL wl_buffer is applied. These rules apply hidden, or if a NULL wl_buffer is applied. These rules apply
recursively through the tree of surfaces. recursively through the tree of surfaces.
The behaviour of a wl_surface.commit request on a sub-surface A sub-surface can be in one of two modes. The possible modes are
depends on the sub-surface's mode. The possible modes are synchronized and desynchronized, see methods wl_subsurface.set_sync and
synchronized and desynchronized, see methods wl_subsurface.set_desync.
wl_subsurface.set_sync and wl_subsurface.set_desync. Synchronized
mode caches the wl_surface state to be applied when the parent's
state gets applied, and desynchronized mode applies the pending
wl_surface state directly. A sub-surface is initially in the
synchronized mode.
Sub-surfaces also have another kind of state, which is managed by
wl_subsurface requests, as opposed to wl_surface requests. This
state includes the sub-surface position relative to the parent
surface (wl_subsurface.set_position), and the stacking order of
the parent and its sub-surfaces (wl_subsurface.place_above and
.place_below). This state is applied when the parent surface's
wl_surface state is applied, regardless of the sub-surface's mode.
As the exception, set_sync and set_desync are effective immediately.
The main surface can be thought to be always in desynchronized mode, The main surface can be thought to be always in desynchronized mode,
since it does not have a parent in the sub-surfaces sense. since it does not have a parent in the sub-surfaces sense.
@ -3168,6 +3181,15 @@
synchronized mode, and then assume that all its child and grand-child synchronized mode, and then assume that all its child and grand-child
sub-surfaces are synchronized, too, without explicitly setting them. sub-surfaces are synchronized, too, without explicitly setting them.
If a surface behaves as in synchronized mode, it is effectively
synchronized, otherwise it is effectively desynchronized.
A sub-surface is initially in the synchronized mode.
The wl_subsurface interface has requests which modify double-buffered
state of the parent surface (wl_subsurface.set_position, .place_above and
.place_below).
Destroying a sub-surface takes effect immediately. If you need to Destroying a sub-surface takes effect immediately. If you need to
synchronize the removal of a sub-surface to the parent surface update, synchronize the removal of a sub-surface to the parent surface update,
unmap the sub-surface first by attaching a NULL wl_buffer, update parent, unmap the sub-surface first by attaching a NULL wl_buffer, update parent,
@ -3198,20 +3220,18 @@
<request name="set_position"> <request name="set_position">
<description summary="reposition the sub-surface"> <description summary="reposition the sub-surface">
This schedules a sub-surface position change. This sets the position of the sub-surface, relative to the parent
surface.
The sub-surface will be moved so that its origin (top left The sub-surface will be moved so that its origin (top left
corner pixel) will be at the location x, y of the parent surface corner pixel) will be at the location x, y of the parent surface
coordinate system. The coordinates are not restricted to the parent coordinate system. The coordinates are not restricted to the parent
surface area. Negative values are allowed. surface area. Negative values are allowed.
The scheduled coordinates will take effect whenever the state of the
parent surface is applied.
If more than one set_position request is invoked by the client before
the commit of the parent surface, the position of a new request always
replaces the scheduled position from any previous request.
The initial position is 0, 0. The initial position is 0, 0.
Position is double-buffered state on the parent surface, see
wl_subsurface and wl_surface.commit for more information.
</description> </description>
<arg name="x" type="int" summary="x coordinate in the parent surface"/> <arg name="x" type="int" summary="x coordinate in the parent surface"/>
<arg name="y" type="int" summary="y coordinate in the parent surface"/> <arg name="y" type="int" summary="y coordinate in the parent surface"/>
@ -3225,13 +3245,11 @@
parent surface. Using any other surface, including this sub-surface, parent surface. Using any other surface, including this sub-surface,
will cause a protocol error. will cause a protocol error.
The z-order is double-buffered. Requests are handled in order and
applied immediately to a pending state. The final pending state is
copied to the active state the next time the state of the parent
surface is applied.
A new sub-surface is initially added as the top-most in the stack A new sub-surface is initially added as the top-most in the stack
of its siblings and parent. of its siblings and parent.
Z-order is double-buffered state on the parent surface, see
wl_subsurface and wl_surface.commit for more information.
</description> </description>
<arg name="sibling" type="object" interface="wl_surface" <arg name="sibling" type="object" interface="wl_surface"
summary="the reference surface"/> summary="the reference surface"/>
@ -3240,6 +3258,7 @@
<request name="place_below"> <request name="place_below">
<description summary="restack the sub-surface"> <description summary="restack the sub-surface">
The sub-surface is placed just below the reference surface. The sub-surface is placed just below the reference surface.
See wl_subsurface.place_above. See wl_subsurface.place_above.
</description> </description>
<arg name="sibling" type="object" interface="wl_surface" <arg name="sibling" type="object" interface="wl_surface"
@ -3249,42 +3268,18 @@
<request name="set_sync"> <request name="set_sync">
<description summary="set sub-surface to synchronized mode"> <description summary="set sub-surface to synchronized mode">
Change the commit behaviour of the sub-surface to synchronized Change the commit behaviour of the sub-surface to synchronized
mode, also described as the parent dependent mode. mode.
In synchronized mode, wl_surface.commit on a sub-surface will See wl_subsurface and wl_surface.commit for more information.
accumulate the committed state in a cache, but the state will
not be applied and hence will not change the compositor output.
The cached state is applied to the sub-surface immediately after
the parent surface's state is applied. This ensures atomic
updates of the parent and all its synchronized sub-surfaces.
Applying the cached state will invalidate the cache, so further
parent surface commits do not (re-)apply old state.
See wl_subsurface for the recursive effect of this mode.
</description> </description>
</request> </request>
<request name="set_desync"> <request name="set_desync">
<description summary="set sub-surface to desynchronized mode"> <description summary="set sub-surface to desynchronized mode">
Change the commit behaviour of the sub-surface to desynchronized Change the commit behaviour of the sub-surface to desynchronized
mode, also described as independent or freely running mode. mode.
In desynchronized mode, wl_surface.commit on a sub-surface will See wl_subsurface and wl_surface.commit for more information.
apply the pending state directly, without caching, as happens
normally with a wl_surface. Calling wl_surface.commit on the
parent surface has no effect on the sub-surface's wl_surface
state. This mode allows a sub-surface to be updated on its own.
If cached state exists when wl_surface.commit is called in
desynchronized mode, the pending state is added to the cached
state, and applied as a whole. This invalidates the cache.
Note: even if a sub-surface is set to desynchronized, a parent
sub-surface may override it to behave as synchronized. For details,
see wl_subsurface.
If a surface's parent surface behaves as desynchronized, then
the cached state is applied on set_desync.
</description> </description>
</request> </request>
</interface> </interface>