Skip to content

Reduce animation registry copying and allocation (#58775) - #58775

Open
bartlomiejbloniarz wants to merge 4 commits into
mainfrom
export-D122570619
Open

bartlomiejbloniarz wants to merge 4 commits into
mainfrom
export-D122570619

Conversation

@bartlomiejbloniarz

@bartlomiejbloniarz bartlomiejbloniarz commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Summary:

For every raw update, AnimatedPropsRegistry::update copied the incoming props before merging them, and it built a full typed view-props snapshot even when no typed prop was animated. It now merges from the incoming folly::dynamic without copying it and allocates typed props only when a typed update arrives. The merge semantics and the caller's input are unchanged.

On a low-end Android phone (4× Cortex-A53) with 12 views animating opacity, the registry update per frame dropped from about 0.22 ms to 0.13 ms.

Changelog:

[Internal]

Reviewed By: zeyap

Differential Revision: D122570619

@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 Sep 30, 2026
@facebook-github-tools facebook-github-tools Bot added p: Software Mansion Partner: Software Mansion Partner p: Facebook Partner: Facebook labels Sep 30, 2026
@meta-codesync

meta-codesync Bot commented Sep 30, 2026

Copy link
Copy Markdown

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

@meta-codesync
meta-codesync Bot force-pushed the export-D122570619 branch from 7620e3a to c5d2e33 Compare October 2, 2026 12:07
@meta-codesync meta-codesync Bot changed the title Reduce animation registry copying and allocation Reduce animation registry copying and allocation (#58775) Oct 2, 2026
meta-codesync Bot pushed a commit that referenced this pull request Oct 2, 2026
Summary:
Pull Request resolved: #58775

For every raw update, `AnimatedPropsRegistry::update` copied the incoming props before merging them, and it built a full typed view-props snapshot even when no typed prop was animated. It now merges from the incoming `folly::dynamic` without copying it and allocates typed props only when a typed update arrives. The merge semantics (Folly's merge-patch) and the caller's input are unchanged.

On a low-end Android phone (4× Cortex-A53) with 12 views animating opacity, the registry update per frame dropped from about 0.22 ms to 0.13 ms.

The new registry tests are excluded from React-Fabric's CocoaPods and Swift Package Manager production sources.

## Changelog:

[General] [Changed] - Reduce animation registry copying and allocation

Differential Revision: D122570619
@meta-codesync
meta-codesync Bot force-pushed the export-D122570619 branch from c5d2e33 to 8e34e80 Compare October 2, 2026 13:57
meta-codesync Bot pushed a commit that referenced this pull request Oct 2, 2026
Summary:
Pull Request resolved: #58775

For every raw update, `AnimatedPropsRegistry::update` copied the incoming props before merging them, and it built a full typed view-props snapshot even when no typed prop was animated. It now merges from the incoming `folly::dynamic` without copying it and allocates typed props only when a typed update arrives. The merge semantics and the caller's input are unchanged.

On a low-end Android phone (4× Cortex-A53) with 12 views animating opacity, the registry update per frame dropped from about 0.22 ms to 0.13 ms.

The new registry tests are excluded from React-Fabric's CocoaPods and Swift Package Manager production sources.

## Changelog:

[Internal]

Differential Revision: D122570619
meta-codesync Bot pushed a commit that referenced this pull request Oct 2, 2026
Summary:
Pull Request resolved: #58775

For every raw update, `AnimatedPropsRegistry::update` copied the incoming props before merging them, and it built a full typed view-props snapshot even when no typed prop was animated. It now merges from the incoming `folly::dynamic` without copying it and allocates typed props only when a typed update arrives. The merge semantics and the caller's input are unchanged.

On a low-end Android phone (4× Cortex-A53) with 12 views animating opacity, the registry update per frame dropped from about 0.22 ms to 0.13 ms.

The new registry tests are excluded from React-Fabric's CocoaPods and Swift Package Manager production sources.

## Changelog:

[Internal]

Differential Revision: D122570619
@meta-codesync
meta-codesync Bot force-pushed the export-D122570619 branch from 8e34e80 to 4f6243b Compare October 2, 2026 14:01
meta-codesync Bot pushed a commit that referenced this pull request Oct 5, 2026
Summary:
Pull Request resolved: #58775

For every raw update, `AnimatedPropsRegistry::update` copied the incoming props before merging them, and it built a full typed view-props snapshot even when no typed prop was animated. It now merges from the incoming `folly::dynamic` without copying it and allocates typed props only when a typed update arrives. The merge semantics and the caller's input are unchanged.

On a low-end Android phone (4× Cortex-A53) with 12 views animating opacity, the registry update per frame dropped from about 0.22 ms to 0.13 ms.

The new registry tests are excluded from React-Fabric's CocoaPods and Swift Package Manager production sources.

## Changelog:

[Internal]

Differential Revision: D122570619
@meta-codesync
meta-codesync Bot force-pushed the export-D122570619 branch from 4f6243b to e58bcbc Compare October 5, 2026 16:34
meta-codesync Bot pushed a commit that referenced this pull request Oct 5, 2026
Summary:
Pull Request resolved: #58775

For every raw update, `AnimatedPropsRegistry::update` copied the incoming props before merging them, and it built a full typed view-props snapshot even when no typed prop was animated. It now merges from the incoming `folly::dynamic` without copying it and allocates typed props only when a typed update arrives. The merge semantics and the caller's input are unchanged.

On a low-end Android phone (4× Cortex-A53) with 12 views animating opacity, the registry update per frame dropped from about 0.22 ms to 0.13 ms.

The new registry tests are excluded from React-Fabric's CocoaPods and Swift Package Manager production sources.

## Changelog:

[Internal]

Differential Revision: D122570619
@meta-codesync
meta-codesync Bot force-pushed the export-D122570619 branch 2 times, most recently from 6c32ed4 to 3c2855d Compare October 6, 2026 14:21
meta-codesync Bot pushed a commit that referenced this pull request Oct 6, 2026
Summary:
Pull Request resolved: #58775

For every raw update, `AnimatedPropsRegistry::update` copied the incoming props before merging them, and it built a full typed view-props snapshot even when no typed prop was animated. It now merges from the incoming `folly::dynamic` without copying it and allocates typed props only when a typed update arrives. The merge semantics and the caller's input are unchanged.

On a low-end Android phone (4× Cortex-A53) with 12 views animating opacity, the registry update per frame dropped from about 0.22 ms to 0.13 ms.

## Changelog:

[Internal]

Reviewed By: zeyap

Differential Revision: D122570619
@meta-codesync
meta-codesync Bot force-pushed the export-D122570619 branch from 3c2855d to 7cbb646 Compare October 6, 2026 15:35
meta-codesync Bot pushed a commit that referenced this pull request Oct 6, 2026
Summary:
Pull Request resolved: #58775

For every raw update, `AnimatedPropsRegistry::update` copied the incoming props before merging them, and it built a full typed view-props snapshot even when no typed prop was animated. It now merges from the incoming `folly::dynamic` without copying it and allocates typed props only when a typed update arrives. The merge semantics and the caller's input are unchanged.

On a low-end Android phone (4× Cortex-A53) with 12 views animating opacity, the registry update per frame dropped from about 0.22 ms to 0.13 ms.

## Changelog:

[Internal]

Reviewed By: zeyap

Differential Revision: D122570619
meta-codesync Bot pushed a commit that referenced this pull request Oct 6, 2026
Summary:
Pull Request resolved: #58775

For every raw update, `AnimatedPropsRegistry::update` copied the incoming props before merging them, and it built a full typed view-props snapshot even when no typed prop was animated. It now merges from the incoming `folly::dynamic` without copying it and allocates typed props only when a typed update arrives. The merge semantics and the caller's input are unchanged.

On a low-end Android phone (4× Cortex-A53) with 12 views animating opacity, the registry update per frame dropped from about 0.22 ms to 0.13 ms.

## Changelog:

[Internal]

Reviewed By: zeyap

Differential Revision: D122570619
@meta-codesync
meta-codesync Bot force-pushed the export-D122570619 branch from 7cbb646 to 058405c Compare October 6, 2026 15:40
Bartlomiej Bloniarz added 3 commits October 6, 2026 09:03
… animates layout (#58772)

Summary:
Pull Request resolved: #58772

The shared animation backend decided per surface whether a frame's animated props go through a shadow tree commit or straight to the mounted views. As soon as one view animated a layout prop, every animated view on that surface went through the commit path for the whole animation, including views that only animate `transform` or `opacity`. Besides the extra commit work, on Android this moved those views from `updatePropsSynchronously` to regular mounts. There the synchronous mount props cache (`overrideBySynchronousMountPropsAtMountingAndroid`) replaced the incoming transform with its last synchronously written value, so the view froze while the other view's layout animation ran and jumped when it ended.

The decision is now made per view. `applySurfaceUpdates` splits a surface's mutations, keyed by view tag, into views with layout updates, which go through `commitUpdates`, and the rest, which are applied synchronously. Views with layout updates are also written synchronously, as the non-shared Animated path already does, so a view that starts animating a layout prop while animating `opacity` doesn't freeze its opacity behind the cache either. `packAnimatedProps`, which only serves the synchronous path, now skips typed layout props instead of throwing, since views with layout updates reach it too and the commit applies those props. `AnimationMutation` and `AnimationMutations` move to `AnimationMutation.h`, still included by `AnimationBackend.h`. `AnimatedPropsRegistry::update` reads the frame's batches instead of the merged per-surface map. When the same view receives several mutations in one frame, they are merged, so the mounted view and the registry stay in sync. Raw animated props are now merged per key, like the animation drivers merge them, instead of with merge-patch, which dropped `null` values meant to reset a prop. `RawProps::getDynamic` exposes a dynamic source without copying it.

The new `animationbackend` C++ tests are excluded from React-Fabric's CocoaPods and Swift Package Manager production sources.

## Changelog:

[General] [Fixed] - Non-layout props animated on the shared animation backend no longer freeze while the same view or another view on the surface animates a layout prop

Differential Revision: D122570616
Summary:
Pull Request resolved: #58773

The animation backend hands its synchronous updates to the platform one view at a time. This adds `UIManager::synchronouslyUpdateAnimatedProps` and a matching `SchedulerDelegate` call that receive a whole frame's updates at once, continuing #56771. The backend uses it when `optimizedAnimatedPropUpdates` is on, and that flag's description now only promises what is implemented. The default `SchedulerDelegate` implementation forwards to the existing per-view update, so platforms that don't override it are unaffected. `UIManagerDelegate` gains a required method, implemented by `Scheduler`.

## Changelog:

[Internal]

Reviewed By: zeyap

Differential Revision: D122570617
Summary:
Pull Request resolved: #58774

With the batched delegate from the previous diff, Android applied the backend's synchronous updates with one JNI call and one `ReadableNativeMap` per view per frame. When `optimizedAnimatedPropUpdates` is on, a surface's updates now go to the UI thread in a single call: `FabricMountingManager::synchronouslyUpdateAnimatedProps` encodes them into int and double buffers, and `BatchedAnimatedPropsMountItem` decodes and applies them. This continues the encoder and mount item from #56468. Views that are not mounted yet are still queued.

A view is packed only when every prop it carries has a command. Unsupported props, null resets, platform colors, malformed values and typed props send the whole view through the existing `packAnimatedProps` serializer as a map instead. The commands cover the props Reanimated applies synchronously on Android, following the parity review in #56468 (comment). Angles are converted to radians in C++, and radii and translations keep their px or percent units. The C++ and Kotlin command tables are checked against each other by an instrumentation test that runs a batch through the real encoder, JNI call and decoder.

On a low-end Android phone (4× Cortex-A53) with 12 views animating opacity, UI-thread time per frame's synchronous batch dropped from about 6.5 ms to 3.4 ms.

## Changelog:

[Internal]

Differential Revision: D122570618
meta-codesync Bot pushed a commit that referenced this pull request Oct 6, 2026
Summary:
Pull Request resolved: #58775

For every raw update, `AnimatedPropsRegistry::update` copied the incoming props before merging them, and it built a full typed view-props snapshot even when no typed prop was animated. It now merges from the incoming `folly::dynamic` without copying it and allocates typed props only when a typed update arrives. The merge semantics and the caller's input are unchanged.

On a low-end Android phone (4× Cortex-A53) with 12 views animating opacity, the registry update per frame dropped from about 0.22 ms to 0.13 ms.

## Changelog:

[Internal]

Reviewed By: zeyap

Differential Revision: D122570619
@meta-codesync
meta-codesync Bot force-pushed the export-D122570619 branch from 058405c to 0b5fecf Compare October 6, 2026 16:17
Summary:
Pull Request resolved: #58775

For every raw update, `AnimatedPropsRegistry::update` copied the incoming props before merging them, and it built a full typed view-props snapshot even when no typed prop was animated. It now merges from the incoming `folly::dynamic` without copying it and allocates typed props only when a typed update arrives. The merge semantics and the caller's input are unchanged.

On a low-end Android phone (4× Cortex-A53) with 12 views animating opacity, the registry update per frame dropped from about 0.22 ms to 0.13 ms.

## Changelog:

[Internal]

Reviewed By: zeyap

Differential Revision: D122570619
@meta-codesync
meta-codesync Bot force-pushed the export-D122570619 branch from 0b5fecf to 30a7cf0 Compare October 6, 2026 16:22

This branch has not been deployed

No deployments
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 p: Software Mansion Partner: Software Mansion Partner

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant