mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-01-12 22:50:10 +08:00
Fabric: A new way to compute nodes for onLayout event
Summary: Previously we computed the list of nodes that need to be notified about layout changes using a list of mutation instructions. That was fine, but that's not really compatible with some other changes that I plan to make, so I decided to change it (make it better). Besides the better design (debatable; fewer dependencies to unrelated moving pieces), here is why I believe the new way is more performant: * The new approach has no `dynamic_casts`, whereas the previous has tons of them (two per a mutation). If a `dynamic_cast` takes 10 ns, for 500 nodes it can take up to 5ms only for casts. (Non-scientific assumption.) * After removing dependency to mutation instruction, we can enable flattening for views which have `onLayout` event. Reviewed By: mdvacca Differential Revision: D15110725 fbshipit-source-id: 31a657ccfd02441734ad1d71a833653223163289
This commit is contained in:
committed by
Facebook Github Bot
parent
af5633bcba
commit
ea8a57116f
@@ -15,10 +15,15 @@ namespace react {
|
||||
|
||||
const char RootComponentName[] = "RootView";
|
||||
|
||||
void RootShadowNode::layout() {
|
||||
void RootShadowNode::layout(
|
||||
std::vector<LayoutableShadowNode const *> *affectedNodes) {
|
||||
SystraceSection s("RootShadowNode::layout");
|
||||
ensureUnsealed();
|
||||
layout(getProps()->layoutContext);
|
||||
|
||||
auto layoutContext = getProps()->layoutContext;
|
||||
layoutContext.affectedNodes = affectedNodes;
|
||||
|
||||
layout(layoutContext);
|
||||
|
||||
// This is the rare place where shadow node must layout (set `layoutMetrics`)
|
||||
// itself because there is no a parent node which usually should do it.
|
||||
|
||||
@@ -37,7 +37,7 @@ class RootShadowNode final
|
||||
/*
|
||||
* Layouts the shadow tree.
|
||||
*/
|
||||
void layout();
|
||||
void layout(std::vector<LayoutableShadowNode const *> *affectedNodes = {});
|
||||
|
||||
/*
|
||||
* Clones the node with given `layoutConstraints` and `layoutContext`.
|
||||
|
||||
@@ -199,7 +199,11 @@ void YogaLayoutableShadowNode::layoutChildren(LayoutContext layoutContext) {
|
||||
assert(childYogaNode->getOwner() == &yogaNode_);
|
||||
|
||||
childNode->ensureUnsealed();
|
||||
childNode->setLayoutMetrics(childLayoutMetrics);
|
||||
auto affected = childNode->setLayoutMetrics(childLayoutMetrics);
|
||||
|
||||
if (affected && layoutContext.affectedNodes) {
|
||||
layoutContext.affectedNodes->push_back(childNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <react/core/LayoutableShadowNode.h>
|
||||
#include <react/graphics/Geometry.h>
|
||||
|
||||
namespace facebook {
|
||||
@@ -28,7 +31,16 @@ struct LayoutContext {
|
||||
* Some layout systems *might* use this to round layout metric values
|
||||
* to `pixel value`.
|
||||
*/
|
||||
Float pointScaleFactor = {1.0};
|
||||
Float pointScaleFactor{1.0};
|
||||
|
||||
/*
|
||||
* A raw pointer to list of raw pointers to `LayoutableShadowNode`s that were
|
||||
* affected by the re-layout pass. If the field is not `nullptr`, a particular
|
||||
* `LayoutableShadowNode` implementation should add mutated nodes to this
|
||||
* list. The order is not specified. Nothing in this collection is owing (on
|
||||
* purpose), make sure the memory is managed responsibly.
|
||||
*/
|
||||
std::vector<LayoutableShadowNode const *> *affectedNodes{};
|
||||
};
|
||||
|
||||
} // namespace react
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <glog/logging.h>
|
||||
|
||||
#include <react/components/root/RootComponentDescriptor.h>
|
||||
#include <react/components/view/ViewShadowNode.h>
|
||||
#include <react/core/LayoutContext.h>
|
||||
#include <react/core/LayoutPrimitives.h>
|
||||
#include <react/debug/SystraceSection.h>
|
||||
@@ -163,8 +164,11 @@ bool ShadowTree::tryCommit(
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<LayoutableShadowNode const *> affectedLayoutableNodes{};
|
||||
affectedLayoutableNodes.reserve(1024);
|
||||
|
||||
long layoutTime = getTime();
|
||||
newRootShadowNode->layout();
|
||||
newRootShadowNode->layout(&affectedLayoutableNodes);
|
||||
layoutTime = getTime() - layoutTime;
|
||||
newRootShadowNode->sealRecursive();
|
||||
|
||||
@@ -210,7 +214,7 @@ bool ShadowTree::tryCommit(
|
||||
#endif
|
||||
}
|
||||
|
||||
emitLayoutEvents(mutations);
|
||||
emitLayoutEvents(affectedLayoutableNodes);
|
||||
|
||||
if (delegate_) {
|
||||
delegate_->shadowTreeDidCommit(
|
||||
@@ -225,43 +229,25 @@ bool ShadowTree::tryCommit(
|
||||
}
|
||||
|
||||
void ShadowTree::emitLayoutEvents(
|
||||
const ShadowViewMutationList &mutations) const {
|
||||
std::vector<LayoutableShadowNode const *> &affectedLayoutableNodes) const {
|
||||
SystraceSection s("ShadowTree::emitLayoutEvents");
|
||||
|
||||
for (const auto &mutation : mutations) {
|
||||
// Only `Insert` and `Update` mutations can affect layout metrics.
|
||||
if (mutation.type != ShadowViewMutation::Insert &&
|
||||
mutation.type != ShadowViewMutation::Update) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto viewEventEmitter =
|
||||
std::dynamic_pointer_cast<const ViewEventEmitter>(
|
||||
mutation.newChildShadowView.eventEmitter);
|
||||
|
||||
// Checking if particular shadow node supports `onLayout` event (part of
|
||||
// `ViewEventEmitter`).
|
||||
if (!viewEventEmitter) {
|
||||
continue;
|
||||
}
|
||||
for (auto const *layoutableNode : affectedLayoutableNodes) {
|
||||
// Only instances of `ViewShadowNode` (and subclasses) are supported.
|
||||
auto const &viewShadowNode =
|
||||
static_cast<ViewShadowNode const &>(*layoutableNode);
|
||||
auto const &viewEventEmitter = static_cast<ViewEventEmitter const &>(
|
||||
*viewShadowNode.getEventEmitter());
|
||||
|
||||
// Checking if the `onLayout` event was requested for the particular Shadow
|
||||
// Node.
|
||||
const auto viewProps = std::dynamic_pointer_cast<const ViewProps>(
|
||||
mutation.newChildShadowView.props);
|
||||
if (viewProps && !viewProps->onLayout) {
|
||||
auto const &viewProps =
|
||||
static_cast<ViewProps const &>(*viewShadowNode.getProps());
|
||||
if (!viewProps.onLayout) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// In case if we have `oldChildShadowView`, checking that layout metrics
|
||||
// have changed.
|
||||
if (mutation.type != ShadowViewMutation::Update &&
|
||||
mutation.oldChildShadowView.layoutMetrics ==
|
||||
mutation.newChildShadowView.layoutMetrics) {
|
||||
continue;
|
||||
}
|
||||
|
||||
viewEventEmitter->onLayout(mutation.newChildShadowView.layoutMetrics);
|
||||
viewEventEmitter.onLayout(layoutableNode->getLayoutMetrics());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -82,7 +82,8 @@ class ShadowTree final {
|
||||
const LayoutConstraints &layoutConstraints,
|
||||
const LayoutContext &layoutContext) const;
|
||||
|
||||
void emitLayoutEvents(const ShadowViewMutationList &mutations) const;
|
||||
void emitLayoutEvents(
|
||||
std::vector<LayoutableShadowNode const *> &affectedLayoutableNodes) const;
|
||||
|
||||
const SurfaceId surfaceId_;
|
||||
mutable better::shared_mutex commitMutex_;
|
||||
|
||||
Reference in New Issue
Block a user