Start creating views before end of batch
Summary:Creating views shouldn't have side effects so we can start doing them before the end of a batch. In order to limit the effect on framerate, we stop executing these create view operations after we've passed halfway through the frame. Note, this doesn't seem to work yet for nodes: I'll address that in a followup diff. Reviewed By: lexs Differential Revision: D3120631 fb-gh-sync-id: 981540fac5a7499158146adb72c1be21fd0b5702 fbshipit-source-id: 981540fac5a7499158146adb72c1be21fd0b5702
This commit is contained in:
parent
c3c7249689
commit
6a3b334139
|
@ -12,6 +12,7 @@ package com.facebook.react.uimanager;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.annotation.concurrent.GuardedBy;
|
import javax.annotation.concurrent.GuardedBy;
|
||||||
|
|
||||||
|
import java.util.ArrayDeque;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.concurrent.Semaphore;
|
import java.util.concurrent.Semaphore;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
@ -417,8 +418,6 @@ public class UIViewOperationQueue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ArrayList<UIOperation> mOperations = new ArrayList<>();
|
|
||||||
|
|
||||||
private final class FindTargetForTouchOperation implements UIOperation {
|
private final class FindTargetForTouchOperation implements UIOperation {
|
||||||
|
|
||||||
private final int mReactTag;
|
private final int mReactTag;
|
||||||
|
@ -493,14 +492,17 @@ public class UIViewOperationQueue {
|
||||||
|
|
||||||
private final NativeViewHierarchyManager mNativeViewHierarchyManager;
|
private final NativeViewHierarchyManager mNativeViewHierarchyManager;
|
||||||
private final AnimationRegistry mAnimationRegistry;
|
private final AnimationRegistry mAnimationRegistry;
|
||||||
|
|
||||||
private final Object mDispatchRunnablesLock = new Object();
|
private final Object mDispatchRunnablesLock = new Object();
|
||||||
|
private final Object mNonBatchedOperationsLock = new Object();
|
||||||
private final DispatchUIFrameCallback mDispatchUIFrameCallback;
|
private final DispatchUIFrameCallback mDispatchUIFrameCallback;
|
||||||
private final ReactApplicationContext mReactApplicationContext;
|
private final ReactApplicationContext mReactApplicationContext;
|
||||||
|
|
||||||
@GuardedBy("mDispatchRunnablesLock")
|
@GuardedBy("mDispatchRunnablesLock")
|
||||||
private final ArrayList<Runnable> mDispatchUIRunnables = new ArrayList<>();
|
private final ArrayList<Runnable> mDispatchUIRunnables = new ArrayList<>();
|
||||||
|
|
||||||
|
private ArrayList<UIOperation> mOperations = new ArrayList<>();
|
||||||
|
@GuardedBy("mNonBatchedOperationsLock")
|
||||||
|
private ArrayDeque<UIOperation> mNonBatchedOperations = new ArrayDeque<>();
|
||||||
|
|
||||||
private @Nullable NotThreadSafeViewHierarchyUpdateDebugListener mViewHierarchyUpdateDebugListener;
|
private @Nullable NotThreadSafeViewHierarchyUpdateDebugListener mViewHierarchyUpdateDebugListener;
|
||||||
|
|
||||||
public UIViewOperationQueue(
|
public UIViewOperationQueue(
|
||||||
|
@ -604,12 +606,14 @@ public class UIViewOperationQueue {
|
||||||
int viewReactTag,
|
int viewReactTag,
|
||||||
String viewClassName,
|
String viewClassName,
|
||||||
@Nullable ReactStylesDiffMap initialProps) {
|
@Nullable ReactStylesDiffMap initialProps) {
|
||||||
mOperations.add(
|
synchronized (mNonBatchedOperationsLock) {
|
||||||
|
mNonBatchedOperations.addLast(
|
||||||
new CreateViewOperation(
|
new CreateViewOperation(
|
||||||
themedContext,
|
themedContext,
|
||||||
viewReactTag,
|
viewReactTag,
|
||||||
viewClassName,
|
viewClassName,
|
||||||
initialProps));
|
initialProps));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void enqueueUpdateProperties(int reactTag, String className, ReactStylesDiffMap props) {
|
public void enqueueUpdateProperties(int reactTag, String className, ReactStylesDiffMap props) {
|
||||||
|
@ -699,6 +703,17 @@ public class UIViewOperationQueue {
|
||||||
mOperations = new ArrayList<>();
|
mOperations = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final UIOperation[] nonBatchedOperations;
|
||||||
|
synchronized (mNonBatchedOperationsLock) {
|
||||||
|
if (!mNonBatchedOperations.isEmpty()) {
|
||||||
|
nonBatchedOperations =
|
||||||
|
mNonBatchedOperations.toArray(new UIOperation[mNonBatchedOperations.size()]);
|
||||||
|
mNonBatchedOperations.clear();
|
||||||
|
} else {
|
||||||
|
nonBatchedOperations = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (mViewHierarchyUpdateDebugListener != null) {
|
if (mViewHierarchyUpdateDebugListener != null) {
|
||||||
mViewHierarchyUpdateDebugListener.onViewHierarchyUpdateEnqueued();
|
mViewHierarchyUpdateDebugListener.onViewHierarchyUpdateEnqueued();
|
||||||
}
|
}
|
||||||
|
@ -712,6 +727,14 @@ public class UIViewOperationQueue {
|
||||||
.arg("BatchId", batchId)
|
.arg("BatchId", batchId)
|
||||||
.flush();
|
.flush();
|
||||||
try {
|
try {
|
||||||
|
// All nonBatchedOperations should be executed before regular operations as
|
||||||
|
// regular operations may depend on them
|
||||||
|
if (nonBatchedOperations != null) {
|
||||||
|
for (UIOperation op : nonBatchedOperations) {
|
||||||
|
op.execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (operations != null) {
|
if (operations != null) {
|
||||||
for (int i = 0; i < operations.size(); i++) {
|
for (int i = 0; i < operations.size(); i++) {
|
||||||
operations.get(i).execute();
|
operations.get(i).execute();
|
||||||
|
@ -760,12 +783,22 @@ public class UIViewOperationQueue {
|
||||||
*/
|
*/
|
||||||
private class DispatchUIFrameCallback extends GuardedChoreographerFrameCallback {
|
private class DispatchUIFrameCallback extends GuardedChoreographerFrameCallback {
|
||||||
|
|
||||||
|
private static final int MIN_TIME_LEFT_IN_FRAME_TO_SCHEDULE_MORE_WORK_MS = 8;
|
||||||
|
private static final int FRAME_TIME_MS = 16;
|
||||||
|
|
||||||
private DispatchUIFrameCallback(ReactContext reactContext) {
|
private DispatchUIFrameCallback(ReactContext reactContext) {
|
||||||
super(reactContext);
|
super(reactContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void doFrameGuarded(long frameTimeNanos) {
|
public void doFrameGuarded(long frameTimeNanos) {
|
||||||
|
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "dispatchNonBatchedUIOperations");
|
||||||
|
try {
|
||||||
|
dispatchPendingNonBatchedOperations(frameTimeNanos);
|
||||||
|
} finally {
|
||||||
|
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
|
||||||
|
}
|
||||||
|
|
||||||
synchronized (mDispatchRunnablesLock) {
|
synchronized (mDispatchRunnablesLock) {
|
||||||
for (int i = 0; i < mDispatchUIRunnables.size(); i++) {
|
for (int i = 0; i < mDispatchUIRunnables.size(); i++) {
|
||||||
mDispatchUIRunnables.get(i).run();
|
mDispatchUIRunnables.get(i).run();
|
||||||
|
@ -774,7 +807,27 @@ public class UIViewOperationQueue {
|
||||||
}
|
}
|
||||||
|
|
||||||
ReactChoreographer.getInstance().postFrameCallback(
|
ReactChoreographer.getInstance().postFrameCallback(
|
||||||
ReactChoreographer.CallbackType.DISPATCH_UI, this);
|
ReactChoreographer.CallbackType.DISPATCH_UI, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void dispatchPendingNonBatchedOperations(long frameTimeNanos) {
|
||||||
|
while (true) {
|
||||||
|
long timeLeftInFrame = FRAME_TIME_MS - ((System.nanoTime() - frameTimeNanos) / 1000000);
|
||||||
|
if (timeLeftInFrame < MIN_TIME_LEFT_IN_FRAME_TO_SCHEDULE_MORE_WORK_MS) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
UIOperation nextOperation;
|
||||||
|
synchronized (mNonBatchedOperationsLock) {
|
||||||
|
if (mNonBatchedOperations.isEmpty()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
nextOperation = mNonBatchedOperations.pollFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
nextOperation.execute();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue