From ca7a3519cfe78140cddaca7aa49af03b4b118939 Mon Sep 17 00:00:00 2001 From: Denis Koroskin Date: Thu, 25 Feb 2016 11:10:56 -0800 Subject: [PATCH] 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 --- .../react/flat/FlatRootShadowNode.java | 17 --------- .../facebook/react/flat/FlatShadowNode.java | 35 ++++++++++++++++--- .../react/flat/FlatUIImplementation.java | 1 - .../com/facebook/react/flat/StateBuilder.java | 2 ++ 4 files changed, 33 insertions(+), 22 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatRootShadowNode.java b/ReactAndroid/src/main/java/com/facebook/react/flat/FlatRootShadowNode.java index 6db411c60..b976b4c1b 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatRootShadowNode.java +++ b/ReactAndroid/src/main/java/com/facebook/react/flat/FlatRootShadowNode.java @@ -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; - } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatShadowNode.java b/ReactAndroid/src/main/java/com/facebook/react/flat/FlatShadowNode.java index e14be37f2..6197784b1 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatShadowNode.java +++ b/ReactAndroid/src/main/java/com/facebook/react/flat/FlatShadowNode.java @@ -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; diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatUIImplementation.java b/ReactAndroid/src/main/java/com/facebook/react/flat/FlatUIImplementation.java index dccc59d6c..6792714df 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatUIImplementation.java +++ b/ReactAndroid/src/main/java/com/facebook/react/flat/FlatUIImplementation.java @@ -418,7 +418,6 @@ public class FlatUIImplementation extends UIImplementation { } super.calculateRootLayout(rootNode); - rootNode.markUpdated(false); mStateBuilder.applyUpdates(eventDispatcher, rootNode); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/StateBuilder.java b/ReactAndroid/src/main/java/com/facebook/react/flat/StateBuilder.java index f92c4a704..310b057a0 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/StateBuilder.java +++ b/ReactAndroid/src/main/java/com/facebook/react/flat/StateBuilder.java @@ -447,6 +447,8 @@ import com.facebook.react.uimanager.events.EventDispatcher; isAndroidView, needsCustomLayoutForChildren); } + + node.resetUpdated(); } private void markLayoutSeenRecursively(ReactShadowNode node) {