Skip to content

Construct the shared animation backend after the UIManager has a delegate - #57817

Open
zeyap wants to merge 2 commits into
react:mainfrom
zeyap:export-D114759538
Open

Construct the shared animation backend after the UIManager has a delegate#57817
zeyap wants to merge 2 commits into
react:mainfrom
zeyap:export-D114759538

Conversation

@zeyap

@zeyap zeyap commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Summary:

Changelog:

[Internal] - Construct the shared animation backend after the UIManager has a delegate

AnimationBackend's constructor calls UIManager::addOnSurfaceStartCallback so that AnimatedPropsRegistry::initializeSurface runs for each surface as it starts. That call is a no-op unless the UIManager already has a delegate:

void UIManager::addOnSurfaceStartCallback(
    UIManagerDelegate::OnSurfaceStartCallback&& callback) {
  if (delegate_ != nullptr) {
    delegate_->uiManagerShouldAddOnSurfaceStartCallback(std::move(callback));
  }
}

Scheduler's constructor was building the backend immediately after constructing the UIManager and roughly seventy lines before uiManager->setDelegate(this), so the callback was dropped every time and Scheduler::onSurfaceStartCallbacks_ never received it. AnimatedPropsRegistry::update skips surfaces missing from surfaceContexts_, so correctness was left resting on getMap() default-constructing the entry via operator[] — that is, on a commit hook happening to run before the first animated update on a freshly started surface. When that ordering does not hold, early animated updates on a new surface are dropped.

Move the backend construction to just after setDelegate, and add a comment recording the ordering constraint. This is still well before any surface can start, so the registry is populated for every surface from the first frame.

The neighbouring getShadowTreeRegistry().enumerate(...) in the same constructor is also dead today — the UIManager is two lines old and its registry is necessarily empty — but it is left in place deliberately. It mirrors the enumerate-then-register pattern in NativeAnimatedNodesManagerProvider and ViewTransitionModule, both of which are constructed lazily and do need it, and it keeps AnimationBackend's constructor correct if it is ever moved to a lazy call site.

Differential Revision: D114759538

zeyap added 2 commits August 4, 2026 11:34
Summary:
`AnimationBackendCommitHook::shadowTreeWillCommit` and `AnimatedMountingOverrideDelegate::pullTransaction` are the two mutually exclusive implementations of "apply animated props on the way to the screen": the former runs when the shared animated backend is enabled, the latter when it is not. Neither was instrumented, so the cost of one could not be compared against the cost of the other.

Add a `TraceSection` to each, with the size of the work as an argument — `numAnimatedFamilies` for the commit hook (it scales with the number of animated nodes on the surface) and `numMutations` for the mounting override delegate (it scales with the size of the mount transaction). The commit hook's section starts after the early-out for surfaces with no animated families, so an idle surface still costs nothing and does not emit a slice.

`animationbackend` gains a direct dependency on `cxxreact:bridge` for `TraceSection.h`.

Changelog: [Internal]

Differential Revision: D114759537
…gate

Summary:
## Changelog:

[Internal] - Construct the shared animation backend after the UIManager has a delegate

`AnimationBackend`'s constructor calls `UIManager::addOnSurfaceStartCallback` so that `AnimatedPropsRegistry::initializeSurface` runs for each surface as it starts. That call is a no-op unless the `UIManager` already has a delegate:

```
void UIManager::addOnSurfaceStartCallback(
    UIManagerDelegate::OnSurfaceStartCallback&& callback) {
  if (delegate_ != nullptr) {
    delegate_->uiManagerShouldAddOnSurfaceStartCallback(std::move(callback));
  }
}
```

`Scheduler`'s constructor was building the backend immediately after constructing the `UIManager` and roughly seventy lines before `uiManager->setDelegate(this)`, so the callback was dropped every time and `Scheduler::onSurfaceStartCallbacks_` never received it. `AnimatedPropsRegistry::update` skips surfaces missing from `surfaceContexts_`, so correctness was left resting on `getMap()` default-constructing the entry via `operator[]` — that is, on a commit hook happening to run before the first animated update on a freshly started surface. When that ordering does not hold, early animated updates on a new surface are dropped.

Move the backend construction to just after `setDelegate`, and add a comment recording the ordering constraint. This is still well before any surface can start, so the registry is populated for every surface from the first frame.

The neighbouring `getShadowTreeRegistry().enumerate(...)` in the same constructor is also dead today — the `UIManager` is two lines old and its registry is necessarily empty — but it is left in place deliberately. It mirrors the enumerate-then-register pattern in `NativeAnimatedNodesManagerProvider` and `ViewTransitionModule`, both of which are constructed lazily and do need it, and it keeps `AnimationBackend`'s constructor correct if it is ever moved to a lazy call site.

Differential Revision: D114759538
@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Aug 4, 2026
@meta-codesync

meta-codesync Bot commented Aug 4, 2026

Copy link
Copy Markdown

@zeyap has exported this pull request. If you are a Meta employee, you can view the originating Diff in D114759538.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. meta-exported p: Facebook Partner: Facebook Partner

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant