Skip to content

Commit

Permalink
Create feature flag to make estimation for remaining time in frame fo…
Browse files Browse the repository at this point in the history
…r view preallocation more precise (#46563)

Summary:
Pull Request resolved: #46563

Changelog: [internal]

Our current implementation of view preallocation on Android runs very early in the frame (during the `animation` stage, after `input` and before `traversal`). In that implementation, we check the remaining time in the frame and we allocate half of that time for view preallocation.

This logic can make us drop frames unnecessarily, if the work that we need to do after this allocated time is longer than we expect. Because we don't know how long the `traversal` stage will be, we don't account for that time at all (or, at best, we do only use half of the remaining time).

This creates a feature flag to move view preallocation outside of the choreographer, so we can compute the remaining time considering all the work we did in the frame, and not just the work we did in the input stage.

Reviewed By: javache

Differential Revision: D62962341

fbshipit-source-id: 69aaeecc8596906e61b47af9adad23c1075a091a
  • Loading branch information
rubennorte authored and facebook-github-bot committed Sep 18, 2024
1 parent 7f55dc0 commit fea0aa7
Show file tree
Hide file tree
Showing 20 changed files with 182 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import com.facebook.react.bridge.ReactNoCrashSoftException;
import com.facebook.react.bridge.ReactSoftExceptionLogger;
import com.facebook.react.bridge.RetryableMountingLayerException;
import com.facebook.react.bridge.UiThreadUtil;
import com.facebook.react.fabric.mounting.mountitems.DispatchCommandMountItem;
import com.facebook.react.fabric.mounting.mountitems.MountItem;
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags;
Expand Down Expand Up @@ -54,6 +55,21 @@ public class MountItemDispatcher {
private long mBatchedExecutionTime = 0L;
private long mRunStartTime = 0L;

private long mLastFrameTimeNanos = 0L;
private boolean mIsPremountScheduled = false;
private final Runnable mPremountRunnable =
() -> {
mIsPremountScheduled = false;

if (mPreMountItems.isEmpty()) {
// Avoid starting systrace if there are no pre mount items.
return;
}

long deadline = mLastFrameTimeNanos + (FRAME_TIME_NS / 2);
dispatchPreMountItemsImpl(deadline);
};

public MountItemDispatcher(MountingManager mountingManager, ItemDispatchListener listener) {
mMountingManager = mountingManager;
mItemDispatchListener = listener;
Expand Down Expand Up @@ -338,22 +354,35 @@ private boolean dispatchMountItems() {
@UiThread
@ThreadConfined(UI)
public void dispatchPreMountItems(long frameTimeNanos) {
mLastFrameTimeNanos = frameTimeNanos;

if (mPreMountItems.isEmpty()) {
// Avoid starting systrace if there are no pre mount items.
return;
}

if (ReactNativeFeatureFlags.enablePreciseSchedulingForPremountItemsOnAndroid()) {
if (!mIsPremountScheduled) {
mIsPremountScheduled = true;
UiThreadUtil.getUiThreadHandler().post(mPremountRunnable);
}
} else {
long deadline = mLastFrameTimeNanos + FRAME_TIME_NS / 2;
dispatchPreMountItemsImpl(deadline);
}
}

private void dispatchPreMountItemsImpl(long deadline) {
Systrace.beginSection(
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "MountItemDispatcher::premountViews");

// dispatchPreMountItems cannot be reentrant, but we want to prevent dispatchMountItems from
// reentering during dispatchPreMountItems
mInDispatch = true;

long frameTimeDeadline = frameTimeNanos + FRAME_TIME_NS / 2;
try {
while (true) {
if (System.nanoTime() > frameTimeDeadline) {
if (System.nanoTime() > deadline) {
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<<60fcb02736ca814a842225291e3a55b1>>
* @generated SignedSource<<73409b567e77f17838ae9681a8e20be6>>
*/

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

/**
* Moves execution of pre-mount items to outside the choregrapher in the main thread, so we can estimate idle time more precisely (Android only).
*/
@JvmStatic
public fun enablePreciseSchedulingForPremountItemsOnAndroid(): Boolean = accessor.enablePreciseSchedulingForPremountItemsOnAndroid()

/**
* When enabled, Android will receive prop updates based on the differences between the last rendered shadow node and the last committed shadow node.
*/
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<<f2a64fef9775f1890adcf3d761061182>>
* @generated SignedSource<<9f2978b5a732a3ca4f3c3d74debba782>>
*/

/**
Expand Down Expand Up @@ -40,6 +40,7 @@ public class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAccesso
private var enableLayoutAnimationsOnIOSCache: Boolean? = null
private var enableLongTaskAPICache: Boolean? = null
private var enableMicrotasksCache: Boolean? = null
private var enablePreciseSchedulingForPremountItemsOnAndroidCache: Boolean? = null
private var enablePropsUpdateReconciliationAndroidCache: Boolean? = null
private var enableReportEventPaintTimeCache: Boolean? = null
private var enableSynchronousStateUpdatesCache: Boolean? = null
Expand Down Expand Up @@ -251,6 +252,15 @@ public class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAccesso
return cached
}

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

override fun enablePropsUpdateReconciliationAndroid(): Boolean {
var cached = enablePropsUpdateReconciliationAndroidCache
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<<03618f182004add50655a88e6e65e96d>>
* @generated SignedSource<<0d2f5427661ce67e468aea47bdd29802>>
*/

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

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

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

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

@DoNotStrip @JvmStatic public external fun enableReportEventPaintTime(): 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<<fc795741e932cf24e5bfb7e2fe1046ae>>
* @generated SignedSource<<72e3e7b5a53e64f8f48310d8b07cdf76>>
*/

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

override fun enableMicrotasks(): Boolean = false

override fun enablePreciseSchedulingForPremountItemsOnAndroid(): Boolean = false

override fun enablePropsUpdateReconciliationAndroid(): Boolean = false

override fun enableReportEventPaintTime(): 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<<0dbb1f1a08649132955698ac51050e62>>
* @generated SignedSource<<e1a44f8e54709e70934f1d418d4ed80f>>
*/

/**
Expand Down Expand Up @@ -44,6 +44,7 @@ public class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcces
private var enableLayoutAnimationsOnIOSCache: Boolean? = null
private var enableLongTaskAPICache: Boolean? = null
private var enableMicrotasksCache: Boolean? = null
private var enablePreciseSchedulingForPremountItemsOnAndroidCache: Boolean? = null
private var enablePropsUpdateReconciliationAndroidCache: Boolean? = null
private var enableReportEventPaintTimeCache: Boolean? = null
private var enableSynchronousStateUpdatesCache: Boolean? = null
Expand Down Expand Up @@ -275,6 +276,16 @@ public class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcces
return cached
}

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

override fun enablePropsUpdateReconciliationAndroid(): Boolean {
var cached = enablePropsUpdateReconciliationAndroidCache
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<<79cd9812257f42d553b49a3f6d3d4989>>
* @generated SignedSource<<0f6129ccbcf7857785724f14b41edb2d>>
*/

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

@DoNotStrip public fun enableMicrotasks(): Boolean

@DoNotStrip public fun enablePreciseSchedulingForPremountItemsOnAndroid(): Boolean

@DoNotStrip public fun enablePropsUpdateReconciliationAndroid(): Boolean

@DoNotStrip public fun enableReportEventPaintTime(): 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<<9e891a5d26221136d7334982fc2e99d3>>
* @generated SignedSource<<43b92dd984e985e09e3efad9c8fedf44>>
*/

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

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

bool enablePropsUpdateReconciliationAndroid() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("enablePropsUpdateReconciliationAndroid");
Expand Down Expand Up @@ -443,6 +449,11 @@ bool JReactNativeFeatureFlagsCxxInterop::enableMicrotasks(
return ReactNativeFeatureFlags::enableMicrotasks();
}

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

bool JReactNativeFeatureFlagsCxxInterop::enablePropsUpdateReconciliationAndroid(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::enablePropsUpdateReconciliationAndroid();
Expand Down Expand Up @@ -670,6 +681,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() {
makeNativeMethod(
"enableMicrotasks",
JReactNativeFeatureFlagsCxxInterop::enableMicrotasks),
makeNativeMethod(
"enablePreciseSchedulingForPremountItemsOnAndroid",
JReactNativeFeatureFlagsCxxInterop::enablePreciseSchedulingForPremountItemsOnAndroid),
makeNativeMethod(
"enablePropsUpdateReconciliationAndroid",
JReactNativeFeatureFlagsCxxInterop::enablePropsUpdateReconciliationAndroid),
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<<70edc4176da74aed5eaa1561a907a7ee>>
* @generated SignedSource<<54c94f9bfa07e41d978899dbce03469f>>
*/

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

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

static bool enablePropsUpdateReconciliationAndroid(
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<<80b130f32b0bc59b9a9605777537feeb>>
* @generated SignedSource<<b04a641595d870bd9666281b3f0cfec5>>
*/

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

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

bool ReactNativeFeatureFlags::enablePropsUpdateReconciliationAndroid() {
return getAccessor().enablePropsUpdateReconciliationAndroid();
}
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<<3b5ff29faaf1575c051131bdf24a3237>>
* @generated SignedSource<<2cde82b474ab94cd05bdf5e04e700c13>>
*/

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

/**
* Moves execution of pre-mount items to outside the choregrapher in the main thread, so we can estimate idle time more precisely (Android only).
*/
RN_EXPORT static bool enablePreciseSchedulingForPremountItemsOnAndroid();

/**
* When enabled, Android will receive prop updates based on the differences between the last rendered shadow node and the last committed shadow node.
*/
Expand Down
Loading

0 comments on commit fea0aa7

Please sign in to comment.