From a879842033385dc342970692a987cdf08d407e2a Mon Sep 17 00:00:00 2001 From: Valentin Shergin Date: Tue, 8 May 2018 22:56:16 -0700 Subject: [PATCH] Fabric: Introducing `ShadowTree` Summary: ShadowTree is an abstraction around (commited) root shadow node and managing its lifecycle. Reviewed By: mdvacca Differential Revision: D7857049 fbshipit-source-id: 8d530e0366fc703e4aef4ec88dd8ea990dafaaf1 --- ReactCommon/fabric/uimanager/Scheduler.cpp | 78 ++++++------- ReactCommon/fabric/uimanager/Scheduler.h | 28 +++-- ReactCommon/fabric/uimanager/ShadowTree.cpp | 107 ++++++++++++++++++ ReactCommon/fabric/uimanager/ShadowTree.h | 87 ++++++++++++++ .../fabric/uimanager/ShadowTreeDelegate.h | 25 ++++ 5 files changed, 269 insertions(+), 56 deletions(-) create mode 100644 ReactCommon/fabric/uimanager/ShadowTree.cpp create mode 100644 ReactCommon/fabric/uimanager/ShadowTree.h create mode 100644 ReactCommon/fabric/uimanager/ShadowTreeDelegate.h diff --git a/ReactCommon/fabric/uimanager/Scheduler.cpp b/ReactCommon/fabric/uimanager/Scheduler.cpp index 6ad2f45f0..f4ffe53a3 100644 --- a/ReactCommon/fabric/uimanager/Scheduler.cpp +++ b/ReactCommon/fabric/uimanager/Scheduler.cpp @@ -19,7 +19,6 @@ namespace react { Scheduler::Scheduler() { auto componentDescriptorRegistry = std::make_shared(); - componentDescriptorRegistry->registerComponentDescriptor(std::make_shared()); componentDescriptorRegistry->registerComponentDescriptor(std::make_shared()); componentDescriptorRegistry->registerComponentDescriptor(std::make_shared()); @@ -34,12 +33,29 @@ Scheduler::~Scheduler() { } void Scheduler::registerRootTag(Tag rootTag) { - auto rootShadowNode = std::make_shared(rootTag, rootTag, nullptr); - rootNodeRegistry_.insert({rootTag, rootShadowNode}); + auto &&shadowTree = std::make_shared(rootTag); + shadowTree->setDelegate(this); + shadowTreeRegistry_.insert({rootTag, shadowTree}); } void Scheduler::unregisterRootTag(Tag rootTag) { - rootNodeRegistry_.erase(rootTag); + auto &&iterator = shadowTreeRegistry_.find(rootTag); + auto &&shadowTree = iterator->second; + assert(shadowTree); + shadowTree->setDelegate(nullptr); + shadowTreeRegistry_.erase(iterator); +} + +Size Scheduler::measure(const Tag &rootTag, const LayoutConstraints &layoutConstraints, const LayoutContext &layoutContext) const { + auto &&shadowTree = shadowTreeRegistry_.at(rootTag); + assert(shadowTree); + return shadowTree->measure(layoutConstraints, layoutContext); +} + +void Scheduler::constraintLayout(const Tag &rootTag, const LayoutConstraints &layoutConstraints, const LayoutContext &layoutContext) { + auto &&shadowTree = shadowTreeRegistry_.at(rootTag); + assert(shadowTree); + return shadowTree->constraintLayout(layoutConstraints, layoutContext); } #pragma mark - Delegate @@ -48,53 +64,25 @@ void Scheduler::setDelegate(SchedulerDelegate *delegate) { delegate_ = delegate; } -SchedulerDelegate *Scheduler::getDelegate() { +SchedulerDelegate *Scheduler::getDelegate() const { return delegate_; } +#pragma mark - ShadowTreeDelegate + +void Scheduler::shadowTreeDidCommit(const SharedShadowTree &shadowTree, const TreeMutationInstructionList &instructions) { + if (delegate_) { + delegate_->schedulerDidComputeMutationInstructions(shadowTree->getRootTag(), instructions); + } +} + #pragma mark - UIManagerDelegate void Scheduler::uiManagerDidFinishTransaction(Tag rootTag, const SharedShadowNodeUnsharedList &rootChildNodes) { - SharedRootShadowNode oldRootShadowNode = rootNodeRegistry_[rootTag]; - assert(oldRootShadowNode); - - SharedRootShadowNode newRootShadowNode = - std::make_shared(oldRootShadowNode, nullptr, SharedShadowNodeSharedList(rootChildNodes)); - - auto nonConstOldRootShadowNode = std::const_pointer_cast(oldRootShadowNode); - auto nonConstNewRootShadowNode = std::const_pointer_cast(newRootShadowNode); - - LayoutContext layoutContext = LayoutContext(); - - LOG(INFO) << "Old Shadow Tree: \n" << oldRootShadowNode->getDebugDescription(); - LOG(INFO) << "New Shadow Tree *before* layout: \n" << newRootShadowNode->getDebugDescription(); - - nonConstNewRootShadowNode->layout(layoutContext); - - nonConstNewRootShadowNode->sealRecursive(); - - LOG(INFO) << "New Shadow Tree *after* layout: \n" << nonConstNewRootShadowNode->getDebugDescription(); - - TreeMutationInstructionList instructions = TreeMutationInstructionList(); - - calculateMutationInstructions( - instructions, - oldRootShadowNode, - oldRootShadowNode->ShadowNode::getChildren(), - newRootShadowNode->ShadowNode::getChildren() - ); - - LOG(INFO) << "TreeMutationInstructionList:"; - - for (auto instruction : instructions) { - LOG(INFO) << "Instruction: " << instruction.getDebugDescription(); - } - - rootNodeRegistry_[rootTag] = newRootShadowNode; - - if (delegate_) { - delegate_->schedulerDidComputeMutationInstructions(rootTag, instructions); - } + auto &&iterator = shadowTreeRegistry_.find(rootTag); + auto &&shadowTree = iterator->second; + assert(shadowTree); + return shadowTree->complete(rootChildNodes); } void Scheduler::uiManagerDidCreateShadowNode(const SharedShadowNode &shadowNode) { diff --git a/ReactCommon/fabric/uimanager/Scheduler.h b/ReactCommon/fabric/uimanager/Scheduler.h index 5082f9bb6..cadbc4caa 100644 --- a/ReactCommon/fabric/uimanager/Scheduler.h +++ b/ReactCommon/fabric/uimanager/Scheduler.h @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include #include @@ -20,18 +22,21 @@ class FabricUIManager; * Scheduler coordinates Shadow Tree updates and event flows. */ class Scheduler: - public UIManagerDelegate { + public UIManagerDelegate, + public ShadowTreeDelegate { public: - Scheduler(); - virtual ~Scheduler(); -#pragma mark - Root Nodes Managerment + Scheduler(); + ~Scheduler(); + +#pragma mark - Shadow Tree Management void registerRootTag(Tag rootTag); void unregisterRootTag(Tag rootTag); - void setLayoutConstraints(Tag rootTag, LayoutConstraints layoutConstraints); + Size measure(const Tag &rootTag, const LayoutConstraints &layoutConstraints, const LayoutContext &layoutContext) const; + void constraintLayout(const Tag &rootTag, const LayoutConstraints &layoutConstraints, const LayoutContext &layoutContext); #pragma mark - Delegate @@ -41,13 +46,17 @@ public: * the pointer before being destroyed. */ void setDelegate(SchedulerDelegate *delegate); - SchedulerDelegate *getDelegate(); + SchedulerDelegate *getDelegate() const; #pragma mark - UIManagerDelegate void uiManagerDidFinishTransaction(Tag rootTag, const SharedShadowNodeUnsharedList &rootChildNodes) override; void uiManagerDidCreateShadowNode(const SharedShadowNode &shadowNode) override; +#pragma mark - ShadowTreeDelegate + + void shadowTreeDidCommit(const SharedShadowTree &shadowTree, const TreeMutationInstructionList &instructions) override; + #pragma mark - Deprecated /* @@ -56,13 +65,10 @@ public: std::shared_ptr getUIManager_DO_NOT_USE(); private: + SchedulerDelegate *delegate_; std::shared_ptr uiManager_; - - /* - * All commited `RootShadowNode` instances to differentiate against. - */ - std::unordered_map rootNodeRegistry_; + std::unordered_map shadowTreeRegistry_; }; } // namespace react diff --git a/ReactCommon/fabric/uimanager/ShadowTree.cpp b/ReactCommon/fabric/uimanager/ShadowTree.cpp new file mode 100644 index 000000000..66d2e2306 --- /dev/null +++ b/ReactCommon/fabric/uimanager/ShadowTree.cpp @@ -0,0 +1,107 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#include "ShadowTree.h" + +#include +#include + +#include "ShadowTreeDelegate.h" +#include "Differentiator.h" +#include "TreeMutationInstruction.h" + +namespace facebook { +namespace react { + +ShadowTree::ShadowTree(Tag rootTag): + rootTag_(rootTag) { + + rootShadowNode_ = std::make_shared( + rootTag, + rootTag, + nullptr, + RootShadowNode::defaultSharedProps() + ); +} + +Tag ShadowTree::getRootTag() const { + return rootTag_; +} + +#pragma mark - Layout + +Size ShadowTree::measure(const LayoutConstraints &layoutConstraints, const LayoutContext &layoutContext) const { + auto newRootShadowNode = cloneRootShadowNode(layoutConstraints, layoutContext); + newRootShadowNode->layout(); + return newRootShadowNode->getLayoutMetrics().frame.size; +} + +void ShadowTree::constraintLayout(const LayoutConstraints &layoutConstraints, const LayoutContext &layoutContext) { + auto newRootShadowNode = cloneRootShadowNode(layoutConstraints, layoutContext); + complete(newRootShadowNode); +} + +#pragma mark - Commiting + +UnsharedRootShadowNode ShadowTree::cloneRootShadowNode(const LayoutConstraints &layoutConstraints, const LayoutContext &layoutContext) const { + auto oldRootShadowNode = rootShadowNode_; + auto &&props = std::make_shared(*oldRootShadowNode->getProps()); + props->applyLayoutConstraints(layoutConstraints); + props->applyLayoutContext(layoutContext); + auto newRootShadowNode = std::make_shared(oldRootShadowNode, props, nullptr); + return newRootShadowNode; +} + +void ShadowTree::complete(const SharedShadowNodeUnsharedList &rootChildNodes) { + auto oldRootShadowNode = rootShadowNode_; + auto newRootShadowNode = + std::make_shared(oldRootShadowNode, nullptr, SharedShadowNodeSharedList(rootChildNodes)); + + complete(newRootShadowNode); +} + +void ShadowTree::complete(UnsharedRootShadowNode newRootShadowNode) { + SharedRootShadowNode oldRootShadowNode = rootShadowNode_; + + newRootShadowNode->layout(); + + newRootShadowNode->sealRecursive(); + + TreeMutationInstructionList instructions = TreeMutationInstructionList(); + + calculateMutationInstructions( + instructions, + oldRootShadowNode, + oldRootShadowNode->ShadowNode::getChildren(), + newRootShadowNode->ShadowNode::getChildren() + ); + + if (commit(newRootShadowNode)) { + if (delegate_) { + delegate_->shadowTreeDidCommit(shared_from_this(), instructions); + } + } +} + +bool ShadowTree::commit(const SharedRootShadowNode &newRootShadowNode) { + std::lock_guard lock(commitMutex_); + + if (newRootShadowNode->getSourceNode() != rootShadowNode_) { + return false; + } + + rootShadowNode_ = newRootShadowNode; + return true; +} + +#pragma mark - Delegate + +void ShadowTree::setDelegate(ShadowTreeDelegate *delegate) { + delegate_ = delegate; +} + +ShadowTreeDelegate *ShadowTree::getDelegate() const { + return delegate_; +} + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/uimanager/ShadowTree.h b/ReactCommon/fabric/uimanager/ShadowTree.h new file mode 100644 index 000000000..e2abea7a3 --- /dev/null +++ b/ReactCommon/fabric/uimanager/ShadowTree.h @@ -0,0 +1,87 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace facebook { +namespace react { + +class ShadowTree; + +using SharedShadowTree = std::shared_ptr; + +/* + * Represents the shadow tree and its lifecycle. + */ +class ShadowTree final: + public std::enable_shared_from_this { + +public: + + /* + * Creates a new shadow tree instance with given `rootTag`. + */ + ShadowTree(Tag rootTag); + + /* + * Returns the rootTag associated with the shadow tree (the tag of the + * root shadow node). + */ + Tag getRootTag() const; + +#pragma mark - Layout + + /* + * Measures the shadow tree with given `layoutConstraints` and `layoutContext`. + * Can be called from any thread, side-effect-less. + */ + Size measure(const LayoutConstraints &layoutConstraints, const LayoutContext &layoutContext) const; + + /* + * Applies given `layoutConstraints` and `layoutContext` and commit + * the new shadow tree. + * Can be called from any thread. + */ + void constraintLayout(const LayoutConstraints &layoutConstraints, const LayoutContext &layoutContext); + +#pragma mark - Application + + /* + * Create a new shadow tree with given `rootChildNodes` and commit. + * Can be called from any thread. + */ + void complete(const SharedShadowNodeUnsharedList &rootChildNodes); + +#pragma mark - Delegate + + /* + * Sets and gets the delegate. + * The delegate is stored as a raw pointer, so the owner must null + * the pointer before being destroyed. + */ + void setDelegate(ShadowTreeDelegate *delegate); + ShadowTreeDelegate *getDelegate() const; + +private: + + UnsharedRootShadowNode cloneRootShadowNode(const LayoutConstraints &layoutConstraints, const LayoutContext &layoutContext) const; + void complete(UnsharedRootShadowNode newRootShadowNode); + bool commit(const SharedRootShadowNode &newRootShadowNode); + + const Tag rootTag_; + SharedRootShadowNode rootShadowNode_; + ShadowTreeDelegate *delegate_; + mutable std::mutex commitMutex_ {}; +}; + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/uimanager/ShadowTreeDelegate.h b/ReactCommon/fabric/uimanager/ShadowTreeDelegate.h new file mode 100644 index 000000000..59ca85f46 --- /dev/null +++ b/ReactCommon/fabric/uimanager/ShadowTreeDelegate.h @@ -0,0 +1,25 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#pragma once + +#include + +namespace facebook { +namespace react { + +class ShadowTree; + +/* + * Abstract class for ShadowTree's delegate. + */ +class ShadowTreeDelegate { +public: + + /* + * Called right after Shadow Tree commit a new state of the the tree. + */ + virtual void shadowTreeDidCommit(const std::shared_ptr &shadowTree, const TreeMutationInstructionList &instructions) = 0; +}; + +} // namespace react +} // namespace facebook