Use paint nodes everywhere
MR !612 (merged) introduces "paint nodes" as suggested by @daniels.
The following discussion from !582 (merged) should be addressed:
- @daniels started a discussion: (+11 comments)
In that thread, a plan was drafted for making use of paint nodes to resolve several internal design problems in libweston. This plan, when fully realized, should:
- allow overlapping
weston_output
s with correct damage tracking - simplify
weston_view
handling vs. sub-surfaces - help DRM-backend with KMS plane vs. view related bookkeeping
- provide per-output z-ordered view repaint lists optimized for each output
- allow caching the total geometric transformation from buffer to output pixel coordinates
Many new tests will be needed to minimize regressions:
-
Ensuring damage is correctly handled with damage from clients, and view map, unmap, move, resize, restack. (This should be correct already.) -
Check that damage masked by unchanged opaque regions gets correctly ignored on repainting an output. (This should happen already too.) -
In-compositor inflicted damage does not result in texture uploads (#3). -
Test everything also multiple outputs, and views spanning multiple outputs.
Issues from !612 (merged):
-
weston_output
paint_node_z_order_list
needs to be pruned; build it so that it contains only those paint nodes that actually affect the output.
From !582 (comment 899406):
-
weston_view
is retained as the structure for the frontend window manager to place surfaces into the scene graph, which are placed in layer lists -
we create a new structure for the backends (including renderer) to consume as part of repaint (call it weston_paint_node
) containing view ptr + link, output ptr + link, and surface ptr + link; it's placed in a list inweston_view
, withweston_compositor_build_view_list
creating it for everyweston_view
+weston_output
combination if not already existing, andweston_output_destroy
/weston_view_destroy
destroying any paint nodes which reference that output/view (!612 (merged)) -
we create a weston_paint_node->color_state
which this patchset can use to cache the core color state without the need for surface/view/output destroy listeners, since it will be explicitly destroyed from either surface -> view -> paint_node destruction or output -> paint_node destruction (!582 (merged)) -
output repaint (backend and renderer) is changed to iterate paint-nodes rather than views, albeit mostly just back-referencing to views for everything (!612 (merged)) -
over time, we hollow out weston_view
as things like damage/geometry/plane/etc migrate toweston_paint_node
, being constructed from surface+view as required (e.g.weston_surface_damage
calls through toweston_view_damage
, however this just iterates the list of paint-nodes and applies damage to those, at this point we could probably make the paint-node damage be pre-transformed to output co-ordinate space too, as well as handling occlusion) -
view_list
becomes completely untouched by backends at this point, being used only by middle end surface (e.g. surface damage -> paint-node damage via views) and input picking (global co-ord -> surface+co-ord via views) -
weston_paint_node
gains aroot_view
member, referring to the view which was actually created by the frontend rather than internal -
input picking is refactored: touch events (being derived from output co-ordinates) pick a paint-node directly, pointer events (being derived from global co-ordinates) are transformed to output co-ordinate space and then pick a paint-node, focus is stored as a root-view + surface pair (and we eventually create a more sensible focus interface for frontends but this is already too long to get into) -
middle-end paint-node generation is refactored: rather than generating views as required (e.g. materialising views for subsurfaces), it begins from a root view, directly materialising paint nodes as required for every surface+output combination under that root view (perhaps generating multiple paint-nodes for each render key such as blended-under vs. unoccluded, etc)
From !582 (comment 901104):
-
At a later point, paint nodes later get their own transform
member (transformed into output space and clipped for occlusion), which the backend and renderers are modified to use during repaint, initially just derived from theweston_view
. At a further later point (either commit or MR), the view'stransform
is removed, with each paint-node's transform being derived from the root view's geometry + (sub)surface transform + output transform.
From !582 (comment 901163):
The path to sensible damage tracking looks quite similar, I think, and definitely comes after the above movement of transform, but also comes somewhere before being able to move away from views in the backend/renderer.
In rough order, here's what I see we need to do:
-
Everything remains as is, nothing is touched -
Paint nodes gain a damage
member, andview_accumulate_damage
propagates the damage region down to each paint node for that view, transformed to the output and clipped to the paint node's total clip (i.e. none if occluded) -
output_accumulate_damage
is changed to accumulate from all the paint nodes for that output, rather than from the view -
output_accumulate_damage
is changed to only flush surface damage for surfaces whose paint nodes on that output contain active damage, which means we no longer flush surface damage for occluded surfaces (the first client-visible behaviour change) -
weston_view_damage
is introduced to damage a region (or all) of a view, which internally calls a newweston_paint_node_damage()
for all of its paint nodes -
weston_view->plane
is moved to the paint node, with damage incurred from plane changes being placed on the paint node rather than the whole view - [everything after this point is a little hazy as it's too far in the future to say confidently, but in broad strokes ...]
-
the logic for incurring damage from subsurface stacking/geometry changes and view transform updates (e.g. moving a view), is moved to act on paint nodes, if a paint node changes position or is added/removed within the stack, it is that operation of manipulating the paint nodes which incurs damage, rather than the current mechanism of surface commit -> subsurface commit -> subsurface z-order stack change -> damage all surfaces -> schedule repaint for involved outputs -> [wait for output repaint] -> accumulate surface damage into view damage -> reduce view damage into paint-node damage -
weston_surface_damage
callers are migrated to damage either paint nodes (for low-level operations), or damage views (for window-management operations) which in turn damage paint nodes -
view damage is removed, since it is no longer used by anyone and no longer makes sense at this point -
We now have one less obstacle in the way of requiring one view per (sub)surface in the scene graph, and we've fixed #3, and we can finally have outputs which overlap in global co-ordinate space
Suffice to say, this is a lot of work, but it can at least be done incrementally, and gives us a lot of good intermediate points to write new tests against: with the new model separating client-provoked damage from WM-provoked damage, we have semantics we can reason about and validate at each point.