Skip to content

Commit da4a186

Browse files
Bartlomiej Bloniarzmeta-codesync[bot]
authored andcommitted
Add a batched scheduler delegate call for animated prop updates
Summary: 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 delegate implementation forwards to the existing per-view update, so platforms that don't implement it are unaffected. ## Changelog: [General] [Changed] - Add a batched scheduler delegate call for animated prop updates behind `optimizedAnimatedPropUpdates` Differential Revision: D122570617
1 parent fcaf99d commit da4a186

17 files changed

Lines changed: 108 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
@@ -222,6 +222,12 @@ void AnimationBackend::commitUpdates(
222222

223223
void AnimationBackend::synchronouslyUpdateProps(
224224
const std::unordered_map<Tag, AnimatedProps>& updates) {
225+
if (ReactNativeFeatureFlags::optimizedAnimatedPropUpdates()) {
226+
if (auto uiManager = uiManager_.lock()) {
227+
uiManager->synchronouslyUpdateAnimatedProps(updates);
228+
}
229+
return;
230+
}
225231
for (auto& [tag, animatedProps] : updates) {
226232
// TODO: We shouldn't repack it into dynamic, but for that a rewrite
227233
// 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
@@ -100,6 +100,7 @@ class Scheduler final : public UIManagerDelegate {
100100
bool isJSResponder,
101101
bool blockNativeResponder) override;
102102
void uiManagerShouldSynchronouslyUpdateViewOnUIThread(Tag tag, const folly::dynamic &props) override;
103+
void uiManagerShouldSynchronouslyUpdateAnimatedProps(const std::unordered_map<Tag, AnimatedProps> &updates) override;
103104
void uiManagerDidUpdateShadowTree(const std::unordered_map<Tag, folly::dynamic> &tagToProps) override;
104105
void uiManagerDidCaptureViewSnapshot(Tag tag, SurfaceId surfaceId) override;
105106
void uiManagerDidSetViewSnapshot(Tag sourceTag, Tag targetTag, SurfaceId surfaceId) override;

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
#include <memory>
1313

14+
#include <react/renderer/animationbackend/AnimatedPropsSerializer.h>
1415
#include <react/renderer/core/ReactPrimitives.h>
1516
#include <react/renderer/mounting/MountingCoordinator.h>
1617
#include <react/renderer/mounting/ShadowView.h>
@@ -66,6 +67,13 @@ class SchedulerDelegate {
6667

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

70+
virtual void schedulerShouldSynchronouslyUpdateAnimatedProps(const std::unordered_map<Tag, AnimatedProps> &updates)
71+
{
72+
for (const auto &[tag, props] : updates) {
73+
schedulerShouldSynchronouslyUpdateViewOnUIThread(tag, animationbackend::packAnimatedProps(props));
74+
}
75+
}
76+
6977
virtual void schedulerDidUpdateShadowTree(const std::unordered_map<Tag, folly::dynamic> &tagToProps) = 0;
7078

7179
// 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
@@ -4618,6 +4618,7 @@ class facebook::react::Scheduler : public facebook::react::UIManagerDelegate {
46184618
public virtual void uiManagerShouldAddEventListener(std::shared_ptr<const facebook::react::EventListener> listener) final;
46194619
public virtual void uiManagerShouldAddOnSurfaceStartCallback(facebook::react::UIManagerDelegate::OnSurfaceStartCallback&& callback) override;
46204620
public virtual void uiManagerShouldRemoveEventListener(const std::shared_ptr<const facebook::react::EventListener>& listener) final;
4621+
public virtual void uiManagerShouldSynchronouslyUpdateAnimatedProps(const std::unordered_map<facebook::react::Tag, facebook::react::AnimatedProps>& updates) override;
46214622
public virtual void uiManagerShouldSynchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props) override;
46224623
public void addEventListener(std::shared_ptr<const facebook::react::EventListener> listener);
46234624
public void animationTick() const;
@@ -4641,6 +4642,7 @@ class facebook::react::SchedulerDelegate {
46414642
public virtual void schedulerDidUpdateShadowTree(const std::unordered_map<facebook::react::Tag, folly::dynamic>& tagToProps) = 0;
46424643
public virtual void schedulerShouldMergeReactRevision(facebook::react::SurfaceId surfaceId) = 0;
46434644
public virtual void schedulerShouldRenderTransactions(const std::shared_ptr<const facebook::react::MountingCoordinator>& mountingCoordinator) = 0;
4645+
public virtual void schedulerShouldSynchronouslyUpdateAnimatedProps(const std::unordered_map<facebook::react::Tag, facebook::react::AnimatedProps>& updates);
46444646
public virtual void schedulerShouldSynchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props) = 0;
46454647
public virtual ~SchedulerDelegate() noexcept = default;
46464648
}
@@ -5372,6 +5374,7 @@ class facebook::react::UIManager : public facebook::react::ShadowTreeDelegate {
53725374
public void startEmptySurface(facebook::react::ShadowTree::Unique&& shadowTree) const noexcept;
53735375
public void startSurface(facebook::react::ShadowTree::Unique&& shadowTree, std::string moduleName, folly::dynamic props, facebook::react::DisplayMode displayMode) const noexcept;
53745376
public void stopSurfaceForAnimationDelegate(facebook::react::SurfaceId surfaceId) const;
5377+
public void synchronouslyUpdateAnimatedProps(const std::unordered_map<facebook::react::Tag, facebook::react::AnimatedProps>& updates);
53755378
public void synchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props);
53765379
public void unregisterCommitHook(facebook::react::UIManagerCommitHook& commitHook);
53775380
public void unregisterMountHook(facebook::react::UIManagerMountHook& mountHook);
@@ -5439,6 +5442,7 @@ class facebook::react::UIManagerDelegate {
54395442
public virtual void uiManagerShouldAddEventListener(std::shared_ptr<const facebook::react::EventListener> listener) = 0;
54405443
public virtual void uiManagerShouldAddOnSurfaceStartCallback(facebook::react::UIManagerDelegate::OnSurfaceStartCallback&& callback) = 0;
54415444
public virtual void uiManagerShouldRemoveEventListener(const std::shared_ptr<const facebook::react::EventListener>& listener) = 0;
5445+
public virtual void uiManagerShouldSynchronouslyUpdateAnimatedProps(const std::unordered_map<facebook::react::Tag, facebook::react::AnimatedProps>& updates) = 0;
54425446
public virtual void uiManagerShouldSynchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props) = 0;
54435447
public virtual ~UIManagerDelegate() noexcept = default;
54445448
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4434,6 +4434,7 @@ class facebook::react::Scheduler : public facebook::react::UIManagerDelegate {
44344434
public virtual void uiManagerShouldAddEventListener(std::shared_ptr<const facebook::react::EventListener> listener) final;
44354435
public virtual void uiManagerShouldAddOnSurfaceStartCallback(facebook::react::UIManagerDelegate::OnSurfaceStartCallback&& callback) override;
44364436
public virtual void uiManagerShouldRemoveEventListener(const std::shared_ptr<const facebook::react::EventListener>& listener) final;
4437+
public virtual void uiManagerShouldSynchronouslyUpdateAnimatedProps(const std::unordered_map<facebook::react::Tag, facebook::react::AnimatedProps>& updates) override;
44374438
public virtual void uiManagerShouldSynchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props) override;
44384439
public void addEventListener(std::shared_ptr<const facebook::react::EventListener> listener);
44394440
public void animationTick() const;
@@ -4457,6 +4458,7 @@ class facebook::react::SchedulerDelegate {
44574458
public virtual void schedulerDidUpdateShadowTree(const std::unordered_map<facebook::react::Tag, folly::dynamic>& tagToProps) = 0;
44584459
public virtual void schedulerShouldMergeReactRevision(facebook::react::SurfaceId surfaceId) = 0;
44594460
public virtual void schedulerShouldRenderTransactions(const std::shared_ptr<const facebook::react::MountingCoordinator>& mountingCoordinator) = 0;
4461+
public virtual void schedulerShouldSynchronouslyUpdateAnimatedProps(const std::unordered_map<facebook::react::Tag, facebook::react::AnimatedProps>& updates);
44604462
public virtual void schedulerShouldSynchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props) = 0;
44614463
public virtual ~SchedulerDelegate() noexcept = default;
44624464
}
@@ -5182,6 +5184,7 @@ class facebook::react::UIManager : public facebook::react::ShadowTreeDelegate {
51825184
public void startEmptySurface(facebook::react::ShadowTree::Unique&& shadowTree) const noexcept;
51835185
public void startSurface(facebook::react::ShadowTree::Unique&& shadowTree, std::string moduleName, folly::dynamic props, facebook::react::DisplayMode displayMode) const noexcept;
51845186
public void stopSurfaceForAnimationDelegate(facebook::react::SurfaceId surfaceId) const;
5187+
public void synchronouslyUpdateAnimatedProps(const std::unordered_map<facebook::react::Tag, facebook::react::AnimatedProps>& updates);
51855188
public void synchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props);
51865189
public void unregisterCommitHook(facebook::react::UIManagerCommitHook& commitHook);
51875190
public void unregisterMountHook(facebook::react::UIManagerMountHook& mountHook);
@@ -5249,6 +5252,7 @@ class facebook::react::UIManagerDelegate {
52495252
public virtual void uiManagerShouldAddEventListener(std::shared_ptr<const facebook::react::EventListener> listener) = 0;
52505253
public virtual void uiManagerShouldAddOnSurfaceStartCallback(facebook::react::UIManagerDelegate::OnSurfaceStartCallback&& callback) = 0;
52515254
public virtual void uiManagerShouldRemoveEventListener(const std::shared_ptr<const facebook::react::EventListener>& listener) = 0;
5255+
public virtual void uiManagerShouldSynchronouslyUpdateAnimatedProps(const std::unordered_map<facebook::react::Tag, facebook::react::AnimatedProps>& updates) = 0;
52525256
public virtual void uiManagerShouldSynchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props) = 0;
52535257
public virtual ~UIManagerDelegate() noexcept = default;
52545258
}

0 commit comments

Comments
 (0)