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
This commit is contained in:
Valentin Shergin 2018-05-08 22:56:16 -07:00 committed by Facebook Github Bot
parent 2bb41031ba
commit a879842033
5 changed files with 269 additions and 56 deletions

View File

@ -19,7 +19,6 @@ namespace react {
Scheduler::Scheduler() { Scheduler::Scheduler() {
auto componentDescriptorRegistry = std::make_shared<ComponentDescriptorRegistry>(); auto componentDescriptorRegistry = std::make_shared<ComponentDescriptorRegistry>();
componentDescriptorRegistry->registerComponentDescriptor(std::make_shared<ViewComponentDescriptor>()); componentDescriptorRegistry->registerComponentDescriptor(std::make_shared<ViewComponentDescriptor>());
componentDescriptorRegistry->registerComponentDescriptor(std::make_shared<ParagraphComponentDescriptor>()); componentDescriptorRegistry->registerComponentDescriptor(std::make_shared<ParagraphComponentDescriptor>());
componentDescriptorRegistry->registerComponentDescriptor(std::make_shared<TextComponentDescriptor>()); componentDescriptorRegistry->registerComponentDescriptor(std::make_shared<TextComponentDescriptor>());
@ -34,12 +33,29 @@ Scheduler::~Scheduler() {
} }
void Scheduler::registerRootTag(Tag rootTag) { void Scheduler::registerRootTag(Tag rootTag) {
auto rootShadowNode = std::make_shared<RootShadowNode>(rootTag, rootTag, nullptr); auto &&shadowTree = std::make_shared<ShadowTree>(rootTag);
rootNodeRegistry_.insert({rootTag, rootShadowNode}); shadowTree->setDelegate(this);
shadowTreeRegistry_.insert({rootTag, shadowTree});
} }
void Scheduler::unregisterRootTag(Tag rootTag) { 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 #pragma mark - Delegate
@ -48,53 +64,25 @@ void Scheduler::setDelegate(SchedulerDelegate *delegate) {
delegate_ = delegate; delegate_ = delegate;
} }
SchedulerDelegate *Scheduler::getDelegate() { SchedulerDelegate *Scheduler::getDelegate() const {
return delegate_; return delegate_;
} }
#pragma mark - ShadowTreeDelegate
void Scheduler::shadowTreeDidCommit(const SharedShadowTree &shadowTree, const TreeMutationInstructionList &instructions) {
if (delegate_) {
delegate_->schedulerDidComputeMutationInstructions(shadowTree->getRootTag(), instructions);
}
}
#pragma mark - UIManagerDelegate #pragma mark - UIManagerDelegate
void Scheduler::uiManagerDidFinishTransaction(Tag rootTag, const SharedShadowNodeUnsharedList &rootChildNodes) { void Scheduler::uiManagerDidFinishTransaction(Tag rootTag, const SharedShadowNodeUnsharedList &rootChildNodes) {
SharedRootShadowNode oldRootShadowNode = rootNodeRegistry_[rootTag]; auto &&iterator = shadowTreeRegistry_.find(rootTag);
assert(oldRootShadowNode); auto &&shadowTree = iterator->second;
assert(shadowTree);
SharedRootShadowNode newRootShadowNode = return shadowTree->complete(rootChildNodes);
std::make_shared<RootShadowNode>(oldRootShadowNode, nullptr, SharedShadowNodeSharedList(rootChildNodes));
auto nonConstOldRootShadowNode = std::const_pointer_cast<RootShadowNode>(oldRootShadowNode);
auto nonConstNewRootShadowNode = std::const_pointer_cast<RootShadowNode>(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);
}
} }
void Scheduler::uiManagerDidCreateShadowNode(const SharedShadowNode &shadowNode) { void Scheduler::uiManagerDidCreateShadowNode(const SharedShadowNode &shadowNode) {

View File

@ -8,6 +8,8 @@
#include <fabric/core/LayoutConstraints.h> #include <fabric/core/LayoutConstraints.h>
#include <fabric/uimanager/SchedulerDelegate.h> #include <fabric/uimanager/SchedulerDelegate.h>
#include <fabric/uimanager/UIManagerDelegate.h> #include <fabric/uimanager/UIManagerDelegate.h>
#include <fabric/uimanager/ShadowTree.h>
#include <fabric/uimanager/ShadowTreeDelegate.h>
#include <fabric/view/ViewShadowNode.h> #include <fabric/view/ViewShadowNode.h>
#include <fabric/view/RootShadowNode.h> #include <fabric/view/RootShadowNode.h>
@ -20,18 +22,21 @@ class FabricUIManager;
* Scheduler coordinates Shadow Tree updates and event flows. * Scheduler coordinates Shadow Tree updates and event flows.
*/ */
class Scheduler: class Scheduler:
public UIManagerDelegate { public UIManagerDelegate,
public ShadowTreeDelegate {
public: public:
Scheduler();
virtual ~Scheduler();
#pragma mark - Root Nodes Managerment Scheduler();
~Scheduler();
#pragma mark - Shadow Tree Management
void registerRootTag(Tag rootTag); void registerRootTag(Tag rootTag);
void unregisterRootTag(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 #pragma mark - Delegate
@ -41,13 +46,17 @@ public:
* the pointer before being destroyed. * the pointer before being destroyed.
*/ */
void setDelegate(SchedulerDelegate *delegate); void setDelegate(SchedulerDelegate *delegate);
SchedulerDelegate *getDelegate(); SchedulerDelegate *getDelegate() const;
#pragma mark - UIManagerDelegate #pragma mark - UIManagerDelegate
void uiManagerDidFinishTransaction(Tag rootTag, const SharedShadowNodeUnsharedList &rootChildNodes) override; void uiManagerDidFinishTransaction(Tag rootTag, const SharedShadowNodeUnsharedList &rootChildNodes) override;
void uiManagerDidCreateShadowNode(const SharedShadowNode &shadowNode) override; void uiManagerDidCreateShadowNode(const SharedShadowNode &shadowNode) override;
#pragma mark - ShadowTreeDelegate
void shadowTreeDidCommit(const SharedShadowTree &shadowTree, const TreeMutationInstructionList &instructions) override;
#pragma mark - Deprecated #pragma mark - Deprecated
/* /*
@ -56,13 +65,10 @@ public:
std::shared_ptr<FabricUIManager> getUIManager_DO_NOT_USE(); std::shared_ptr<FabricUIManager> getUIManager_DO_NOT_USE();
private: private:
SchedulerDelegate *delegate_; SchedulerDelegate *delegate_;
std::shared_ptr<FabricUIManager> uiManager_; std::shared_ptr<FabricUIManager> uiManager_;
std::unordered_map<Tag, SharedShadowTree> shadowTreeRegistry_;
/*
* All commited `RootShadowNode` instances to differentiate against.
*/
std::unordered_map<Tag, SharedRootShadowNode> rootNodeRegistry_;
}; };
} // namespace react } // namespace react

View File

@ -0,0 +1,107 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include "ShadowTree.h"
#include <fabric/core/LayoutContext.h>
#include <fabric/core/LayoutPrimitives.h>
#include "ShadowTreeDelegate.h"
#include "Differentiator.h"
#include "TreeMutationInstruction.h"
namespace facebook {
namespace react {
ShadowTree::ShadowTree(Tag rootTag):
rootTag_(rootTag) {
rootShadowNode_ = std::make_shared<RootShadowNode>(
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<RootProps>(*oldRootShadowNode->getProps());
props->applyLayoutConstraints(layoutConstraints);
props->applyLayoutContext(layoutContext);
auto newRootShadowNode = std::make_shared<RootShadowNode>(oldRootShadowNode, props, nullptr);
return newRootShadowNode;
}
void ShadowTree::complete(const SharedShadowNodeUnsharedList &rootChildNodes) {
auto oldRootShadowNode = rootShadowNode_;
auto newRootShadowNode =
std::make_shared<RootShadowNode>(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<std::mutex> 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

View File

@ -0,0 +1,87 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <memory>
#include <mutex>
#include <stdatomic.h>
#include <fabric/core/LayoutConstraints.h>
#include <fabric/core/ReactPrimitives.h>
#include <fabric/core/ShadowNode.h>
#include <fabric/uimanager/ShadowTreeDelegate.h>
#include <fabric/view/RootShadowNode.h>
namespace facebook {
namespace react {
class ShadowTree;
using SharedShadowTree = std::shared_ptr<ShadowTree>;
/*
* Represents the shadow tree and its lifecycle.
*/
class ShadowTree final:
public std::enable_shared_from_this<ShadowTree> {
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

View File

@ -0,0 +1,25 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <fabric/uimanager/TreeMutationInstruction.h>
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> &shadowTree, const TreeMutationInstructionList &instructions) = 0;
};
} // namespace react
} // namespace facebook