Skip to content

Commit 674794c

Browse files
Bartlomiej Bloniarzmeta-codesync[bot]
authored andcommitted
Add a batched scheduler delegate call for animated prop updates (#58773)
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. 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: [General] [Added] - Add a batched scheduler delegate call for animated prop updates behind `optimizedAnimatedPropUpdates` Differential Revision: D122570617
1 parent 5e636ba commit 674794c

18 files changed

Lines changed: 132 additions & 0 deletions

‎packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.cpp‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,12 @@ void AnimationBackend::commitUpdates(
253253

254254
void AnimationBackend::synchronouslyUpdateProps(
255255
const std::unordered_map<Tag, AnimatedProps>& updates) {
256+
if (ReactNativeFeatureFlags::optimizedAnimatedPropUpdates()) {
257+
if (auto uiManager = uiManager_.lock()) {
258+
uiManager->synchronouslyUpdateAnimatedProps(updates);
259+
}
260+
return;
261+
}
256262
for (auto& [tag, animatedProps] : updates) {
257263
// TODO: We shouldn't repack it into dynamic, but for that a rewrite
258264
// of synchronouslyUpdateViewOnUIThread is needed

‎packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.cpp‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,13 @@ void Scheduler::uiManagerShouldSynchronouslyUpdateViewOnUIThread(
378378
}
379379
}
380380

381+
void Scheduler::uiManagerShouldSynchronouslyUpdateAnimatedProps(
382+
const std::unordered_map<Tag, AnimatedProps>& updates) {
383+
if (delegate_ != nullptr) {
384+
delegate_->schedulerShouldSynchronouslyUpdateAnimatedProps(updates);
385+
}
386+
}
387+
381388
void Scheduler::uiManagerDidUpdateShadowTree(
382389
const std::unordered_map<Tag, folly::dynamic>& tagToProps) {
383390
if (delegate_ != nullptr) {

‎packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ class Scheduler final : public UIManagerDelegate {
9797
bool isJSResponder,
9898
bool blockNativeResponder) override;
9999
void uiManagerShouldSynchronouslyUpdateViewOnUIThread(Tag tag, const folly::dynamic &props) override;
100+
void uiManagerShouldSynchronouslyUpdateAnimatedProps(const std::unordered_map<Tag, AnimatedProps> &updates) override;
100101
void uiManagerDidUpdateShadowTree(const std::unordered_map<Tag, folly::dynamic> &tagToProps) override;
101102
void uiManagerDidCaptureViewSnapshot(Tag tag, SurfaceId surfaceId) override;
102103
void uiManagerDidSetViewSnapshot(Tag sourceTag, Tag targetTag, SurfaceId surfaceId) override;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#include "SchedulerDelegate.h"
9+
10+
#include <react/renderer/animationbackend/AnimatedProps.h>
11+
#include <react/renderer/animationbackend/AnimatedPropsSerializer.h>
12+
13+
namespace facebook::react {
14+
15+
void SchedulerDelegate::schedulerShouldSynchronouslyUpdateAnimatedProps(
16+
const std::unordered_map<Tag, AnimatedProps>& updates) {
17+
for (const auto& [tag, props] : updates) {
18+
schedulerShouldSynchronouslyUpdateViewOnUIThread(
19+
tag, animationbackend::packAnimatedProps(props));
20+
}
21+
}
22+
23+
} // namespace facebook::react

‎packages/react-native/ReactCommon/react/renderer/scheduler/SchedulerDelegate.h‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,16 @@
1010
#include <react/cxxstableapi/FrameworksGuard.h>
1111

1212
#include <memory>
13+
#include <unordered_map>
1314

1415
#include <React/RendererCore.h>
1516
#include <react/renderer/mounting/MountingCoordinator.h>
1617
#include <react/renderer/mounting/ShadowView.h>
1718

1819
namespace facebook::react {
1920

21+
struct AnimatedProps;
22+
2023
/*
2124
* Abstract class for Scheduler's delegate.
2225
*/
@@ -66,6 +69,12 @@ class SchedulerDelegate {
6669

6770
virtual void schedulerShouldSynchronouslyUpdateViewOnUIThread(Tag tag, const folly::dynamic &props) = 0;
6871

72+
/*
73+
* Synchronous update of animated props for many views at once. By default,
74+
* forwards each view to `schedulerShouldSynchronouslyUpdateViewOnUIThread`.
75+
*/
76+
virtual void schedulerShouldSynchronouslyUpdateAnimatedProps(const std::unordered_map<Tag, AnimatedProps> &updates);
77+
6978
virtual void schedulerDidUpdateShadowTree(const std::unordered_map<Tag, folly::dynamic> &tagToProps) = 0;
7079

7180
// View transition bitmap snapshot capture and application.

‎packages/react-native/ReactCommon/react/renderer/scheduler/tests/SchedulerDelegateInvalidationTest.cpp‎

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include <ReactCommon/RuntimeExecutor.h>
3535
#include <react/featureflags/ReactNativeFeatureFlags.h>
3636
#include <react/featureflags/ReactNativeFeatureFlagsDefaults.h>
37+
#include <react/renderer/animationbackend/AnimatedPropsBuilder.h>
3738
#include <react/renderer/componentregistry/ComponentDescriptorProviderRegistry.h>
3839
#include <react/renderer/components/root/RootComponentDescriptor.h>
3940
#include <react/renderer/components/root/RootProps.h>
@@ -588,4 +589,36 @@ TEST_F(
588589
EXPECT_EQ(delegate_->shouldRenderTransactionsCount(), 1);
589590
}
590591

592+
TEST(SchedulerDelegateTest, animatedPropsUseExistingViewUpdatesByDefault) {
593+
class PropsDelegate : public RecordingDelegate {
594+
public:
595+
std::unordered_map<Tag, folly::dynamic> received;
596+
597+
void schedulerShouldSynchronouslyUpdateViewOnUIThread(
598+
Tag tag,
599+
const folly::dynamic& props) override {
600+
received.emplace(tag, props);
601+
}
602+
} delegate;
603+
604+
std::unordered_map<Tag, AnimatedProps> updates;
605+
updates.emplace(
606+
10,
607+
AnimatedProps{
608+
{},
609+
std::make_unique<RawProps>(folly::dynamic::object("opacity", 0.25))});
610+
AnimatedPropsBuilder builder;
611+
builder.setOpacity(0.75);
612+
updates.emplace(20, builder.get());
613+
614+
delegate.schedulerShouldSynchronouslyUpdateAnimatedProps(updates);
615+
616+
ASSERT_EQ(delegate.received.size(), 2);
617+
folly::dynamic expectedRaw = folly::dynamic::object("opacity", 0.25);
618+
folly::dynamic expectedTyped = folly::dynamic::object("opacity", 0.75);
619+
EXPECT_EQ(delegate.received.at(10), expectedRaw);
620+
EXPECT_EQ(delegate.received.at(20), expectedTyped);
621+
EXPECT_EQ(updates.at(10).rawProps->toDynamic(), delegate.received.at(10));
622+
}
623+
591624
} // namespace facebook::react

‎packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.cpp‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -769,6 +769,13 @@ void UIManager::synchronouslyUpdateViewOnUIThread(
769769
}
770770
}
771771

772+
void UIManager::synchronouslyUpdateAnimatedProps(
773+
const std::unordered_map<Tag, AnimatedProps>& updates) {
774+
if (delegate_ != nullptr) {
775+
delegate_->uiManagerShouldSynchronouslyUpdateAnimatedProps(updates);
776+
}
777+
}
778+
772779
#pragma mark ContextContainer
773780

774781
std::shared_ptr<const ContextContainer> UIManager::getContextContainer() const {

‎packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.h‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ class UIManager final : public ShadowTreeDelegate {
8989

9090
void synchronouslyUpdateViewOnUIThread(Tag tag, const folly::dynamic &props);
9191

92+
void synchronouslyUpdateAnimatedProps(const std::unordered_map<Tag, AnimatedProps> &updates);
93+
9294
/*
9395
* Provides access to a UIManagerBinding.
9496
* The `callback` methods will not be called if the internal pointer to

‎packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerDelegate.h‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121

2222
namespace facebook::react {
2323

24+
struct AnimatedProps;
25+
2426
/*
2527
* Abstract class for UIManager's delegate.
2628
*/
@@ -71,6 +73,12 @@ class UIManagerDelegate {
7173
*/
7274
virtual void uiManagerShouldSynchronouslyUpdateViewOnUIThread(Tag tag, const folly::dynamic &props) = 0;
7375

76+
/*
77+
* Synchronous update of animated props for many views at once.
78+
*/
79+
virtual void uiManagerShouldSynchronouslyUpdateAnimatedProps(
80+
const std::unordered_map<Tag, AnimatedProps> &updates) = 0;
81+
7482
/*
7583
* Called after updateShadowTree is invoked.
7684
*/

‎scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4617,6 +4617,7 @@ class facebook::react::Scheduler : public facebook::react::UIManagerDelegate {
46174617
public virtual void uiManagerShouldAddEventListener(std::shared_ptr<const facebook::react::EventListener> listener) final;
46184618
public virtual void uiManagerShouldAddOnSurfaceStartCallback(facebook::react::UIManagerDelegate::OnSurfaceStartCallback&& callback) override;
46194619
public virtual void uiManagerShouldRemoveEventListener(const std::shared_ptr<const facebook::react::EventListener>& listener) final;
4620+
public virtual void uiManagerShouldSynchronouslyUpdateAnimatedProps(const std::unordered_map<facebook::react::Tag, facebook::react::AnimatedProps>& updates) override;
46204621
public virtual void uiManagerShouldSynchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props) override;
46214622
public void addEventListener(std::shared_ptr<const facebook::react::EventListener> listener);
46224623
public void animationTick() const;
@@ -4640,6 +4641,7 @@ class facebook::react::SchedulerDelegate {
46404641
public virtual void schedulerDidUpdateShadowTree(const std::unordered_map<facebook::react::Tag, folly::dynamic>& tagToProps) = 0;
46414642
public virtual void schedulerShouldMergeReactRevision(facebook::react::SurfaceId surfaceId) = 0;
46424643
public virtual void schedulerShouldRenderTransactions(const std::shared_ptr<const facebook::react::MountingCoordinator>& mountingCoordinator) = 0;
4644+
public virtual void schedulerShouldSynchronouslyUpdateAnimatedProps(const std::unordered_map<facebook::react::Tag, facebook::react::AnimatedProps>& updates);
46434645
public virtual void schedulerShouldSynchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props) = 0;
46444646
public virtual ~SchedulerDelegate() noexcept = default;
46454647
}
@@ -5371,6 +5373,7 @@ class facebook::react::UIManager : public facebook::react::ShadowTreeDelegate {
53715373
public void startEmptySurface(facebook::react::ShadowTree::Unique&& shadowTree) const noexcept;
53725374
public void startSurface(facebook::react::ShadowTree::Unique&& shadowTree, std::string moduleName, folly::dynamic props, facebook::react::DisplayMode displayMode) const noexcept;
53735375
public void stopSurfaceForAnimationDelegate(facebook::react::SurfaceId surfaceId) const;
5376+
public void synchronouslyUpdateAnimatedProps(const std::unordered_map<facebook::react::Tag, facebook::react::AnimatedProps>& updates);
53745377
public void synchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props);
53755378
public void unregisterCommitHook(facebook::react::UIManagerCommitHook& commitHook);
53765379
public void unregisterMountHook(facebook::react::UIManagerMountHook& mountHook);
@@ -5438,6 +5441,7 @@ class facebook::react::UIManagerDelegate {
54385441
public virtual void uiManagerShouldAddEventListener(std::shared_ptr<const facebook::react::EventListener> listener) = 0;
54395442
public virtual void uiManagerShouldAddOnSurfaceStartCallback(facebook::react::UIManagerDelegate::OnSurfaceStartCallback&& callback) = 0;
54405443
public virtual void uiManagerShouldRemoveEventListener(const std::shared_ptr<const facebook::react::EventListener>& listener) = 0;
5444+
public virtual void uiManagerShouldSynchronouslyUpdateAnimatedProps(const std::unordered_map<facebook::react::Tag, facebook::react::AnimatedProps>& updates) = 0;
54415445
public virtual void uiManagerShouldSynchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props) = 0;
54425446
public virtual ~UIManagerDelegate() noexcept = default;
54435447
}

0 commit comments

Comments
 (0)