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:
Valentin Shergin
2019-05-01 16:24:23 -07:00
committed by Facebook Github Bot
parent af5633bcba
commit ea8a57116f
6 changed files with 45 additions and 37 deletions

View File

@@ -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.

View File

@@ -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`.

View File

@@ -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);
}
}
}

View File

@@ -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

View File

@@ -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());
}
}

View File

@@ -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_;