Aperture UI
Engineer Notes

Render Layer System

Render Layer System

Overview

The Render Layer System provides a compositing model for Harrlow similar to modern browser engines (Chrome, Safari, Firefox). It ensures correct paint order for UI elements, particularly addressing the issue where text was rendering behind backgrounds.

Based on concepts from: https://web.dev/articles/speed-layers

Architecture

Key Components

  1. HarrlowDrawCommand (HarrlowCommandList.h)
    • Extended with layerId, zIndex, and createsStackingContext fields
    • Commands are sorted by (layerId, drawOrder) before rendering
  2. RenderLayer (RenderLayer.h)
    • Represents a compositing layer in the render tree
    • Contains layer creation reasons, transform, opacity, blend mode
    • Maintains list of command indices belonging to this layer
    • Supports layer caching for performance
  3. LayerTree (RenderLayer.h/.cpp)
    • Manages the hierarchy of render layers
    • Handles layer creation, sorting, and compositing order
    • Provides flattened layer list for rendering
  4. SimpleAPHIRenderContext (SimpleAPHIRenderContext.cpp)
    • Modified to sort commands by (layerId, drawOrder) before processing
    • Ensures backgrounds render before text, z-index is respected

Layer Creation Criteria

Elements get their own compositing layer for these reasons:

  • RootLayer: html/body element
  • PositionedWithZIndex: position:absolute/relative/fixed with z-index
  • Opacity: opacity < 1
  • Transform3D: 3D transforms (perspective, rotateX/Y/Z)
  • Transform2D: 2D transforms (rotate, scale, translate)
  • Filter: CSS filter effects
  • Video: <video> elements
  • Canvas: <canvas> elements
  • WillChange: will-change CSS hint
  • AnimatedTransform/Opacity: CSS animations on transform/opacity

Paint Order (CSS 2.1 Appendix E)

Within each stacking context, elements are painted in this order:

  1. Background and borders of the element
  2. Descendants with negative z-index (in stacking order)
  3. In-flow, non-positioned, block-level descendants
  4. Non-positioned floats
  5. In-flow, non-positioned, inline-level descendants
  6. Positioned descendants with z-index: auto or z-index: 0
  7. Descendants with positive z-index (in stacking order)

Command Sorting

Commands are sorted using a stable sort with key (layerId, drawOrder):

std::stable_sort(commands.begin(), commands.end(),
  [](const HarrlowDrawCommand& a, const HarrlowDrawCommand& b)
  {
    if (a.layerId != b.layerId)
      return a.layerId < b.layerId;
    return a.drawOrder < b.drawOrder;
  });

This ensures:

  • Lower layers paint first (backgrounds before foregrounds)
  • Within a layer, lower drawOrder paints first
  • Stable sort preserves document tree order for equal keys

Usage

Automatic (HTMLPainter)

HTMLPainter automatically assigns drawOrder values based on the CSS stacking context rules. No additional code is required for correct rendering.

Manual (Custom Rendering)

When appending commands manually:

HarrlowDrawCommand cmd;
cmd.drawOrder = 100;  // Background
// ... setup vertices, etc
recorder->AppendCommand(std::move(cmd));

HarrlowDrawCommand textCmd;
textCmd.drawOrder = 200;  // Text (higher = on top)
// ... setup text vertices
recorder->AppendCommand(std::move(textCmd));

Performance Considerations

  1. Batching: Commands with identical state (texture, blend, clip) but different drawOrder are still batched together after sorting
  2. Layer Caching: Layers can cache their content as textures to avoid repaint
  3. Sort Cost: O(n log n) sort per frame, but n is typically small (<1000 commands)
  4. Memory: LayerTree allocates per-layer, but most UIs have <50 layers

Future Enhancements

  • GPU-accelerated layer caching (render-to-texture)
  • Layer squashing (merge overlapping layers with compatible properties)
  • Partial layer updates (only repaint dirty regions)
  • Will-change optimization hints
  • Layer debugging overlay (show layer boundaries, reasons)
Copyright © 2026