Reconciliation (allowing state-preserving hot-reload) #36
+764
−82
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Based on: bevyengine#20158
Depends on: #35 (commit included in this branch)
Objective
Reconciliation is the process of incrementally updating entities, components,
and relationships from a
ResolvedSceneby storing state from previous reconciliations.This idea is based on previous work by @NthTensor in
i-cant-believe-its-not-bsn.Since this may be a bit controversial to merge onto the "main" BSN branch, here's how you can try it out today:
I'll do my best to keep this up-to-date with
cart/next-gen-scenes.How does it work?
When a scene is reconciled on an entity, it will:
ReconcileAnchors or otherwise spawn new entitiesNamecomponent - which makes them identifiable among their siblings. This also works with#MyEntityNamesyntax inbsn!.ReconcileReceiptcomponent on the entity.Why is it useful?
Non-destructive hot reloads
The obvious use case for an algorithm like this would be hot reloading of scenes. When using reconciliation, scenes can be hot reloaded while maintaing state that isn't part of the explicit scene output.
Demo from Discord: https://discord.com/channels/691052431525675048/1264881140007702558/1396963952050835527
Reactivity
On the web, reconciliation is a common technique for updating the DOM from an intermediate representation (aka VDOM). This is sometimes referred to as "coarse grained", and does have its performance penalties compared to more fine grained surgical updates. An upside to reconciliation is that it doesn't require a lot of special casing for templates - scenes can be built using familiar rust expressions.
The algorithm included here is not reactivity by itself, but it could potentially be used for future experiments in a number of ways, for example:
The "state vs output" problem
One issue when using reconciliation (or reactivity in general) which became very clear when trying to reconcile
bevy_featherswidgets, is that we are storing mutable state and scene output on the same entities. It is convenient to include the internal state components in thebsn!to instantiate them. This poses a problem when using reconciliation, as the state components would be overwritten with their defaults on every reconciliation.One solution to this problem is to use required components for internal state. That way those components are "implicit" and won't be overwritten on each reconciliation. I have converted the internal state of all feathers controls to use this approcach, making them usable with reconciliation.
Another solution we could consider would be syntax in
bsn!for marking components as implicit/required, hinting that they should only be inserted once.Implementation caveats/TODOs
Templateon every reconciliation. This may cause issues with implementations that assume the template runs on a freshly spawned entity. When testing it withon/OnTemplatehowever,it looks like it doesn't create any duplicate observersOnHandlecomponent to ensure observers of a certain type are added once, and removed when that component is removed. Seems to work well with reconciliation.spawn_scene. Should by inserted as a single (dynamic) bundle to minimze archetype moves.Names are not handled and causes chaos - fall back to auto-inc and log a warning?Example: Subsecond hot reload
This is the example from the demo on Discord, try it out with subsecond hot patching enabled!