app/layer_tree.go
Functions
func AppendOverlaysToLayerTree
func AppendOverlaysToLayerTree(tree *compositor.OffsetLayerImpl, overlayWidgets []widget.Widget, existing *compositor.OffsetLayerImpl) {
if tree == nil || len(overlayWidgets) == 0 {
return
}
// Record child count before appending so we can fix overlay-specific
// flags on the newly added layers below.
preCount := len(tree.Children())
// Collect existing overlay layers for reuse.
var index map[uint64]layerIndex
if existing != nil {
index = collectLayerIndex(existing)
}
for _, w := range overlayWidgets {
if w == nil {
continue
}
if index != nil {
updateLayerRecursive(w, tree, index, 0, 0)
} else {
buildLayerRecursive(w, tree, 0, 0)
}
}
// Fix IsRoot flag on overlay PictureLayers. Overlay content widgets have
// Parent() == nil (they're standalone, not part of the main tree), so
// buildBoundaryLayer/syncPictureLayer sets IsRoot=true. This causes
// DrawGPUTextureBase (QueueBaseLayer, last-call-wins) to overwrite the
// actual root texture with the overlay texture → black background.
// Overlays must use DrawGPUTexture (sublayer blit) instead.
for _, child := range tree.Children()[preCount:] {
clearRootOnPictureLayers(child)
}
}
func BuildLayerTree
BuildLayerTree walks the widget tree and constructs a compositor layer tree.
Each RepaintBoundary widget produces a PictureLayer inside an OffsetLayer.
Non-boundary widgets are skipped (they're drawn inside their parent boundary).
ADR-007 Phase D: Layer Tree provides STRUCTURE (which boundaries exist,
their offsets, clip rects, opacity) for the texture rendering/blitting
pipeline. PictureLayerImpl stores BoundaryCacheKey, IsRoot, and Size to
link back to the per-boundary GPU texture cache in renderLoop.
Flutter equivalent: Layer tree is built during paint via paintChild.
func BuildLayerTree(root widget.Widget) *compositor.OffsetLayerImpl {
if root == nil {
return compositor.NewOffsetLayer(geometry.Point{})
}
rootLayer := compositor.NewOffsetLayer(geometry.Point{})
buildLayerRecursive(root, rootLayer, 0, 0)
return rootLayer
}
func PaintBoundaryLayers
PaintBoundaryLayers walks the widget tree and re-records dirty boundaries.
This is the Flutter flushPaint equivalent: only dirty boundary PictureLayers
are re-recorded. Clean boundaries keep their cached scenes.
After this function, all boundary CachedScene values are fresh.
The compositor can then Compose the layer tree to assemble the final scene.
PaintBoundaryLayers re-records dirty boundaries with nil context.
func PaintBoundaryLayers(root widget.Widget, _ *compositor.OffsetLayerImpl) {
PaintBoundaryLayersWithContext(root, nil, nil)
}
func PaintBoundaryLayersWithContext
PaintBoundaryLayersWithContext re-records dirty boundaries with a given context.
func PaintBoundaryLayersWithContext(root widget.Widget, _ *compositor.OffsetLayerImpl, ctx widget.Context) {
if root == nil {
return
}
paintBoundaryRecursiveCtx(root, ctx)
}
func PaintOverlayBoundaries
PaintOverlayBoundaries re-records dirty overlay content boundaries.
Overlay content widgets are already marked as RepaintBoundary by PushOverlay.
This function walks each overlay content widget (same as PaintBoundaryLayers
walks the main tree) so their CachedScene values are fresh for the compositor.
func PaintOverlayBoundaries(overlayWidgets []widget.Widget, ctx widget.Context) {
for _, w := range overlayWidgets {
if w == nil {
continue
}
paintBoundaryRecursiveCtx(w, ctx)
}
}
func UpdateLayerTree
UpdateLayerTree builds or updates a persistent layer tree. On the first call
(existing == nil), it builds from scratch (same as BuildLayerTree). On
subsequent calls, it reuses PictureLayerImpl and OffsetLayerImpl objects
for boundaries that still exist (matched by BoundaryCacheKey), updating
their fields from the current widget state. New boundaries get fresh layers;
removed boundaries are dropped.
Flutter equivalent: Layer.addRetained + ContainerLayer.updateSubtreeNeedsAddToScene.
The persistent tree eliminates per-frame layer allocations for stable UIs.
Returns the root OffsetLayerImpl (may be the same pointer as existing or new).
func UpdateLayerTree(root widget.Widget, existing *compositor.OffsetLayerImpl) *compositor.OffsetLayerImpl {
if existing == nil {
return BuildLayerTree(root)
}
if root == nil {
return compositor.NewOffsetLayer(geometry.Point{})
}
// Collect existing layers indexed by BoundaryCacheKey for O(1) lookup.
index := collectLayerIndex(existing)
// Build new tree structure, reusing existing layer objects where possible.
newRoot := compositor.NewOffsetLayer(geometry.Point{})
updateLayerRecursive(root, newRoot, index, 0, 0)
return newRoot
}
AppendOverlaysToLayerTree adds overlay content boundaries to an existing
Layer Tree. Overlays are appended AFTER main tree children, so they
composite on top (correct Z-order: main content → overlays bottom-to-top).
Each overlay content widget that is a RepaintBoundary gets its own
OffsetLayer + PictureLayer in the tree, just like main tree boundaries.
Non-boundary overlay widgets are skipped (they have no scene to composite).
The existing parameter is used for persistent tree reuse: overlay layers
from previous frames are matched by BoundaryCacheKey.