Replies: 1 comment
|
I totally agree and support the idea. Using rkyv with a zero-copy IPC (like rtipc) and a completion-based dual-ring buffers async runtime like Compio would be the best for performance and resources consumption, and of course, I think the best would be using something as Slint or, even better, Xilem (when it's stable enough). |
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Should Bevy's editor be a thin host over a WIT extension boundary?
This post is trying to do one specific thing: argue that the direction in the Vision and Architecture docs has a more thorough version worth someone qualified investigating. I'm not proposing a design. I'm raising the question of whether someone should design this. Governance, reflection internals, UI contribution, and performance characteristics are real questions, but I'm not the person to answer them. That's the work I'm hoping to make visible, not work I'm claiming to have done.
With that scoping clear, the claim:
Bevy's editor should be a thin host over a WIT extension boundary, so the existing crate ecosystem can project itself into editor form rather than the editor becoming a parallel ecosystem of its own.
Cargo came first
This is the part I think matters most.
Other game engines built their editor and their ecosystem together. The editor came first or alongside the engine, and the plugin ecosystem grew out of it, with extensions filling gaps in what the editor offered. The editor is the foundation; the ecosystem is downstream.
Bevy is in the opposite situation. Bevy already has a deep, mature ecosystem with hundreds of crates, expressed as pure code, distributed through cargo. That ecosystem grew without an editor and doesn't need one to function. Any editor arriving now is arriving into something that already exists.
This inverts the usual relationship. The architecture doc partially recognizes it: "self-contained
Plugins reusable without the editor binary" gestures at the inversion. But there's a more thorough version. The editor isn't just a thing that uses plugins; the editor is downstream of the ecosystem. It's a window into cargo and crates.io, not a parallel source of tools.A thin host with a WIT extension boundary is what makes that directionality real. Crates implement against the host's WIT contract, the host stays small, and the editor remains derivative of the ecosystem rather than competing with it. Component-defining crates ship companion extensions providing editor UI for their own types: Rapier knows about
RigidBody, the host doesn't need to.Three open questions, one mechanism
The architecture doc names three open questions:
A WIT boundary answers these together. Adding or removing functionality becomes loading or unloading a component, no fork required. The Rust toolchain becomes optional, since extensions can be authored in any language that targets WIT. User-defined types become a question of what's in the reflection interface, not whether the host has heard of them.
I think this is the strongest single argument for the approach: not that any one of these problems demands WIT, but that one mechanism dissolves three of them at once.
The parallel-editors problem is a symptom, not a coincidence
Jackdaw and bevy_editor_prototypes (now archived but it'll be my example) both exist, both are structured as crates with extension APIs, and neither is monolithic. What they can't currently do is share extension infrastructure without coupling to each other's Rust types. If you write an inspector panel for Jackdaw, you can't drop it into bevy_editor_prototypes; the trait it implements lives in Jackdaw's crate.
This isn't anyone's wrong decision. It's a consequence of native-trait extension, which is the only path currently available.
I think the bigger point is that the current state of editor work in Bevy, multiple parallel efforts rather than shared infrastructure, isn't a coincidence. It's what happens when the smallest viable unit of contribution is a whole editor. Everywhere else in the Bevy ecosystem, "going ahead with what interests you and what your workflow requires" means writing the crate you need and publishing it. People scratch their itches at the granularity of crates, and the ecosystem composes. Editor work doesn't follow that pattern because there's no extension surface to publish into. The unit of useful contribution is much larger than it should be, so contributors who want to ship something end up shipping editors.
A WIT boundary would make editor contributions look like the rest of the ecosystem: scoped, composable, distributable as units smaller than a whole application. The proposal isn't really "Bevy should have one editor instead of many." It's "the unit of editor contribution should be small enough that the question of one-versus-many stops being interesting." If Jackdaw and bevy_editor_prototypes implemented the same WIT, the question of which one wins matters less, because the contributions stack up against the same target either way.
Why not native dynamic plugins?
Bevy already tried that path.
bevy_dynamic_pluginwas deprecated in 0.14 and removed in 0.15. The reasons were concrete:TypeIdisn't stable across compilation unitsctorunconditionallyThe reasons it was removed are roughly the reasons WIT works. WIT provides a stable, versioned, language-agnostic ABI by construction, and that's also what enables the toolchain-independence property.
The hard part: reflection across the boundary
An editor's whole job is reflecting and manipulating ECS state. If the reflection interface across the boundary is awkward, chatty, or forces a round-trip per field, everything else collapses.
This is the question I most want someone qualified to look at. The encouraging thing is that Bevy already has
bevy_reflect. A WIT reflection interface wouldn't have to invent reflection from scratch; it could mirror a model that's already battle-tested inside the engine. But how it should mirror it (what gets pushed across the boundary eagerly versus lazily, how change detection works, how mutations batch, what queries look like) is real design work, and it's the part I can't do.If someone with deep
bevy_reflectcontext sees a fundamental obstacle, the rest of the proposal doesn't survive it.The other hard part: how extensions contribute UI
A gap I want to flag explicitly, because I didn't address it earlier and should have: editor extensions are mostly UI. An inspector panel, a gizmo, an asset browser tab, a command palette entry: these are all widget trees, and the host needs some way to know what an extension wants to draw. If extensions can't contribute UI cleanly, the host has to ship every widget itself, which collapses back into the editor-as-monolith model the proposal is trying to avoid.
I don't know the right answer here, but the question has a few plausible shapes worth investigating:
Schema in component metadata. WASM components can carry arbitrary custom sections, so an extension could declare its widgets statically: "I provide an inspector for type path X, with this layout." The host reads metadata without instantiating the component, registers widgets up front, and only calls into the component for behavior. Cheapest option, maps well to extensions that mostly project component fields onto standard widgets, but rigid.
Widget tree returned across WIT. The component exposes a function returning a tree of widget descriptions, which the host renders. More flexible than static schemas because the tree can depend on runtime state, but every redraw potentially crosses the boundary.
Immediate-mode drawing via host primitives. The host exposes drawing primitives over WIT, the component calls them. Maximum flexibility, maximum chattiness, probably wrong for the same reasons chatty reflection would be wrong.
Hybrid. Declarative schemas for registration and structure, function calls only for behavior and dynamic content. Roughly what Zed and Lapce do, and probably the direction worth starting from.
I'd put this as the second most load-bearing technical question after reflection. The two interact: a declarative widget schema that references reflected component fields needs both interfaces to agree on how types are addressed. They probably want to be designed together.
Performance
A WASM boundary adds overhead. Hot paths stay native: engine, renderer, ECS itself never cross. What crosses are editor operations bounded by interaction rather than frame budget. "Interaction-bounded" isn't a free pass, though: selecting thousands of entities and dragging them is a real workload, and chatty reflection or chatty redraws would hurt there. This is downstream of the two questions above.
Prior art
register-component,query-result, and guest systems. Runtime ECS-as-components is a different problem from editor reflection, but it's encouraging that the basic plumbing works.This isn't a new conversation in Bevy either. Multi-editor questions came up in [#7100](#7100) and [#5043](#5043), and Cart suggested WASM plugin support in [#1103](#1103) before the Component Model existed. I'm trying to connect those older threads to current tooling.
Constraint check
The architecture doc forbids editor dependencies on crates that themselves depend on Bevy.
wasmtimeandwit-bindgendon't, so this passes. This is also orthogonal to "the editor doesn't inspect the running game": WIT here is for host/extension communication, not editor/game communication.What I'm not the person to figure out
In rough order of how much they'd determine whether this works:
bevy_dynamic_pluginlessons and toolchain-independence, but reasonable people could disagree.If the cargo-came-first framing is right, reflection is tractable, and UI contribution has a clean answer, I think the rest is engineering. If any of those three fails, none of this works. I'd rather put the question up for argument than sit on it.
All reactions