Back to Azul

Compact Cache Status Report

scripts/COMPACT_CACHE_STATUS.md

0.0.712.6 KB
Original Source

Compact Cache Status Report

Date: 2026-02-17
Branch: master (merged from getter-migration)
Build: cargo build -p azul-dll --features build-dll ✅ passes


1. Current State Summary

What Was Done (getter-migration, 5 commits)

  1. Centralized all CSS property access into layout/src/solver3/getters.rs
  2. Removed direct css_property_cache access from 6 solver files:
    • fc.rs, taffy_bridge.rs, display_list.rs, cache.rs, positioning.rs, layout_tree.rs
  3. 3 macro types handle the routine getters:
    • get_css_property! — enum properties → MultiValue<T> (13 invocations)
    • get_css_property_pixel! — dimension/spacing → MultiValue<PixelValue> (16 invocations)
    • get_css_property_value! — taffy bridge raw values → Option<CssPropertyValue<T>> (18 invocations)
  4. ~37 handwritten getters for complex cases (backgrounds, borders, fonts, scrollbar, etc.)
  5. 84 public functions total in getters.rs (3,473 lines)

What Does NOT Exist Yet

  • core/src/compact_cache.rsnot created
  • No CompactLayoutCache, CompactNodeProps, CompactTextProps structs
  • No build_compact_cache() function
  • No fast-path in any getter — all getters still go through the BTreeMap slow path

2. Are the Getters Using a Fast Path?

No. Every getter currently calls:

styled_dom.css_property_cache.ptr.$method(node_data, &node_id, node_state)

Which internally calls get_property_slow() → walks the cascade:

  1. user_overridden_properties (user JS/Rust overrides)
  2. css_*_props per pseudo-state (hover/active/focus/dragging)
  3. cascaded_*_props (CSS stylesheet)
  4. computed_values (inherited)
  5. UA CSS fallback

Each step does BTreeMap::get (O(log n) with pointer chasing per lookup).

The macros also add a second UA CSS check via azul_core::ua_css::get_ua_property(), which is redundant with the cascade but harmless.


3. Remaining Direct css_property_cache Access Outside getters.rs

FileLineUsageCan Be Moved?
layout/src/solver3/getters.rs79×All accesses (the canonical location)N/A
layout/src/solver3/layout_tree.rsL1484dependency_chains for font-size⚠️ Special — needs dependency chain, not a simple prop
layout/src/hit_test.rsL119get_cursor() for hit testing✅ Could use get_cursor_property()
layout/src/callbacks.rsL2420-2421Direct cache access for callbacks⚠️ May need full CssPropertyCache access

Verdict: The migration is ~97% complete. The 3 remaining call sites are edge cases (dependency chains, hit testing, callbacks) that don't benefit from compact cache anyway.


4. Property Tier Assignment Audit

Tier 1: Enum properties → Vec<u64> bitpacked (8 B/node)

All 20 enum properties from the plan have corresponding getters:

PropertyGetterBitsStatus
displayget_display_property / get_display_property_internal5✅ Has getter
positionget_position3✅ Has getter
floatget_float2✅ Has getter
overflow_xget_overflow_x3✅ Has getter
overflow_yget_overflow_y3✅ Has getter
box_sizingget_css_box_sizing1✅ Has getter
flex_directionget_flex_direction_prop2✅ Has getter (via get_css_property_value!)
flex_wrapget_wrap / get_flex_wrap_prop2✅ Has getter
justify_contentget_justify_content / get_justify_content_prop3✅ Has getter
align_itemsget_align_items_prop3✅ Has getter (via get_css_property_value!)
align_contentget_align_content_prop3✅ Has getter (via get_css_property_value!)
writing_modeget_writing_mode2✅ Has getter
clearget_clear2✅ Has getter
font_weight(accessed in get_style_properties)4⚠️ No standalone getter — inlined in get_style_properties()
font_style(accessed in get_style_properties)2⚠️ No standalone getter — inlined in get_style_properties()
text_alignget_text_align3✅ Has getter
visibilityget_visibility1✅ Has getter
white_spaceget_white_space_prop3✅ Has getter
directionget_direction1✅ Has getter
vertical_alignget_vertical_align_raw / get_vertical_align_for_node3✅ Has getter

Total: 51 bits used, 13 spare. All enum properties are covered.

Tier 2: Numeric dimensions → CompactNodeProps (64 B/node)

PropertyGetterEncodingStatus
widthget_css_widthu32 MSB-sentinel
heightget_css_heightu32
min_widthget_css_min_widthu32
max_widthget_css_max_widthu32
min_heightget_css_min_heightu32
max_heightget_css_max_heightu32
flex_basisget_flex_basis_propu32
font_sizeget_element_font_sizeu32
padding_top/right/bottom/leftget_css_padding_*i16 ×10✅ (4 getters)
margin_top/right/bottom/leftget_css_margin_*i16 ×10✅ (4 getters)
border_top/right/bottom/left_widthget_css_border_*_widthi16 ×10✅ (4 getters)
top/right/bottom/leftget_css_top/right/bottom/lefti16 ×10✅ (4 getters)
flex_growget_flex_grow_propu16 ×100
flex_shrinkget_flex_shrink_propu16 ×100
z_indexget_z_indexi16

All 25 dimension properties have getters. Ready for Tier 2.

Tier 2b: Text/IFC properties → CompactTextProps (24 B/node)

PropertyGetterEncodingStatus
text_colorget_style_properties (inline)u32 RGBA⚠️ No standalone getter
font_familyget_style_properties (inline)u64 hash⚠️ No standalone getter
line_heightget_line_height_valuei16 ×10
letter_spacingget_style_properties (inline)i16 ×10⚠️ No standalone getter
word_spacingget_style_properties (inline)i16 ×10⚠️ No standalone getter
text_indentget_text_indent_valuei16 ×10

4 of 6 text props lack standalone getters — they're embedded in the monolithic get_style_properties() function (L1463-L1789). This function builds a full StyleProperties struct with font resolution, color fallback, etc.

Tier 3: Overflow / Rare Properties → FxHashMap

Everything else: grid props (7 getters), transforms, filters, box-shadow (4 sides), backgrounds, borders (styles/colors), scrollbar, selection, caret, counters, shape-inside/outside, fragmentation (break-before/after/inside, orphans, widows), opacity, text-decoration, tab-size, etc.

Currently 18 get_css_property_value! getters for taffy bridge (flex/grid/alignment) would also fall into Tier 3 for the raw CssPropertyValue<T> wrapper.


5. Edge Cases & Challenges

5.1 Inheritance

The current CssPropertyCache.computed_values handles CSS inheritance (font-size, color, line-height, etc. flow from parent to child). The compact cache would be built after compute_inherited_values(), so it sees the final resolved values. No change needed — compact cache is a read-only snapshot.

5.2 Dynamic State (hover, active, focus)

get_property_slow() checks pseudo-state maps: css_hover_props, css_active_props, css_focus_props, etc. The compact cache must be rebuilt (or patched) when node states change. Options:

  • Full rebuild after state change — simplest. If build is fast enough (~3ms for 72K), acceptable.
  • Per-node patch — only update changed nodes' rows. More complex but avoids full rebuild.
  • Stateless compact cache — only cache the "normal" state, fall through to slow path for hover/active/focus. Since most nodes are in normal state, this still wins.

Recommendation: Start with option 3 (stateless cache for normal state only). Nodes with active hover/focus state (~0.1% of nodes) use the slow path — negligible impact.

5.3 Runtime Property Overrides

user_overridden_properties is the highest-priority layer (set via JS/Rust API at runtime). If the compact cache doesn't check it, runtime overrides would be invisible.

Solution: The compact cache builder includes user overrides (it calls get_property_slow() which already checks that layer first). For subsequent overrides after build, either rebuild or invalidate the affected node's row.

5.4 Font Resolution Chains

dependency_chains in CssPropertyCache tracks font-size resolution with em/rem dependencies. This is accessed in layout_tree.rs:1484 and get_element_font_size(). The compact cache stores resolved pixel values for font-size, so dependency chains are only needed during build.

5.5 Taffy Bridge (get_css_property_value! getters)

The 18 taffy bridge getters return Option<CssPropertyValue<T>> (the raw wrapper including Auto/Initial/Inherit). The compact cache encodes Auto/Initial as sentinels, so these can be reconstructed, but the mapping is more complex than simple value retrieval.

Recommendation: For taffy bridge getters, use Tier 2 encoding for flex/gap properties, and Tier 3 fallback for grid properties (rarely used, complex types).


Phase 1: Create compact_cache.rs (Tier 1 only)

Impact: Eliminates ~40% of BTreeMap lookups (display, position, overflow accessed 44+37+45 = 126× per node)

  1. Create core/src/compact_cache.rs with:

    • CompactLayoutCache { tier1_enums: Vec<u64> }
    • Encode/decode functions for all 20 enum props
    • build_tier1() that iterates nodes, calls get_property_slow() for each enum
  2. Add pub compact_cache: Option<Box<CompactLayoutCache>> to CssPropertyCache

  3. Wire build_tier1() in styled_dom.rs after compute_inherited_values()

  4. Update 12 get_css_property! getters + get_display_property to check Tier 1 first:

    rust
    if let Some(cc) = &styled_dom.css_property_cache.ptr.compact_cache {
        return cc.get_display(node_id.index());
    }
    // fallback to slow path
    

Estimated effort: ~400 lines of code, ~2 hours.

Phase 2: Add Tier 2 dimensions

Impact: Eliminates another ~20% of BTreeMap lookups

  1. Add CompactNodeProps (64 B/node) struct to compact_cache.rs
  2. Add tier2_dims: Vec<CompactNodeProps> to CompactLayoutCache
  3. Implement MSB-sentinel encoding/decoding
  4. Update 16 get_css_property_pixel! getters + 4 get_css_min/max_* getters

Estimated effort: ~600 lines, ~3 hours.

Phase 3: Add Tier 2b text props

Impact: Eliminates font-family cloning and text property BTreeMap lookups

  1. Add CompactTextProps (24 B/node) struct
  2. Extract standalone getters from get_style_properties() for text_color, font_family_hash, letter_spacing, word_spacing
  3. Update get_style_properties() and IFC text getters

Estimated effort: ~300 lines, ~2 hours.

Phase 4: Tier 3 overflow + taffy bridge

Impact: Complete coverage, no BTreeMap on hot paths

  1. Add tier3_overflow: Vec<Option<Box<FxHashMap<CssPropertyType, CssProperty>>>>
  2. Route 18 taffy bridge getters through compact cache
  3. Grid properties stay in Tier 3

Estimated effort: ~200 lines, ~1 hour.


7. Key Design Decision: Keep CssPropertyCache

Recommendation: Keep CssPropertyCache. Don't replace it.

The compact cache is a read-only acceleration layer built on top of the existing CssPropertyCache. Reasons:

  1. CssPropertyCache handles writes — set_property(), user overrides, cascade
  2. CssPropertyCache handles the full cascade — needed for build_compact_cache()
  3. CssPropertyCache handles all pseudo-states — compact cache can start with normal-only
  4. Paint properties (backgrounds, transforms, filters, etc.) don't need compact cache — they're accessed ~1× per node during display list generation, not in tight loops
  5. Incremental migration — each phase independently speeds up the hot path without risking regressions

The goal is: hot layout loops read from compact cache → cold/rare paths read from CssPropertyCache.


8. File Change Summary

FilePhaseChange
core/src/compact_cache.rs1NEW — CompactLayoutCache + Tier 1
core/src/lib.rs1Add pub mod compact_cache;
core/src/prop_cache.rs1Add compact_cache field to CssPropertyCache
core/src/styled_dom.rs1Call build_compact_cache() after restyle
layout/src/solver3/getters.rs1-3Add fast-path checks in macros/getters
core/Cargo.toml4Add rustc-hash for FxHashMap (Tier 3 only)