Summary#
Implement a retained layer tree and damage tracking system so the renderer only repaints regions that have actually changed, instead of rebuilding the entire display list and repainting every pixel each frame.
Background#
Currently, build_display_list() regenerates the complete Vec<PaintCommand> from scratch, and the GPU renderer processes every command. The glyph atlas already has per-page dirty bits, but there's no higher-level damage tracking. For pages with small DOM mutations (e.g., cursor blink, hover effects), this wastes significant GPU and CPU time.
Acceptance Criteria#
- Retained layer tree: maintain a persistent tree of compositing layers between frames
- Each layer has: content display list, bounding rect, transform, opacity, z-index
- Layers are created for: stacking contexts, position:fixed, will-change, overflow:scroll, opacity < 1
- Damage tracking: when layout/style changes occur, compute damaged rectangles
- Compare old and new layout boxes to find changed regions
- Union of old and new bounding rects for moved/resized elements
- Text content changes damage the containing line box rect
- Background/border changes damage the element's border box rect
- Partial display list rebuild: only regenerate paint commands for damaged layers
- Partial GPU upload: only re-render damaged regions to layer textures
- Use scissor rects to limit GPU drawing to damaged areas
- Damage rect merging: coalesce small nearby damage rects to avoid excessive draw calls
- Full repaint still works (e.g., on scroll, resize, or initial load)
- All existing render tests pass
- Add tests: modify one element, verify only its damage rect is repainted (via metrics or debug output)
Implementation Notes#
- The layer tree lives in
crates/render/src/alongside the existing display list code - Each layer caches its display list between frames; only dirty layers rebuild
- Damage rects are in screen coordinates (after layout → paint coordinate transform)
- For scroll, the damage rect is the entire scroll container (optimization: blit and fill new strip)
- Start with a simple approach: track one global damage rect. Refine to per-layer damage later.
- The existing
GpuRendereralready supports scissor rects — extend this for damage clipping
Dependencies#
- Benefits from: Incremental layout (dirty bits propagate to damage rects naturally)
Phase#
Phase 15: Performance