Store \"dirty\" and \"dirty descendant\" flags in every node instead of only marking root node as invalid

Summary: Right now invalidate always tell the root node that the tree is dirty, and next update will traverse the entire tree in search of changes. While this works correctly, it's not the most efficient implementation. It is more efficient to store dirty flag in every node, and skip entire subtrees if this node and all descendants are already up to date. This diff is a first step towards that optimization.

Reviewed By: ahmedre

Differential Revision: D2955197
This commit is contained in:
Denis Koroskin 2016-02-25 11:10:56 -08:00 committed by Ahmed El-Helw
parent 4c92a0b962
commit ca7a3519cf
4 changed files with 33 additions and 22 deletions

View File

@ -19,8 +19,6 @@ package com.facebook.react.flat;
signalBackingViewIsCreated();
}
private boolean mIsUpdated;
/**
* Returns true when this CSSNode tree needs to be re-laid out. If true, FlatUIImplementation
* will request LayoutEngine to perform a layout pass to update node boundaries. This is used
@ -29,19 +27,4 @@ package com.facebook.react.flat;
/* package */ boolean needsLayout() {
return isDirty();
}
/**
* Returns true if there are updates to the node tree other than layout (such as a change in
* background color) that would require StateBuilder to re-collect drawing state.
*/
/* package */ boolean isUpdated() {
return mIsUpdated;
}
/**
* Marks the node tree as requiring or not requiring a StateBuilder pass to collect drawing state.
*/
/* package */ void markUpdated(boolean isUpdated) {
mIsUpdated = isUpdated;
}
}

View File

@ -14,6 +14,7 @@ import javax.annotation.Nullable;
import com.facebook.infer.annotation.Assertions;
import com.facebook.react.uimanager.LayoutShadowNode;
import com.facebook.react.uimanager.OnLayoutEvent;
import com.facebook.react.uimanager.ReactShadowNode;
import com.facebook.react.uimanager.ReactStylesDiffMap;
import com.facebook.react.uimanager.ViewProps;
import com.facebook.react.uimanager.annotations.ReactProp;
@ -50,6 +51,7 @@ import com.facebook.react.uimanager.annotations.ReactProp;
private @Nullable DrawBackgroundColor mDrawBackground;
private int mMoveToIndexInParent;
private boolean mClipToBounds = false;
private boolean mIsUpdated = true;
// last OnLayoutEvent info, only used when shouldNotifyOnLayout() is true.
private int mLayoutX;
@ -142,7 +144,34 @@ import com.facebook.react.uimanager.annotations.ReactProp;
* color is changed).
*/
protected final void invalidate() {
((FlatRootShadowNode) getRootNode()).markUpdated(true);
FlatShadowNode node = this;
while (true) {
if (node.mountsToView()) {
if (node.mIsUpdated) {
// already updated
return;
}
node.mIsUpdated = true;
}
ReactShadowNode parent = node.getParent();
if (parent == null) {
// not attached to a hierarchy yet
return;
}
node = (FlatShadowNode) parent;
}
}
/* package */ final boolean isUpdated() {
return mIsUpdated;
}
/* package */ final void resetUpdated() {
mIsUpdated = false;
}
/**
@ -267,9 +296,7 @@ import com.facebook.react.uimanager.annotations.ReactProp;
if (mDrawView == null) {
mDrawView = DrawView.INSTANCE;
if (getParent() != null) {
invalidate();
}
invalidate();
// reset NodeRegion to allow it getting garbage-collected
mNodeRegion = NodeRegion.EMPTY;

View File

@ -418,7 +418,6 @@ public class FlatUIImplementation extends UIImplementation {
}
super.calculateRootLayout(rootNode);
rootNode.markUpdated(false);
mStateBuilder.applyUpdates(eventDispatcher, rootNode);
}

View File

@ -447,6 +447,8 @@ import com.facebook.react.uimanager.events.EventDispatcher;
isAndroidView,
needsCustomLayoutForChildren);
}
node.resetUpdated();
}
private void markLayoutSeenRecursively(ReactShadowNode node) {