Skip to content

Migrate motiongfx off bevy_ecs onto typarena#114

Open
nixonyh wants to merge 5 commits into
mainfrom
nixon/typarena
Open

Migrate motiongfx off bevy_ecs onto typarena#114
nixonyh wants to merge 5 commits into
mainfrom
nixon/typarena

Conversation

@nixonyh

@nixonyh nixonyh commented Jul 4, 2026

Copy link
Copy Markdown
Member

nixonyh added 2 commits July 3, 2026 00:15
`ActionWorld` (renamed `ActionTable`) no longer wraps a bevy_ecs
`World`; it now composes `typarena::id::{GenId, IdGenerator}` for
entity-style ids and `typarena::type_table::TypeTable` for
heterogeneous per-action storage. `IdRegistry`'s bevy hook-based
cleanup is replaced by an explicit `Cleanup` fn stored once per
subject-id type in `Resources`, a hand-rolled type-keyed singleton
map standing in for bevy's `Resource` storage. `pipeline::sample`'s
multi-component query is replaced by a manual column probe driven
from the `SampleMode` column, with `ColumnId`s for hot, non-generic
columns cached on `ActionTable` to skip repeat `TypeId` hashing.

The `action` module is split into `action/id_registry.rs`
(`IdRegistry`, `UId`, `Cleanup`) and `action/table.rs` (`ActionTable`
and its builders), with `action.rs` left holding the plain
component/value types and re-exporting the public surface.

`bevy_platform` is dropped in favor of `hashbrown` directly, and
`MotionGfxManager` (in `bevy_motiongfx`) becomes a non-send resource
instead of requiring `ActionTable` to be `Send + Sync` — `typarena`'s
`TypeTable` makes no such guarantee, and forcing one would preclude
storing non-thread-safe values in it for other consumers.
@nixonyh nixonyh requested a review from Jaghov July 4, 2026 05:48
@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ed8931fc-b815-470b-98a8-35d4cea2663d

📥 Commits

Reviewing files that changed from the base of the PR and between f5397a6 and 2f25de5.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (2)
  • Cargo.toml
  • crates/motiongfx/src/track.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • Cargo.toml
  • crates/motiongfx/src/track.rs

📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Introduced a new action table–based storage and sampling flow for baking and playback.
    • Added stable subject-ID tracking with automatic cleanup and safe reuse across actions.
    • Added benchmarks for action storage performance (single and mixed scenes).
  • Bug Fixes
    • Improved how queued actions are resolved during sampling, including correct application of optional easing.
    • Fixed behavior when action resources are absent and ensured removed actions are no longer accessible.
  • Chores
    • Updated internal collection choices and refreshed related test coverage.

Walkthrough

This PR replaces ECS-based action storage with Resources, IdRegistry, and ActionTable, then updates pipeline baking, sampling, timeline queuing, dependency usage, tests, and benchmarks for the new storage model.

Changes

ActionWorld to ActionTable migration

Layer / File(s) Summary
Dependencies and resource storage
Cargo.toml, crates/motiongfx/Cargo.toml, crates/motiongfx/src/resources.rs, crates/motiongfx/src/registry.rs
Adds typarena and hashbrown, narrows the std feature, introduces typed Resources, and switches collection usage to hashbrown.
IdRegistry and cleanup infrastructure
crates/motiongfx/src/action/id_registry.rs
Adds type-erased subject IDs, reference-counted bidirectional lookup, cleanup callbacks, and unit tests.
Action data types and module split
crates/motiongfx/src/action.rs
Moves table and registry implementations into submodules and removes ECS component metadata from action value and storage types.
ActionTable and builder APIs
crates/motiongfx/src/action/table.rs
Adds typed-column action storage, generational IDs, cleanup-aware removal, interpolation/easing builders, and lifecycle tests.
Column-based baking and sampling
crates/motiongfx/src/pipeline.rs
Rewrites baking and sampling around ActionTable columns, queued sample modes, optional easing, and mock-world tests.
Timeline queue and integration wiring
crates/motiongfx/src/timeline.rs
Replaces timeline action storage with ActionTable, stores sample modes in queues, and updates compilation, sampling, and queue reset behavior.
Manager and track integration
crates/bevy_motiongfx/src/manager.rs, crates/motiongfx/src/track.rs
Uses infallible manager resource scoping and replaces ECS entity IDs in track tests with a local subject ID.
Action storage benchmarks
crates/motiongfx/benches/action_storage.rs
Adds Criterion benchmarks for build, bake, and scrub phases across single-type and mixed-type timelines.

Estimated code review effort: 4 (Complex) | ~75 minutes

Sequence Diagram(s)

sequenceDiagram
    participant Timeline
    participant ActionTable
    participant Pipeline
    participant SubjectWorld

    Timeline->>ActionTable: add actions and targets
    Timeline->>Timeline: queue ActionId and SampleMode
    Timeline->>Pipeline: sample queued actions
    Pipeline->>ActionTable: read segments, interpolation, keys, and easing
    Pipeline->>SubjectWorld: apply computed target
Loading

Possibly related PRs

Suggested reviewers: Jaghov

Poem

A rabbit hopped through tables new,
Where action rows and IDs grew,
Resources hold the keys with care,
Pipelines sample everywhere,
Hashbrown maps now guide the flight—
The burrow builds by day and night! 🐇

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly states the main migration from bevy_ecs to typarena in motiongfx.
Description check ✅ Passed The description is related to the change set because it points to the typarena project used in the migration.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@nixonyh nixonyh marked this pull request as ready for review July 4, 2026 05:52
@nixonyh nixonyh changed the title Migrate motiongfx off bevy_ecs onto typarena Migrate motiongfx off bevy_ecs onto typarena Jul 4, 2026
@nixonyh

nixonyh commented Jul 7, 2026

Copy link
Copy Markdown
Member Author

@Jaghov benchmark report:

Benchmark: ECS vs typarena action storage

Comparison of the two action-storage backends for motiongfx, measured through the shared public timeline API so the only variable is the storage layer.

ECS baseline main @ b703a02 (bevy_ecs)
Candidate nixon/typarena (typarena + bake ColumnId cache + queue-driven sampling)
Machine Apple M5 Max, 18 cores
Toolchain rustc 1.95.0, --release (bench profile)
Harness criterion 0.5, default sampling (100 samples)
Date 2026-07-07

Method

A single benchmark (crates/motiongfx/benches/timeline.rs) drives the public motiongfx API — build → bake → queue+sample. That API is byte-for-byte identical on both branches, so the same file compiles and runs unmodified on each worktree and any delta is attributable purely to the action-storage backend (bevy_ecs vs typarena), not API differences.

The subject world is a bare Vec (no bevy/vello/velyst), isolating motiongfx's internals from any rendering or host-container cost.

Three phases are measured at N ∈ {100, 1000, 10000} subjects:

  • build — construct + compile a timeline from scratch.
  • bake — recompute every action's start/end Segment.
  • scrub — a 60-frame playback sweep doing queue_actions +
    sample_queued_actions each frame (the per-frame hot path).

Each phase runs in two scenes:

  • single-type — one f32 field: one pipeline, one column set.
  • mixed (mixed_*) — one subject with four distinct value types (f32, V2, V3, V4): four pipelines, four heterogeneous column sets, a four-wide archetype in ECS. mixed does 4·N actions per N subjects.

Results

Times are criterion medians for typarena. Δ is typarena relative to the ECS baseline (negative = typarena faster).

Single-type (1 pipeline)

Phase N typarena Δ vs ECS
build 100 18.68 µs −33.1%
build 1 000 186.9 µs −14.5%
build 10 000 1.771 ms −15.8%
bake 100 1.715 µs −36.0%
bake 1 000 16.45 µs −37.5%
bake 10 000 216.1 µs −23.8%
scrub 100 242.4 µs −40.4%
scrub 1 000 2.575 ms −31.0%
scrub 10 000 32.57 ms −21.0%

Mixed — 4 value types (4 pipelines, 4·N actions)

Phase N typarena Δ vs ECS
mixed_build 100 72.18 µs −26.7%
mixed_build 1 000 758.6 µs −12.2%
mixed_build 10 000 8.308 ms −7.8%
mixed_bake 100 13.53 µs −21.3%
mixed_bake 1 000 135.0 µs −21.8%
mixed_bake 10 000 1.597 ms −16.6%
mixed_scrub 100 979.9 µs −40.2%
mixed_scrub 1 000 11.48 ms −26.3%
mixed_scrub 10 000 158.4 ms −3.6%

typarena is faster than ECS on all 18 cells.

Reproducing

Run on a quiet, cool machine. A shared CRITERION_HOME enables the automatic change: / p-value diff.

# ECS baseline (main worktree)
cd ../motiongfx-main
CRITERION_HOME=/tmp/mgfx-crit \
  cargo bench -p motiongfx --bench action_storage -- --save-baseline ecs

# typarena, auto-diffed against the baseline
cd ../motiongfx            # the nixon/typarena worktree
CRITERION_HOME=/tmp/mgfx-crit \
  cargo bench -p motiongfx --bench action_storage -- --baseline ecs

HTML reports are written to $CRITERION_HOME/report/index.html. To probe the type-diversity axis harder, add more field types to Widget in benches/timeline.rs.

@coderabbitai coderabbitai Bot 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.

🧹 Nitpick comments (1)
crates/motiongfx/benches/action_storage.rs (1)

139-178: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Throughput undercounts actual sampled frames.

Throughput::Elements is set to n * FRAMES (or n * 4 * FRAMES for mixed), but the timed closure samples FRAMES + 1 stepped frames (0..=FRAMES) plus one extra rewind sample — one more sample_queued_actions call than accounted for. This slightly understates elements/sec in the report, though it's a consistent bias so relative branch comparisons remain valid.

Also applies to: 369-404

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/motiongfx/benches/action_storage.rs` around lines 139 - 178, The
benchmark throughput accounting in bench_scrub undercounts the actual sampled
work because the loop in bench_scrub performs FRAMES + 1 sampled frames plus an
extra rewind sample, while Throughput::Elements only counts n * FRAMES. Update
the throughput units to match the real number of sample_queued_actions calls
made inside the timed closure, and apply the same correction to the mixed
benchmark block referenced in the comment so the reported elements/sec reflects
the full sampled workload.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@crates/motiongfx/benches/action_storage.rs`:
- Around line 139-178: The benchmark throughput accounting in bench_scrub
undercounts the actual sampled work because the loop in bench_scrub performs
FRAMES + 1 sampled frames plus an extra rewind sample, while
Throughput::Elements only counts n * FRAMES. Update the throughput units to
match the real number of sample_queued_actions calls made inside the timed
closure, and apply the same correction to the mixed benchmark block referenced
in the comment so the reported elements/sec reflects the full sampled workload.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 1006c13e-43d3-4b93-ad5a-be2a0b6903c2

📥 Commits

Reviewing files that changed from the base of the PR and between 0cba163 and f5397a6.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (5)
  • crates/motiongfx/Cargo.toml
  • crates/motiongfx/benches/action_storage.rs
  • crates/motiongfx/src/action/table.rs
  • crates/motiongfx/src/pipeline.rs
  • crates/motiongfx/src/timeline.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/motiongfx/Cargo.toml

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.

1 participant