Skip to content

feat(atlas): integrate Taffy layout with state-machine lifecycle#14

Merged
Briany4717 merged 11 commits into
mainfrom
feat/atlas-taffy-integration
May 18, 2026
Merged

feat(atlas): integrate Taffy layout with state-machine lifecycle#14
Briany4717 merged 11 commits into
mainfrom
feat/atlas-taffy-integration

Conversation

@Briany4717

@Briany4717 Briany4717 commented May 18, 2026

Copy link
Copy Markdown
Owner

What does this PR do?

Implements the Atlas subsystem's Taffy integration as defined in
RFC-0001 4.1 and 9. Wraps taffy::TaffyTree behind a state-machine API
that enforces strict separation between tree-building and layout-result
phases.

The Atlas exposes a minimal high-level API (add_leaf, add_container,
set_root, compute, resolved_rect, populate_frame) and never
leaks Taffy types to the rest of the engine — AtlasNodeId wraps
taffy::NodeId as an opaque newtype so future layout backend changes
stay contained.

Resolved geometry crosses to the Encoder exclusively through
RenderFrame::push_rect, respecting the dependency graph in
RFC-0001 9.

Linked issue

Closes #3
Refs #1

Tasks

  • Taffy tree initialization inside Atlas (LayoutAtlas::new)
  • Layout computation from a simple node tree
    (computes_layout_for_container_with_one_child)
  • Resolved rects exposed to Encoder via frame.rs primitives
    (Rect, RenderFrame::push_rect, LayoutAtlas::populate_frame)

Acceptance criteria

  • Atlas computes a valid layout for a single rectangle with a child
    computes_layout_for_container_with_one_child (a "text child" is
    modelled as a sized leaf since glyphon integration is a later
    sub-issue; this matches Phase 1 scope per RFC-0001 §1).
  • Resolved geometry is written into RenderFrame without crossing
    subsystem boundaries directly — populate_frame_writes_resolved_geometry
    asserts the full path, and the atlas module imports nothing from
    encoder or any other subsystem.

Design decisions

These were discussed before implementation and recorded here for review:

  1. Zero-allocation reuse via clear(). Both LayoutAtlas and
    RenderFrame expose clear() that retains internal capacity. After
    the first frame, subsequent layouts pay zero allocation cost as long
    as node counts stay within the high-water mark.

  2. Dirty tracking lives in the Evaluator. The Atlas does not
    maintain its own dirty set. The EvaluatorTick::collect_dirty()
    implemented in feat(evaluator): implement dirty-flag tick collection loop #13 already produces TargetIds the Atlas can consume
    in a future sub-issue (recompute_dirty). This avoids state
    duplication and respects the dependency graph.

  3. State-machine lifecycle. LayoutAtlas has two states:
    Building (nodes can be added/modified) and Computed (geometry
    accessible, mutations panic). This prevents accidental partial
    recomputation mid-frame. A type-state encoding was considered but
    rejected for MVP — the enum + panic approach is less invasive on
    callers and can be tightened to type-state later if it proves fragile.

  4. Opaque AtlasNodeId. Wraps taffy::NodeId so the rest of the
    engine never imports from Taffy.

  5. Errors via AtlasError. Wraps taffy::TaffyError and composes
    with ByardError. Never panics on Taffy failures — returns Result.

Performance notes

No benchmarks added in this PR — Phase 1 deliverable is correctness and
the lifecycle contract. Performance benchmarks for typical and worst-case
layouts will land alongside the dirty-recompute sub-issue, where they
can compare full vs incremental recomputation meaningfully.

Checklist

  • cargo fmt --all passes
  • cargo clippy --workspace --all-targets -- -D warnings passes
  • cargo test --workspace passes (55 unit tests, +16 in this PR)
  • New public items have doc comments
  • If this changes behavior visible to users, CHANGELOG.md has an entry under [Unreleased]
  • If this changes the architecture, an RFC is linked or opened alongside this PR

Follow-up sub-issues

Will be opened as sub-issues of #1 after merge:

  • feat(atlas): builder API for ergonomic tree construction
    — fluent chainable API on top of add_leaf/add_container for the
    bylang transpiler to target.
  • feat(atlas): incremental recompute_dirty consuming EvaluatorTick output
    — wires TargetIds from the Evaluator into Taffy's mark_dirty API
    for selective recomputation.
  • feat(atlas): implement spatial hash grid for O(1) hit-testing
    — the other half of the Atlas subsystem per RFC-0001 4.1.

Notes for reviewers

The state-machine assertions use panic! rather than Result because
they protect against caller bugs (calling add_leaf after compute),
not user input. This matches the convention in wgpu::CommandEncoder
where finish() consumes the encoder.

populate_frame walks the tree in pre-order. This will become Z-bin
order once the Encoder lands and primitives are sorted by pipeline +
local Z-index. The current order is documented as provisional.

CHANGELOG checkbox N/A — pre-alpha; changelog will be introduced at v0.1.0.

Copilot AI review requested due to automatic review settings May 18, 2026 02:04

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds the initial Atlas layout implementation by wrapping Taffy behind a LayoutAtlas state-machine API and introducing shared frame geometry primitives.

Changes:

  • Adds Rect and Viewport shared frame types.
  • Adds the atlas::layout module with LayoutAtlas, node IDs, styles, errors, and tests.
  • Adds the taffy dependency to byard-core.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 12 comments.

File Description
crates/byard-core/src/frame.rs Adds shared rectangle and viewport primitives plus rectangle tests.
crates/byard-core/src/atlas/mod.rs Updates Atlas module docs and re-exports layout API types.
crates/byard-core/src/atlas/layout.rs Implements the Taffy-backed layout atlas and lifecycle tests.
crates/byard-core/Cargo.toml Adds the taffy dependency.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread crates/byard-core/src/frame.rs
Comment thread crates/byard-core/src/atlas/mod.rs Outdated
Comment thread crates/byard-core/src/atlas/layout.rs Outdated
Comment thread crates/byard-core/src/atlas/layout.rs
Comment thread crates/byard-core/src/frame.rs
Comment thread crates/byard-core/src/atlas/layout.rs Outdated
Comment thread crates/byard-core/src/atlas/layout.rs
Comment thread crates/byard-core/src/atlas/layout.rs
Comment thread crates/byard-core/src/atlas/layout.rs
Comment thread crates/byard-core/src/atlas/layout.rs Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 13 comments.

Comments suppressed due to low confidence (4)

crates/byard-core/src/lib.rs:39

  • These public variants and the public fields on PipelineCompilation lost their doc comments. The workspace enables missing_docs = "warn" and CI runs clippy with -D warnings, and CONTRIBUTING.md requires public API docs, so this will fail the documented checks until the variant and field docs are restored.
    /// A render pipeline failed to compile during initialisation.
    PipelineCompilation {

crates/byard-core/src/atlas/layout.rs:153

  • This intra-doc link points to AtlasError::Taffy, but the enum only defines Backend. The public error documentation for add_container is currently wrong and will produce a broken link when building docs.
    /// Returns [`AtlasError::Taffy`] if the underlying engine refuses the

crates/byard-core/src/atlas/layout.rs:205

  • This intra-doc link points to AtlasError::Taffy, but the enum only defines Backend. The public error documentation for compute is currently wrong and will produce a broken link when building docs.
    pub fn compute(&mut self, viewport: Viewport) -> Result<(), AtlasError> {

crates/byard-core/src/lib.rs:41

  • Wrapping AtlasError here without updating the std::error::Error impl means ByardError::source() still returns None for layout failures. That breaks normal Rust error-chain reporting for the new nested error even though the underlying AtlasError is stored directly.
        /// Name of the pipeline that failed (e.g. `"SolidBox"`).
        pipeline: String,

Comment thread crates/byard-core/src/atlas/layout.rs
Comment thread crates/byard-core/src/atlas/layout.rs Outdated
Comment thread crates/byard-core/src/frame.rs Outdated
Comment thread crates/byard-core/src/atlas/layout.rs
Comment thread crates/byard-core/src/atlas/layout.rs
Comment thread crates/byard-core/src/atlas/layout.rs
Comment thread crates/byard-core/src/atlas/layout.rs
Comment thread crates/byard-core/src/atlas/layout.rs
Comment thread crates/byard-core/src/atlas/layout.rs
Comment thread crates/byard-core/src/atlas/layout.rs Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@Briany4717 Briany4717 merged commit 5c278c9 into main May 18, 2026
6 of 7 checks passed
@Briany4717 Briany4717 deleted the feat/atlas-taffy-integration branch May 18, 2026 04:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(atlas): integrate Taffy layout

2 participants