Skip to content

Commit

Permalink
Only schedule Choreographer callbacks if there's an active animation
Browse files Browse the repository at this point in the history
Summary:
Changelog: [Internal]
A very similar diff was attempted with D50647971 and reverted in D51617862. The main difference here is all behavior is gated behind the feature flag. Before, we were enqueuing the extra frame callback on start_animating_node even if ondemand choreographer was disabled.

Reviewed By: javache

Differential Revision: D56085369

fbshipit-source-id: fa6335303fe98199b18fa2b4819110afb8efcc0d
  • Loading branch information
John Ward authored and facebook-github-bot committed May 1, 2024
1 parent dfa6519 commit 0383669
Show file tree
Hide file tree
Showing 20 changed files with 143 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import com.facebook.react.bridge.UIManagerListener;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.common.annotations.VisibleForTesting;
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.modules.core.ReactChoreographer;
import com.facebook.react.uimanager.GuardedFrameCallback;
Expand Down Expand Up @@ -225,6 +226,7 @@ void executeBatch(long maxBatchNumber, NativeAnimatedNodesManager nodesManager)

private boolean mInitializedForFabric = false;
private boolean mInitializedForNonFabric = false;
private boolean mEnqueuedAnimationOnFrame = false;
private @UIManagerType int mUIManagerType = UIManagerType.DEFAULT;
private int mNumFabricAnimations = 0;
private int mNumNonFabricAnimations = 0;
Expand All @@ -238,23 +240,20 @@ public NativeAnimatedModule(ReactApplicationContext reactContext) {
@Override
protected void doFrameGuarded(final long frameTimeNanos) {
try {
mEnqueuedAnimationOnFrame = false;
NativeAnimatedNodesManager nodesManager = getNodesManager();
if (nodesManager != null && nodesManager.hasActiveAnimations()) {
nodesManager.runUpdates(frameTimeNanos);
}
// This is very unlikely to ever be hit.
if (nodesManager == null && mReactChoreographer == null) {
if (nodesManager == null || mReactChoreographer == null) {
return;
}

// TODO: Would be great to avoid adding this callback in case there are no active
// animations and no outstanding tasks on the operations queue. Apparently frame
// callbacks can only be posted from the UI thread and therefore we cannot schedule
// them directly from other threads.
Assertions.assertNotNull(mReactChoreographer)
.postFrameCallback(
ReactChoreographer.CallbackType.NATIVE_ANIMATED_MODULE,
mAnimatedFrameCallback);
if (!ReactNativeFeatureFlags.lazyAnimationCallbacks()
|| nodesManager.hasActiveAnimations()) {
enqueueFrameCallback();
}
} catch (Exception ex) {
throw new RuntimeException(ex);
}
Expand Down Expand Up @@ -406,12 +405,16 @@ private void clearFrameCallback() {
Assertions.assertNotNull(mReactChoreographer)
.removeFrameCallback(
ReactChoreographer.CallbackType.NATIVE_ANIMATED_MODULE, mAnimatedFrameCallback);
mEnqueuedAnimationOnFrame = false;
}

private void enqueueFrameCallback() {
Assertions.assertNotNull(mReactChoreographer)
.postFrameCallback(
ReactChoreographer.CallbackType.NATIVE_ANIMATED_MODULE, mAnimatedFrameCallback);
if (!mEnqueuedAnimationOnFrame) {
Assertions.assertNotNull(mReactChoreographer)
.postFrameCallback(
ReactChoreographer.CallbackType.NATIVE_ANIMATED_MODULE, mAnimatedFrameCallback);
mEnqueuedAnimationOnFrame = true;
}
}

@VisibleForTesting
Expand Down Expand Up @@ -1117,6 +1120,9 @@ public void onValueUpdate(double value) {
opsAndArgs.getInt(i++), opsAndArgs.getInt(i++));
break;
case OP_CODE_START_ANIMATING_NODE:
if (ReactNativeFeatureFlags.lazyAnimationCallbacks()) {
enqueueFrameCallback();
}
animatedNodesManager.startAnimatingNode(
opsAndArgs.getInt(i++), opsAndArgs.getInt(i++), opsAndArgs.getMap(i++), null);
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<2ce486e0fad0c305ea953c877e146104>>
* @generated SignedSource<<3585602cd983452045d3165edbafc0ca>>
*/

/**
Expand Down Expand Up @@ -118,6 +118,12 @@ public object ReactNativeFeatureFlags {
@JvmStatic
public fun inspectorEnableModernCDPRegistry(): Boolean = accessor.inspectorEnableModernCDPRegistry()

/**
* Only enqueue Choreographer calls if there is an ongoing animation, instead of enqueueing every frame.
*/
@JvmStatic
public fun lazyAnimationCallbacks(): Boolean = accessor.lazyAnimationCallbacks()

/**
* When enabled, ParagraphShadowNode will no longer call measure twice.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<cb271f43cd3a105679439b8dc269beac>>
* @generated SignedSource<<36558176ca3a38b086a50ac90cf08c67>>
*/

/**
Expand Down Expand Up @@ -35,6 +35,7 @@ public class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAccesso
private var forceBatchingMountItemsOnAndroidCache: Boolean? = null
private var inspectorEnableCxxInspectorPackagerConnectionCache: Boolean? = null
private var inspectorEnableModernCDPRegistryCache: Boolean? = null
private var lazyAnimationCallbacksCache: Boolean? = null
private var preventDoubleTextMeasureCache: Boolean? = null
private var useModernRuntimeSchedulerCache: Boolean? = null
private var useNativeViewConfigsInBridgelessModeCache: Boolean? = null
Expand Down Expand Up @@ -175,6 +176,15 @@ public class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAccesso
return cached
}

override fun lazyAnimationCallbacks(): Boolean {
var cached = lazyAnimationCallbacksCache
if (cached == null) {
cached = ReactNativeFeatureFlagsCxxInterop.lazyAnimationCallbacks()
lazyAnimationCallbacksCache = cached
}
return cached
}

override fun preventDoubleTextMeasure(): Boolean {
var cached = preventDoubleTextMeasureCache
if (cached == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<766945dd8430333fdf4c66129a97d039>>
* @generated SignedSource<<34806c87ad1769ee08a5322994b0bb1b>>
*/

/**
Expand Down Expand Up @@ -58,6 +58,8 @@ public object ReactNativeFeatureFlagsCxxInterop {

@DoNotStrip @JvmStatic public external fun inspectorEnableModernCDPRegistry(): Boolean

@DoNotStrip @JvmStatic public external fun lazyAnimationCallbacks(): Boolean

@DoNotStrip @JvmStatic public external fun preventDoubleTextMeasure(): Boolean

@DoNotStrip @JvmStatic public external fun useModernRuntimeScheduler(): Boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<0c39f3c8abdce75d6a197b6227bb181e>>
* @generated SignedSource<<e6a248df3e57d36319d9a32bd49dfa5a>>
*/

/**
Expand Down Expand Up @@ -53,6 +53,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi

override fun inspectorEnableModernCDPRegistry(): Boolean = false

override fun lazyAnimationCallbacks(): Boolean = false

override fun preventDoubleTextMeasure(): Boolean = false

override fun useModernRuntimeScheduler(): Boolean = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<2453d73e433ecc5c13e0d08f6323539b>>
* @generated SignedSource<<40609d554dca56ea34653f0a77e5b62c>>
*/

/**
Expand Down Expand Up @@ -39,6 +39,7 @@ public class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcces
private var forceBatchingMountItemsOnAndroidCache: Boolean? = null
private var inspectorEnableCxxInspectorPackagerConnectionCache: Boolean? = null
private var inspectorEnableModernCDPRegistryCache: Boolean? = null
private var lazyAnimationCallbacksCache: Boolean? = null
private var preventDoubleTextMeasureCache: Boolean? = null
private var useModernRuntimeSchedulerCache: Boolean? = null
private var useNativeViewConfigsInBridgelessModeCache: Boolean? = null
Expand Down Expand Up @@ -194,6 +195,16 @@ public class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcces
return cached
}

override fun lazyAnimationCallbacks(): Boolean {
var cached = lazyAnimationCallbacksCache
if (cached == null) {
cached = currentProvider.lazyAnimationCallbacks()
accessedFeatureFlags.add("lazyAnimationCallbacks")
lazyAnimationCallbacksCache = cached
}
return cached
}

override fun preventDoubleTextMeasure(): Boolean {
var cached = preventDoubleTextMeasureCache
if (cached == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<251133cdd333901af532b1442c68996b>>
* @generated SignedSource<<173f9f2cd094134c32a7a78c7241518c>>
*/

/**
Expand Down Expand Up @@ -53,6 +53,8 @@ public interface ReactNativeFeatureFlagsProvider {

@DoNotStrip public fun inspectorEnableModernCDPRegistry(): Boolean

@DoNotStrip public fun lazyAnimationCallbacks(): Boolean

@DoNotStrip public fun preventDoubleTextMeasure(): Boolean

@DoNotStrip public fun useModernRuntimeScheduler(): Boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<1a5515a24a45dac77e9833fecfa7d952>>
* @generated SignedSource<<70cc98f74b676cb12f4e510b88d551aa>>
*/

/**
Expand Down Expand Up @@ -129,6 +129,12 @@ class ReactNativeFeatureFlagsProviderHolder
return method(javaProvider_);
}

bool lazyAnimationCallbacks() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("lazyAnimationCallbacks");
return method(javaProvider_);
}

bool preventDoubleTextMeasure() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("preventDoubleTextMeasure");
Expand Down Expand Up @@ -232,6 +238,11 @@ bool JReactNativeFeatureFlagsCxxInterop::inspectorEnableModernCDPRegistry(
return ReactNativeFeatureFlags::inspectorEnableModernCDPRegistry();
}

bool JReactNativeFeatureFlagsCxxInterop::lazyAnimationCallbacks(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::lazyAnimationCallbacks();
}

bool JReactNativeFeatureFlagsCxxInterop::preventDoubleTextMeasure(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::preventDoubleTextMeasure();
Expand Down Expand Up @@ -314,6 +325,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() {
makeNativeMethod(
"inspectorEnableModernCDPRegistry",
JReactNativeFeatureFlagsCxxInterop::inspectorEnableModernCDPRegistry),
makeNativeMethod(
"lazyAnimationCallbacks",
JReactNativeFeatureFlagsCxxInterop::lazyAnimationCallbacks),
makeNativeMethod(
"preventDoubleTextMeasure",
JReactNativeFeatureFlagsCxxInterop::preventDoubleTextMeasure),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<4fa2aa14a3e8646855b6464dc6fb3668>>
* @generated SignedSource<<5065ed4400b76ae62f43ec64ee03ebd2>>
*/

/**
Expand Down Expand Up @@ -75,6 +75,9 @@ class JReactNativeFeatureFlagsCxxInterop
static bool inspectorEnableModernCDPRegistry(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);

static bool lazyAnimationCallbacks(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);

static bool preventDoubleTextMeasure(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<6c09901762e8ae0cb33f014a4b982acc>>
* @generated SignedSource<<6c93c729ef1a05b5b7d00bafa1f386ad>>
*/

/**
Expand Down Expand Up @@ -81,6 +81,10 @@ bool ReactNativeFeatureFlags::inspectorEnableModernCDPRegistry() {
return getAccessor().inspectorEnableModernCDPRegistry();
}

bool ReactNativeFeatureFlags::lazyAnimationCallbacks() {
return getAccessor().lazyAnimationCallbacks();
}

bool ReactNativeFeatureFlags::preventDoubleTextMeasure() {
return getAccessor().preventDoubleTextMeasure();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<029cba10ed8a02c85ecc54d4752e8e14>>
* @generated SignedSource<<b081516c5b1aa804ec4f444f48773fbb>>
*/

/**
Expand Down Expand Up @@ -112,6 +112,11 @@ class ReactNativeFeatureFlags {
*/
RN_EXPORT static bool inspectorEnableModernCDPRegistry();

/**
* Only enqueue Choreographer calls if there is an ongoing animation, instead of enqueueing every frame.
*/
RN_EXPORT static bool lazyAnimationCallbacks();

/**
* When enabled, ParagraphShadowNode will no longer call measure twice.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<0681b9ba9141253f4caf6e2bac00a60f>>
* @generated SignedSource<<2091c7b1d7d19d8967a6bb5ebb2ac76f>>
*/

/**
Expand Down Expand Up @@ -299,6 +299,24 @@ bool ReactNativeFeatureFlagsAccessor::inspectorEnableModernCDPRegistry() {
return flagValue.value();
}

bool ReactNativeFeatureFlagsAccessor::lazyAnimationCallbacks() {
auto flagValue = lazyAnimationCallbacks_.load();

if (!flagValue.has_value()) {
// This block is not exclusive but it is not necessary.
// If multiple threads try to initialize the feature flag, we would only
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.

markFlagAsAccessed(15, "lazyAnimationCallbacks");

flagValue = currentProvider_->lazyAnimationCallbacks();
lazyAnimationCallbacks_ = flagValue;
}

return flagValue.value();
}

bool ReactNativeFeatureFlagsAccessor::preventDoubleTextMeasure() {
auto flagValue = preventDoubleTextMeasure_.load();

Expand All @@ -308,7 +326,7 @@ bool ReactNativeFeatureFlagsAccessor::preventDoubleTextMeasure() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.

markFlagAsAccessed(15, "preventDoubleTextMeasure");
markFlagAsAccessed(16, "preventDoubleTextMeasure");

flagValue = currentProvider_->preventDoubleTextMeasure();
preventDoubleTextMeasure_ = flagValue;
Expand All @@ -326,7 +344,7 @@ bool ReactNativeFeatureFlagsAccessor::useModernRuntimeScheduler() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.

markFlagAsAccessed(16, "useModernRuntimeScheduler");
markFlagAsAccessed(17, "useModernRuntimeScheduler");

flagValue = currentProvider_->useModernRuntimeScheduler();
useModernRuntimeScheduler_ = flagValue;
Expand All @@ -344,7 +362,7 @@ bool ReactNativeFeatureFlagsAccessor::useNativeViewConfigsInBridgelessMode() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.

markFlagAsAccessed(17, "useNativeViewConfigsInBridgelessMode");
markFlagAsAccessed(18, "useNativeViewConfigsInBridgelessMode");

flagValue = currentProvider_->useNativeViewConfigsInBridgelessMode();
useNativeViewConfigsInBridgelessMode_ = flagValue;
Expand All @@ -362,7 +380,7 @@ bool ReactNativeFeatureFlagsAccessor::useStateAlignmentMechanism() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.

markFlagAsAccessed(18, "useStateAlignmentMechanism");
markFlagAsAccessed(19, "useStateAlignmentMechanism");

flagValue = currentProvider_->useStateAlignmentMechanism();
useStateAlignmentMechanism_ = flagValue;
Expand Down

0 comments on commit 0383669

Please sign in to comment.