Revert D17486030: C++ Fabric Core LayoutAnimations

Differential Revision:
D17486030

Original commit changeset: 95c72cf9fc2b

fbshipit-source-id: fa7ef058f5d0dea0154c62718a8a11d9330698d9
This commit is contained in:
Ishan Khot 2020-05-20 16:31:18 -07:00 коммит произвёл Facebook GitHub Bot
Родитель 6a15d7487f
Коммит eb504e613e
26 изменённых файлов: 66 добавлений и 1658 удалений

Просмотреть файл

@ -70,7 +70,7 @@ class SchedulerDelegateProxy : public SchedulerDelegate {
{
if (self = [super init]) {
_delegateProxy = std::make_shared<SchedulerDelegateProxy>((__bridge void *)self);
_scheduler = std::make_shared<Scheduler>(toolbox, nullptr, _delegateProxy.get());
_scheduler = std::make_shared<Scheduler>(toolbox, _delegateProxy.get());
}
return self;

Просмотреть файл

@ -288,7 +288,7 @@ void Binding::installFabricUIManager(
toolbox.runtimeExecutor = runtimeExecutor;
toolbox.synchronousEventBeatFactory = synchronousBeatFactory;
toolbox.asynchronousEventBeatFactory = asynchronousBeatFactory;
scheduler_ = std::make_shared<Scheduler>(toolbox, nullptr, this);
scheduler_ = std::make_shared<Scheduler>(toolbox, this);
}
void Binding::uninstallFabricUIManager() {

Просмотреть файл

@ -1,95 +0,0 @@
load("@fbsource//tools/build_defs/apple:flag_defs.bzl", "get_preprocessor_flags_for_build_mode")
load(
"//tools/build_defs/oss:rn_defs.bzl",
"ANDROID",
"APPLE",
"CXX",
"fb_xplat_cxx_test",
"get_apple_compiler_flags",
"get_apple_inspector_flags",
"react_native_xplat_target",
"rn_xplat_cxx_library",
"subdir_glob",
)
APPLE_COMPILER_FLAGS = get_apple_compiler_flags()
rn_xplat_cxx_library(
name = "animations",
srcs = glob(
["**/*.cpp"],
exclude = glob(["tests/**/*.cpp"]),
),
headers = glob(
["**/*.h"],
exclude = glob(["tests/**/*.h"]),
),
header_namespace = "",
exported_headers = subdir_glob(
[
("", "*.h"),
],
prefix = "react/animations",
),
compiler_flags = [
"-fexceptions",
"-frtti",
"-std=c++14",
"-Wall",
],
fbobjc_compiler_flags = APPLE_COMPILER_FLAGS,
fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(),
force_static = True,
labels = ["supermodule:xplat/default/public.react_native.infra"],
macosx_tests_override = [],
platforms = (ANDROID, APPLE, CXX),
preprocessor_flags = [
"-DLOG_TAG=\"ReactNative\"",
"-DWITH_FBSYSTRACE=1",
],
tests = [":tests"],
visibility = ["PUBLIC"],
deps = [
"//third-party/glog:glog",
"//xplat/fbsystrace:fbsystrace",
"//xplat/folly:headers_only",
"//xplat/folly:memory",
"//xplat/folly:molly",
"//xplat/jsi:JSIDynamic",
"//xplat/jsi:jsi",
react_native_xplat_target("config:config"),
react_native_xplat_target("fabric/componentregistry:componentregistry"),
react_native_xplat_target("fabric/components/view:view"),
react_native_xplat_target("fabric/core:core"),
react_native_xplat_target("fabric/debug:debug"),
react_native_xplat_target("fabric/mounting:mounting"),
react_native_xplat_target("fabric/uimanager:uimanager"),
react_native_xplat_target("runtimeexecutor:runtimeexecutor"),
],
)
fb_xplat_cxx_test(
name = "tests",
srcs = glob(["tests/**/*.cpp"]),
headers = glob(["tests/**/*.h"]),
compiler_flags = [
"-fexceptions",
"-frtti",
"-std=c++14",
"-Wall",
],
contacts = ["oncall+react_native@xmail.facebook.com"],
platforms = (ANDROID, APPLE, CXX),
deps = [
":animations",
"//xplat/folly:molly",
"//xplat/third-party/gmock:gtest",
react_native_xplat_target("config:config"),
react_native_xplat_target("fabric/components/activityindicator:activityindicator"),
react_native_xplat_target("fabric/components/image:image"),
react_native_xplat_target("fabric/components/root:root"),
react_native_xplat_target("fabric/components/scrollview:scrollview"),
react_native_xplat_target("fabric/components/view:view"),
"//xplat/js/react-native-github:generated_components-rncore",
],
)

Просмотреть файл

@ -1,197 +0,0 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "LayoutAnimationDriver.h"
#include <algorithm>
#include <chrono>
#include <react/componentregistry/ComponentDescriptorFactory.h>
#include <react/components/root/RootShadowNode.h>
#include <react/components/view/ViewProps.h>
#include <react/core/ComponentDescriptor.h>
#include <react/core/LayoutMetrics.h>
#include <react/core/LayoutableShadowNode.h>
#include <react/core/Props.h>
#include <react/mounting/MountingCoordinator.h>
#include <react/mounting/Differentiator.h>
#include <react/mounting/ShadowTreeRevision.h>
#include <react/mounting/ShadowView.h>
#include <react/mounting/ShadowViewMutation.h>
#include <glog/logging.h>
namespace facebook {
namespace react {
static double
getProgressFromValues(double start, double end, double currentValue) {
auto opacityMinmax = std::minmax({start, end});
auto min = opacityMinmax.first;
auto max = opacityMinmax.second;
return (
currentValue < min
? 0
: (currentValue > max ? 0 : ((max - currentValue) / (max - min))));
}
/**
* Given an animation and a ShadowView with properties set on it, detect how
* far through the animation the ShadowView has progressed.
*
* @param mutationsList
* @param now
*/
double LayoutAnimationDriver::getProgressThroughAnimation(
AnimationKeyFrame const &keyFrame,
LayoutAnimation const *layoutAnimation,
ShadowView const &animationStateView) const {
auto layoutAnimationConfig = layoutAnimation->layoutAnimationConfig;
auto const mutationConfig =
*(keyFrame.type == AnimationConfigurationType::Delete
? layoutAnimationConfig.deleteConfig
: (keyFrame.type == AnimationConfigurationType::Create
? layoutAnimationConfig.createConfig
: layoutAnimationConfig.updateConfig));
auto initialProps = keyFrame.viewStart.props;
auto finalProps = keyFrame.viewEnd.props;
if (mutationConfig.animationProperty == AnimationProperty::Opacity) {
// Detect progress through opacity animation.
const auto &oldViewProps =
dynamic_cast<const ViewProps *>(initialProps.get());
const auto &newViewProps =
dynamic_cast<const ViewProps *>(finalProps.get());
const auto &animationStateViewProps =
dynamic_cast<const ViewProps *>(animationStateView.props.get());
if (oldViewProps != nullptr && newViewProps != nullptr &&
animationStateViewProps != nullptr) {
return getProgressFromValues(
oldViewProps->opacity,
newViewProps->opacity,
animationStateViewProps->opacity);
}
} else if (
mutationConfig.animationProperty != AnimationProperty::NotApplicable) {
// Detect progress through layout animation.
LayoutMetrics const &finalLayoutMetrics = keyFrame.viewEnd.layoutMetrics;
LayoutMetrics const &baselineLayoutMetrics =
keyFrame.viewStart.layoutMetrics;
LayoutMetrics const &animationStateLayoutMetrics =
animationStateView.layoutMetrics;
if (baselineLayoutMetrics.frame.size.height !=
finalLayoutMetrics.frame.size.height) {
return getProgressFromValues(
baselineLayoutMetrics.frame.size.height,
finalLayoutMetrics.frame.size.height,
animationStateLayoutMetrics.frame.size.height);
}
if (baselineLayoutMetrics.frame.size.width !=
finalLayoutMetrics.frame.size.width) {
return getProgressFromValues(
baselineLayoutMetrics.frame.size.width,
finalLayoutMetrics.frame.size.width,
animationStateLayoutMetrics.frame.size.width);
}
if (baselineLayoutMetrics.frame.origin.x !=
finalLayoutMetrics.frame.origin.x) {
return getProgressFromValues(
baselineLayoutMetrics.frame.origin.x,
finalLayoutMetrics.frame.origin.x,
animationStateLayoutMetrics.frame.origin.x);
}
if (baselineLayoutMetrics.frame.origin.y !=
finalLayoutMetrics.frame.origin.y) {
return getProgressFromValues(
baselineLayoutMetrics.frame.origin.y,
finalLayoutMetrics.frame.origin.y,
animationStateLayoutMetrics.frame.origin.y);
}
}
return 0;
}
void LayoutAnimationDriver::animationMutationsForFrame(
SurfaceId surfaceId,
ShadowViewMutation::List &mutationsList,
uint64_t now) const {
for (auto &animation : inflightAnimations_) {
if (animation.surfaceId != surfaceId) {
continue;
}
int incompleteAnimations = 0;
for (const auto &keyframe : animation.keyFrames) {
if (keyframe.type == AnimationConfigurationType::Noop) {
continue;
}
auto const &baselineShadowView = keyframe.viewStart;
auto const &finalShadowView = keyframe.viewEnd;
// The contract with the "keyframes generation" phase is that any animated
// node will have a valid configuration.
auto const layoutAnimationConfig = animation.layoutAnimationConfig;
auto const mutationConfig =
(keyframe.type == AnimationConfigurationType::Delete
? layoutAnimationConfig.deleteConfig
: (keyframe.type == AnimationConfigurationType::Create
? layoutAnimationConfig.createConfig
: layoutAnimationConfig.updateConfig));
// Interpolate
std::pair<double, double> progress =
calculateAnimationProgress(now, animation, *mutationConfig);
double animationTimeProgressLinear = progress.first;
double animationInterpolationFactor = progress.second;
auto mutatedShadowView = createInterpolatedShadowView(
animationInterpolationFactor,
*mutationConfig,
baselineShadowView,
finalShadowView);
// Create the mutation instruction
mutationsList.push_back(ShadowViewMutation::UpdateMutation(
keyframe.parentView, baselineShadowView, mutatedShadowView, -1));
if (animationTimeProgressLinear < 1) {
incompleteAnimations++;
}
}
// Are there no ongoing mutations left in this animation?
if (incompleteAnimations == 0) {
animation.completed = true;
}
}
// Clear out finished animations
for (auto it = inflightAnimations_.begin();
it != inflightAnimations_.end();) {
const auto &animation = *it;
if (animation.completed) {
// Queue up "final" mutations for all keyframes in the completed animation
for (auto const &keyframe : animation.keyFrames) {
if (keyframe.finalMutationForKeyFrame.hasValue()) {
mutationsList.push_back(*keyframe.finalMutationForKeyFrame);
}
}
it = inflightAnimations_.erase(it);
} else {
it++;
}
}
}
} // namespace react
} // namespace facebook

Просмотреть файл

@ -1,40 +0,0 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <react/core/EventTarget.h>
#include <react/mounting/Differentiator.h>
#include <react/mounting/MountingCoordinator.h>
#include <react/mounting/MountingOverrideDelegate.h>
#include <react/mounting/MountingTransaction.h>
#include <react/uimanager/UIManagerAnimationDelegate.h>
#include <folly/dynamic.h>
#include "LayoutAnimationKeyFrameManager.h"
namespace facebook {
namespace react {
class LayoutAnimationDriver : public LayoutAnimationKeyFrameManager {
public:
virtual ~LayoutAnimationDriver() {}
protected:
virtual void animationMutationsForFrame(
SurfaceId surfaceId,
ShadowViewMutation::List &mutationsList,
uint64_t now) const override;
virtual double getProgressThroughAnimation(
AnimationKeyFrame const &keyFrame,
LayoutAnimation const *layoutAnimation,
ShadowView const &animationStateView) const override;
};
} // namespace react
} // namespace facebook

Просмотреть файл

@ -1,847 +0,0 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "LayoutAnimationKeyFrameManager.h"
#include <algorithm>
#include <chrono>
#include <react/componentregistry/ComponentDescriptorFactory.h>
#include <react/components/root/RootShadowNode.h>
#include <react/components/view/ViewProps.h>
#include <react/core/ComponentDescriptor.h>
#include <react/core/LayoutMetrics.h>
#include <react/core/LayoutableShadowNode.h>
#include <react/core/Props.h>
#include <react/core/RawValue.h>
#include <react/mounting/MountingCoordinator.h>
#include <react/mounting/Differentiator.h>
#include <react/mounting/ShadowTreeRevision.h>
#include <react/mounting/ShadowView.h>
#include <react/mounting/ShadowViewMutation.h>
#include <Glog/logging.h>
namespace facebook {
namespace react {
static better::optional<AnimationType> parseAnimationType(std::string param) {
if (param == "spring") {
return better::optional<AnimationType>(AnimationType::Spring);
}
if (param == "linear") {
return better::optional<AnimationType>(AnimationType::Linear);
}
if (param == "easeInEaseOut") {
return better::optional<AnimationType>(AnimationType::EaseInEaseOut);
}
if (param == "easeIn") {
return better::optional<AnimationType>(AnimationType::EaseIn);
}
if (param == "easeOut") {
return better::optional<AnimationType>(AnimationType::EaseOut);
}
if (param == "keyboard") {
return better::optional<AnimationType>(AnimationType::Keyboard);
}
return {};
}
static better::optional<AnimationProperty> parseAnimationProperty(
std::string param) {
if (param == "opacity") {
return better::optional<AnimationProperty>(AnimationProperty::Opacity);
}
if (param == "scaleX") {
return better::optional<AnimationProperty>(AnimationProperty::ScaleX);
}
if (param == "scaleY") {
return better::optional<AnimationProperty>(AnimationProperty::ScaleY);
}
if (param == "scaleXY") {
return better::optional<AnimationProperty>(AnimationProperty::ScaleXY);
}
return {};
}
static better::optional<AnimationConfig> parseAnimationConfig(
folly::dynamic const &config,
double defaultDuration) {
if (config.empty() || !config.isObject()) {
return better::optional<AnimationConfig>(
AnimationConfig{AnimationType::Linear,
AnimationProperty::NotApplicable,
defaultDuration,
0,
0,
0});
}
folly::dynamic const &animationTypeParam = config["type"];
if (animationTypeParam.empty() || !animationTypeParam.isString()) {
return {};
}
const auto animationType = parseAnimationType(animationTypeParam.asString());
if (!animationType) {
return {};
}
folly::dynamic const &animationPropertyParam = config["property"];
if (animationPropertyParam.empty() || !animationPropertyParam.isString()) {
return {};
}
const auto animationProperty =
parseAnimationProperty(animationPropertyParam.asString());
if (!animationProperty) {
return {};
}
double duration = defaultDuration;
double delay = 0;
double springDamping = 0;
double initialVelocity = 0;
auto const durationIt = config.find("duration");
if (durationIt != config.items().end()) {
if (durationIt->second.isDouble()) {
duration = durationIt->second.asDouble();
} else {
return {};
}
}
auto const delayIt = config.find("delay");
if (delayIt != config.items().end()) {
if (delayIt->second.isDouble()) {
delay = delayIt->second.asDouble();
} else {
return {};
}
}
auto const springDampingIt = config.find("springDamping");
if (springDampingIt != config.items().end() &&
springDampingIt->second.isDouble()) {
if (springDampingIt->second.isDouble()) {
springDamping = springDampingIt->second.asDouble();
} else {
return {};
}
}
auto const initialVelocityIt = config.find("initialVelocity");
if (initialVelocityIt != config.items().end()) {
if (initialVelocityIt->second.isDouble()) {
initialVelocity = initialVelocityIt->second.asDouble();
} else {
return {};
}
}
return better::optional<AnimationConfig>(AnimationConfig{*animationType,
*animationProperty,
duration,
delay,
springDamping,
initialVelocity});
}
// Parse animation config from JS
static better::optional<LayoutAnimationConfig> parseLayoutAnimationConfig(
folly::dynamic const &config) {
if (config.empty() || !config.isObject()) {
return {};
}
auto const durationIt = config.find("duration");
if (durationIt == config.items().end() || !durationIt->second.isDouble()) {
return {};
}
const double duration = durationIt->second.asDouble();
const auto createConfig = parseAnimationConfig(config["create"], duration);
if (!createConfig) {
return {};
}
const auto updateConfig = parseAnimationConfig(config["update"], duration);
if (!updateConfig) {
return {};
}
const auto deleteConfig = parseAnimationConfig(config["delete"], duration);
if (!deleteConfig) {
return {};
}
return better::optional<LayoutAnimationConfig>(LayoutAnimationConfig{
duration, *createConfig, *updateConfig, *deleteConfig});
}
/**
* Globally configure next LayoutAnimation.
*/
void LayoutAnimationKeyFrameManager::uiManagerDidConfigureNextLayoutAnimation(
RawValue const &config,
std::shared_ptr<const EventTarget> successCallback,
std::shared_ptr<const EventTarget> errorCallback) const {
auto layoutAnimationConfig =
parseLayoutAnimationConfig((folly::dynamic)config);
if (layoutAnimationConfig) {
std::lock_guard<std::mutex> lock(currentAnimationMutex_);
currentAnimation_ = better::optional<LayoutAnimation>{
LayoutAnimation{-1,
0,
false,
*layoutAnimationConfig,
successCallback,
errorCallback,
{}}};
} else {
// TODO: call errorCallback
LOG(ERROR) << "Parsing LayoutAnimationConfig failed: "
<< (folly::dynamic)config;
}
}
bool LayoutAnimationKeyFrameManager::shouldOverridePullTransaction() const {
return shouldAnimateFrame();
}
bool LayoutAnimationKeyFrameManager::shouldAnimateFrame() const {
// There is potentially a race here between getting and setting
// `currentMutation_`. We don't want to lock around this because then we're
// creating contention between pullTransaction and the JS thread.
return currentAnimation_ || !inflightAnimations_.empty();
}
static inline const float
interpolateFloats(float coefficient, float oldValue, float newValue) {
return oldValue + (newValue - oldValue) * coefficient;
}
std::pair<double, double>
LayoutAnimationKeyFrameManager::calculateAnimationProgress(
uint64_t now,
const LayoutAnimation &animation,
const AnimationConfig &mutationConfig) const {
uint64_t startTime = animation.startTime;
uint64_t delay = mutationConfig.delay;
uint64_t endTime = startTime + delay + mutationConfig.duration;
double progress = (now >= endTime)
? 1
: ((now < startTime + delay) ? 0
: 1 -
(double)(endTime - delay - now) /
(double)(endTime - animation.startTime));
return {progress, progress};
}
void LayoutAnimationKeyFrameManager::adjustDelayedMutationIndicesForMutation(
SurfaceId surfaceId,
ShadowViewMutation const &mutation) const {
bool isRemoveMutation = mutation.type == ShadowViewMutation::Type::Remove;
bool isInsertMutation = mutation.type == ShadowViewMutation::Type::Insert;
assert(isRemoveMutation || isInsertMutation);
for (auto &inflightAnimation : inflightAnimations_) {
if (inflightAnimation.surfaceId != surfaceId) {
continue;
}
for (auto it = inflightAnimation.keyFrames.begin();
it != inflightAnimation.keyFrames.end();
it++) {
auto &animatedKeyFrame = *it;
// Detect if they're in the same view hierarchy, but not equivalent
// (We've already detected direct conflicts and handled them above)
if (animatedKeyFrame.parentView.tag != mutation.parentShadowView.tag) {
continue;
}
if (animatedKeyFrame.type != AnimationConfigurationType::Noop) {
continue;
}
if (!animatedKeyFrame.finalMutationForKeyFrame.has_value()) {
continue;
}
ShadowViewMutation &finalAnimationMutation =
*animatedKeyFrame.finalMutationForKeyFrame;
if (finalAnimationMutation.type != ShadowViewMutation::Type::Remove) {
continue;
}
// Do we need to adjust the index of this operation?
if (isRemoveMutation && mutation.index <= finalAnimationMutation.index) {
finalAnimationMutation.index--;
} else if (
isInsertMutation && mutation.index <= finalAnimationMutation.index) {
finalAnimationMutation.index++;
}
}
}
}
better::optional<MountingTransaction>
LayoutAnimationKeyFrameManager::pullTransaction(
SurfaceId surfaceId,
MountingTransaction::Number transactionNumber,
MountingTelemetry const &telemetry,
ShadowViewMutationList mutations) const {
// Current time in milliseconds
uint64_t now =
std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now().time_since_epoch())
.count();
if (!mutations.empty()) {
#ifdef RN_SHADOW_TREE_INTROSPECTION
{
std::stringstream ss(getDebugDescription(mutations, {}));
std::string to;
while (std::getline(ss, to, '\n')) {
LOG(ERROR)
<< "LayoutAnimationKeyFrameManager.cpp: got mutation list: Line: "
<< to;
}
};
#endif
// What to do if we detect a conflict? Get current value and make
// that the baseline of the next animation. Scale the remaining time
// in the animation
// Types of conflicts and how we handle them:
// Update -> update: remove the previous update, make it the baseline of the
// next update (with current progress) Update -> remove: same, with final
// mutation being a remove Insert -> update: treat as update->update Insert
// -> remove: same, as update->remove Remove -> update/insert: not possible
// We just collect pairs here of <Mutation, AnimationConfig> and delete them
// from active animations. If another animation is queued up from the
// current mutations then these deleted mutations will serve as the baseline
// for the next animation. If not, the current mutations are executed
// immediately without issues.
std::vector<
std::tuple<AnimationKeyFrame, AnimationConfig, LayoutAnimation *>>
conflictingAnimations{};
for (auto &mutation : mutations) {
auto const &baselineShadowView =
(mutation.type == ShadowViewMutation::Type::Insert)
? mutation.newChildShadowView
: mutation.oldChildShadowView;
for (auto &inflightAnimation : inflightAnimations_) {
if (inflightAnimation.surfaceId != surfaceId) {
continue;
}
for (auto it = inflightAnimation.keyFrames.begin();
it != inflightAnimation.keyFrames.end();) {
auto &animatedKeyFrame = *it;
// Conflicting animation detected
if (animatedKeyFrame.tag == baselineShadowView.tag) {
auto const layoutAnimationConfig =
inflightAnimation.layoutAnimationConfig;
auto const mutationConfig =
(animatedKeyFrame.type == AnimationConfigurationType::Delete
? layoutAnimationConfig.deleteConfig
: (animatedKeyFrame.type ==
AnimationConfigurationType::Create
? layoutAnimationConfig.createConfig
: layoutAnimationConfig.updateConfig));
conflictingAnimations.push_back(std::make_tuple(
animatedKeyFrame, *mutationConfig, &inflightAnimation));
// Delete from existing animation
it = inflightAnimation.keyFrames.erase(it);
} else {
it++;
}
}
}
}
// Are we animating this list of mutations?
better::optional<LayoutAnimation> currentAnimation{};
{
std::lock_guard<std::mutex> lock(currentAnimationMutex_);
if (currentAnimation_) {
currentAnimation = currentAnimation_;
currentAnimation_ = {};
}
}
if (currentAnimation) {
LayoutAnimation animation = currentAnimation.value();
animation.surfaceId = surfaceId;
animation.startTime = now;
// Pre-process list to:
// Catch remove+reinsert (reorders)
// Catch delete+create (reparenting) (this should be optimized away at
// the diffing level eventually?)
// TODO: to prevent this step we could tag Remove/Insert mutations as
// being moves on the Differ level, since we know that there? We could use
// TinyMap here, but it's not exposed by Differentiator (yet).
std::vector<Tag> insertedTags;
std::vector<Tag> createdTags;
std::unordered_map<Tag, ShadowViewMutation> movedTags;
std::vector<Tag> reparentedTags;
for (const auto &mutation : mutations) {
if (mutation.type == ShadowViewMutation::Type::Insert) {
insertedTags.push_back(mutation.newChildShadowView.tag);
}
if (mutation.type == ShadowViewMutation::Type::Create) {
createdTags.push_back(mutation.newChildShadowView.tag);
}
}
// Process mutations list into operations that can be sent to platform
// immediately, and those that need to be animated Deletions, removals,
// updates are delayed and animated. Creations and insertions are sent to
// platform and then "animated in" with opacity updates. Upon completion,
// removals and deletions are sent to platform
ShadowViewMutation::List immediateMutations;
// Remove operations that are actually moves should be copied to
// "immediate mutations". The corresponding "insert" will also be executed
// immediately and animated as an update.
std::vector<AnimationKeyFrame> keyFramesToAnimate;
std::vector<AnimationKeyFrame> movesToAnimate;
auto const layoutAnimationConfig = animation.layoutAnimationConfig;
for (auto &mutation : mutations) {
ShadowView baselineShadowView =
(mutation.type == ShadowViewMutation::Type::Delete ||
mutation.type == ShadowViewMutation::Type::Remove
? mutation.oldChildShadowView
: mutation.newChildShadowView);
auto const &componentDescriptor =
getComponentDescriptorForShadowView(baselineShadowView);
auto mutationConfig =
(mutation.type == ShadowViewMutation::Type::Delete
? layoutAnimationConfig.deleteConfig
: (mutation.type == ShadowViewMutation::Type::Insert
? layoutAnimationConfig.createConfig
: layoutAnimationConfig.updateConfig));
bool isRemoveReinserted =
mutation.type == ShadowViewMutation::Type::Remove &&
std::find(
insertedTags.begin(),
insertedTags.end(),
mutation.oldChildShadowView.tag) != insertedTags.end();
// Reparenting can result in a node being removed, inserted (moved) and
// also deleted and created in the same frame, with the same props etc.
// This should eventually be optimized out of the diffing algorithm, but
// for now we detect reparenting and prevent the corresponding
// Delete/Create instructions from being animated.
bool isReparented =
(mutation.type == ShadowViewMutation::Delete &&
std::find(
createdTags.begin(),
createdTags.end(),
mutation.oldChildShadowView.tag) != createdTags.end()) ||
(mutation.type == ShadowViewMutation::Create &&
std::find(
reparentedTags.begin(),
reparentedTags.end(),
mutation.newChildShadowView.tag) != reparentedTags.end());
if (isRemoveReinserted) {
movedTags.insert({mutation.oldChildShadowView.tag, mutation});
}
if (isReparented && mutation.type == ShadowViewMutation::Delete) {
reparentedTags.push_back(mutation.oldChildShadowView.tag);
}
// Inserts that follow a "remove" of the same tag should be treated as
// an update (move) animation.
bool wasInsertedTagRemoved = false;
bool haveConfiguration = mutationConfig.has_value();
if (mutation.type == ShadowViewMutation::Type::Insert) {
// If this is a move, we actually don't want to copy this insert
// instruction to animated instructions - we want to
// generate an Update mutation for Remove+Insert pairs to animate
// the layout.
// The corresponding Remove and Insert instructions will instead
// be treated as "immediate" instructions.
auto movedIt = movedTags.find(mutation.newChildShadowView.tag);
wasInsertedTagRemoved = movedIt != movedTags.end();
if (wasInsertedTagRemoved) {
mutationConfig = layoutAnimationConfig.updateConfig;
}
haveConfiguration = mutationConfig.has_value();
if (wasInsertedTagRemoved && haveConfiguration) {
movesToAnimate.push_back(
AnimationKeyFrame{{},
AnimationConfigurationType::Update,
mutation.newChildShadowView.tag,
mutation.parentShadowView,
movedIt->second.oldChildShadowView,
mutation.newChildShadowView});
}
}
// Creates and inserts should also be executed immediately.
// Mutations that would otherwise be animated, but have no
// configuration, are also executed immediately.
if (isRemoveReinserted || !haveConfiguration || isReparented ||
mutation.type == ShadowViewMutation::Type::Create ||
mutation.type == ShadowViewMutation::Type::Insert) {
immediateMutations.push_back(mutation);
// Adjust indices for any non-directly-conflicting animations that
// affect the same parent view by inserting or removing anything
// from the hierarchy.
if (mutation.type == ShadowViewMutation::Type::Insert ||
mutation.type == ShadowViewMutation::Type::Remove) {
adjustDelayedMutationIndicesForMutation(surfaceId, mutation);
}
}
// Deletes, non-move inserts, updates get animated
if (!wasInsertedTagRemoved && !isRemoveReinserted && !isReparented &&
haveConfiguration &&
mutation.type != ShadowViewMutation::Type::Create) {
ShadowView viewStart = ShadowView(
mutation.type == ShadowViewMutation::Type::Insert
? mutation.newChildShadowView
: mutation.oldChildShadowView);
ShadowView viewFinal = ShadowView(
mutation.type == ShadowViewMutation::Type::Update
? mutation.newChildShadowView
: viewStart);
ShadowView parent = mutation.parentShadowView;
Tag tag = viewStart.tag;
Tag parentTag = mutation.parentShadowView.tag;
AnimationKeyFrame keyFrame{};
if (mutation.type == ShadowViewMutation::Type::Insert) {
if (mutationConfig->animationProperty ==
AnimationProperty::Opacity) {
auto props = componentDescriptor.cloneProps(viewStart.props, {});
const auto viewProps =
dynamic_cast<const ViewProps *>(props.get());
if (viewProps != nullptr) {
const_cast<ViewProps *>(viewProps)->opacity = 0;
}
viewStart.props = props;
}
bool isScaleX = mutationConfig->animationProperty ==
AnimationProperty::ScaleX ||
mutationConfig->animationProperty == AnimationProperty::ScaleXY;
bool isScaleY = mutationConfig->animationProperty ==
AnimationProperty::ScaleY ||
mutationConfig->animationProperty == AnimationProperty::ScaleXY;
if (isScaleX || isScaleY) {
auto props = componentDescriptor.cloneProps(viewStart.props, {});
const auto viewProps =
dynamic_cast<const ViewProps *>(props.get());
if (viewProps != nullptr) {
const_cast<ViewProps *>(viewProps)->transform =
Transform::Scale(isScaleX ? 0 : 1, isScaleY ? 0 : 1, 1);
}
viewStart.props = props;
}
keyFrame = AnimationKeyFrame{{},
AnimationConfigurationType::Create,
tag,
parent,
viewStart,
viewFinal,
0};
} else if (mutation.type == ShadowViewMutation::Type::Delete) {
if (mutationConfig->animationProperty ==
AnimationProperty::Opacity) {
auto props = componentDescriptor.cloneProps(viewFinal.props, {});
const auto viewProps =
dynamic_cast<const ViewProps *>(props.get());
if (viewProps != nullptr) {
const_cast<ViewProps *>(viewProps)->opacity = 0;
}
viewFinal.props = props;
}
bool isScaleX = mutationConfig->animationProperty ==
AnimationProperty::ScaleX ||
mutationConfig->animationProperty == AnimationProperty::ScaleXY;
bool isScaleY = mutationConfig->animationProperty ==
AnimationProperty::ScaleY ||
mutationConfig->animationProperty == AnimationProperty::ScaleXY;
if (isScaleX || isScaleY) {
auto props = componentDescriptor.cloneProps(viewFinal.props, {});
const auto viewProps =
dynamic_cast<const ViewProps *>(props.get());
if (viewProps != nullptr) {
const_cast<ViewProps *>(viewProps)->transform =
Transform::Scale(isScaleX ? 0 : 1, isScaleY ? 0 : 1, 1);
}
viewFinal.props = props;
}
keyFrame = AnimationKeyFrame{
better::optional<ShadowViewMutation>(mutation),
AnimationConfigurationType::Delete,
tag,
parent,
viewStart,
viewFinal,
0};
} else if (mutation.type == ShadowViewMutation::Type::Update) {
viewFinal = ShadowView(mutation.newChildShadowView);
keyFrame = AnimationKeyFrame{
better::optional<ShadowViewMutation>(mutation),
AnimationConfigurationType::Update,
tag,
parent,
viewStart,
viewFinal,
0};
} else {
// This should just be "Remove" instructions that are not animated
// (either this is a "move", or there's a corresponding "Delete"
// that is animated). We configure it as a Noop animation so it is
// executed when all the other animations are completed.
assert(mutation.type == ShadowViewMutation::Type::Remove);
// For remove instructions: since the execution of the Remove
// instruction will be delayed and therefore may execute outside of
// otherwise-expected order, other views may be inserted before the
// Remove is executed, requiring index adjustment.
{
int adjustedIndex = mutation.index;
for (const auto &otherMutation : mutations) {
if (otherMutation.type == ShadowViewMutation::Type::Insert &&
otherMutation.parentShadowView.tag == parentTag) {
if (otherMutation.index <= adjustedIndex) {
adjustedIndex++;
}
}
}
mutation = ShadowViewMutation::RemoveMutation(
mutation.parentShadowView,
mutation.oldChildShadowView,
adjustedIndex);
}
keyFrame = AnimationKeyFrame{
better::optional<ShadowViewMutation>(mutation),
AnimationConfigurationType::Noop,
tag,
parent,
{},
{},
0};
}
// Handle conflicting animations
for (auto &conflictingKeyframeTuple : conflictingAnimations) {
auto &conflictingKeyFrame = std::get<0>(conflictingKeyframeTuple);
auto const &conflictingMutationBaselineShadowView =
conflictingKeyFrame.viewStart;
// We've found a conflict.
if (conflictingMutationBaselineShadowView.tag == tag) {
// What's the progress of this ongoing animation?
double conflictingAnimationProgress =
calculateAnimationProgress(
now,
*std::get<2>(conflictingKeyframeTuple),
std::get<1>(conflictingKeyframeTuple))
.first;
// Get a baseline ShadowView at the current progress of the
// inflight animation. TODO: handle multiple properties being
// animated separately?
auto interpolatedInflightShadowView =
createInterpolatedShadowView(
conflictingAnimationProgress,
std::get<1>(conflictingKeyframeTuple),
conflictingKeyFrame.viewStart,
conflictingKeyFrame.viewEnd);
// Pick a Prop or layout property, depending on the current
// animation configuration. Figure out how much progress we've
// already made in the current animation, and start the animation
// from this point.
keyFrame.viewStart = interpolatedInflightShadowView;
keyFrame.initialProgress = getProgressThroughAnimation(
keyFrame, &animation, interpolatedInflightShadowView);
// We're guaranteed that a tag only has one animation associated
// with it, so we can break here. If we support multiple
// animations and animation curves over the same tag in the
// future, this will need to be modified to support that.
break;
}
}
keyFramesToAnimate.push_back(keyFrame);
}
}
#ifdef RN_SHADOW_TREE_INTROSPECTION
{
std::stringstream ss(getDebugDescription(immediateMutations, {}));
std::string to;
while (std::getline(ss, to, '\n')) {
LOG(ERROR)
<< "LayoutAnimationKeyFrameManager.cpp: got IMMEDIATE list: Line: "
<< to;
}
}
{
std::stringstream ss(getDebugDescription(mutationsToAnimate, {}));
std::string to;
while (std::getline(ss, to, '\n')) {
LOG(ERROR)
<< "LayoutAnimationKeyFrameManager.cpp: got FINAL list: Line: "
<< to;
}
}
#endif
animation.keyFrames = keyFramesToAnimate;
inflightAnimations_.push_back(animation);
// These will be executed immediately.
mutations = immediateMutations;
} /* if (currentAnimation) */ else {
// If there's no "next" animation, make sure we queue up "final"
// operations from all ongoing animations.
ShadowViewMutationList finalMutationsForConflictingAnimations{};
for (auto &conflictingKeyframeTuple : conflictingAnimations) {
auto &keyFrame = std::get<0>(conflictingKeyframeTuple);
if (keyFrame.finalMutationForKeyFrame.hasValue()) {
finalMutationsForConflictingAnimations.push_back(
*keyFrame.finalMutationForKeyFrame);
}
}
// Append mutations to this list and swap - so that the final
// conflicting mutations happen before any other mutations
finalMutationsForConflictingAnimations.insert(
finalMutationsForConflictingAnimations.end(),
mutations.begin(),
mutations.end());
mutations = finalMutationsForConflictingAnimations;
// Adjust pending mutation indices base on these operations
for (auto &mutation : mutations) {
if (mutation.type == ShadowViewMutation::Type::Insert ||
mutation.type == ShadowViewMutation::Type::Remove) {
adjustDelayedMutationIndicesForMutation(surfaceId, mutation);
}
}
}
} // if (mutations)
// We never commit a different root or modify anything -
// we just send additional mutations to the mounting layer until the
// animations are finished and the mounting layer (view) represents exactly
// what is in the most recent shadow tree
// Add animation mutations to the end of our existing mutations list in this
// function.
ShadowViewMutationList mutationsForAnimation{};
animationMutationsForFrame(surfaceId, mutationsForAnimation, now);
// Adjust pending mutation indices base on these operations
// For example: if a final "remove" mutation has been performed, and there is
// another that has not yet been executed because it is a part of an ongoing
// animation, its index may need to be adjusted.
for (auto const &animatedMutation : mutationsForAnimation) {
if (animatedMutation.type == ShadowViewMutation::Type::Insert ||
animatedMutation.type == ShadowViewMutation::Type::Remove) {
adjustDelayedMutationIndicesForMutation(surfaceId, animatedMutation);
}
}
mutations.insert(
mutations.end(),
mutationsForAnimation.begin(),
mutationsForAnimation.end());
// TODO: fill in telemetry
return MountingTransaction{
surfaceId, transactionNumber, std::move(mutations), {}};
}
ComponentDescriptor const &
LayoutAnimationKeyFrameManager::getComponentDescriptorForShadowView(
ShadowView const &shadowView) const {
return componentDescriptorRegistry_->at(shadowView.componentHandle);
}
void LayoutAnimationKeyFrameManager::setComponentDescriptorRegistry(
const SharedComponentDescriptorRegistry &componentDescriptorRegistry) {
componentDescriptorRegistry_ = componentDescriptorRegistry;
}
/**
* Given a `progress` between 0 and 1, a mutation and LayoutAnimation config,
* return a ShadowView with mutated props and/or LayoutMetrics.
*
* @param progress
* @param layoutAnimation
* @param animatedMutation
* @return
*/
ShadowView LayoutAnimationKeyFrameManager::createInterpolatedShadowView(
double progress,
AnimationConfig const &animationConfig,
ShadowView startingView,
ShadowView finalView) const {
ComponentDescriptor const &componentDescriptor =
getComponentDescriptorForShadowView(startingView);
auto mutatedShadowView = ShadowView(startingView);
// Animate opacity or scale/transform
mutatedShadowView.props = componentDescriptor.interpolateProps(
progress, startingView.props, finalView.props);
// Interpolate LayoutMetrics
LayoutMetrics const &finalLayoutMetrics = finalView.layoutMetrics;
LayoutMetrics const &baselineLayoutMetrics = startingView.layoutMetrics;
LayoutMetrics interpolatedLayoutMetrics = finalLayoutMetrics;
interpolatedLayoutMetrics.frame.origin.x = interpolateFloats(
progress,
baselineLayoutMetrics.frame.origin.x,
finalLayoutMetrics.frame.origin.x);
interpolatedLayoutMetrics.frame.origin.y = interpolateFloats(
progress,
baselineLayoutMetrics.frame.origin.y,
finalLayoutMetrics.frame.origin.y);
interpolatedLayoutMetrics.frame.size.width = interpolateFloats(
progress,
baselineLayoutMetrics.frame.size.width,
finalLayoutMetrics.frame.size.width);
interpolatedLayoutMetrics.frame.size.height = interpolateFloats(
progress,
baselineLayoutMetrics.frame.size.height,
finalLayoutMetrics.frame.size.height);
mutatedShadowView.layoutMetrics = interpolatedLayoutMetrics;
return mutatedShadowView;
}
} // namespace react
} // namespace facebook

Просмотреть файл

@ -1,164 +0,0 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <react/core/EventTarget.h>
#include <react/core/RawValue.h>
#include <react/mounting/Differentiator.h>
#include <react/mounting/MountingCoordinator.h>
#include <react/mounting/MountingOverrideDelegate.h>
#include <react/mounting/MountingTransaction.h>
#include <react/mounting/ShadowViewMutation.h>
#include <react/uimanager/UIManagerAnimationDelegate.h>
namespace facebook {
namespace react {
// This corresponds exactly with JS.
enum class AnimationType {
Spring,
Linear,
EaseInEaseOut,
EaseIn,
EaseOut,
Keyboard
};
enum class AnimationProperty {
NotApplicable,
Opacity,
ScaleX,
ScaleY,
ScaleXY
};
enum class AnimationConfigurationType {
Noop, // for animation placeholders that are not animated, and should be
// executed once other animations have completed
Create,
Update,
Delete
};
// This corresponds exactly with JS.
struct AnimationConfig {
AnimationType animationType;
AnimationProperty animationProperty;
double duration; // these are perhaps better represented as uint64_t, but they
// come from JS as doubles
double delay;
double springDamping;
double initialVelocity;
};
// This corresponds exactly with JS.
struct LayoutAnimationConfig {
double duration; // ms
better::optional<AnimationConfig> createConfig;
better::optional<AnimationConfig> updateConfig;
better::optional<AnimationConfig> deleteConfig;
};
struct AnimationKeyFrame {
// The mutation that should be executed once the animation completes
// (optional).
better::optional<ShadowViewMutation> finalMutationForKeyFrame;
// The type of animation this is (for configuration purposes)
AnimationConfigurationType type;
// Tag representing the node being animated.
Tag tag;
ShadowView parentView;
// ShadowView representing the start and end points of this animation.
ShadowView viewStart;
ShadowView viewEnd;
// If an animation interrupts an existing one, the starting state may actually
// be halfway through the intended transition.
double initialProgress;
};
struct LayoutAnimation {
SurfaceId surfaceId;
uint64_t startTime;
bool completed = false;
LayoutAnimationConfig layoutAnimationConfig;
std::shared_ptr<const EventTarget> successCallback;
std::shared_ptr<const EventTarget> errorCallback;
std::vector<AnimationKeyFrame> keyFrames;
};
class LayoutAnimationKeyFrameManager : public UIManagerAnimationDelegate,
public MountingOverrideDelegate {
public:
void uiManagerDidConfigureNextLayoutAnimation(
RawValue const &config,
std::shared_ptr<EventTarget const> successCallback,
std::shared_ptr<EventTarget const> errorCallback) const override;
void setComponentDescriptorRegistry(SharedComponentDescriptorRegistry const &
componentDescriptorRegistry) override;
// TODO: add SurfaceId to this API as well
bool shouldAnimateFrame() const override;
bool shouldOverridePullTransaction() const override;
// This is used to "hijack" the diffing process to figure out which mutations
// should be animated. The mutations returned by this function will be
// executed immediately.
better::optional<MountingTransaction> pullTransaction(
SurfaceId surfaceId,
MountingTransaction::Number number,
MountingTelemetry const &telemetry,
ShadowViewMutationList mutations) const override;
private:
void adjustDelayedMutationIndicesForMutation(
SurfaceId surfaceId,
ShadowViewMutation const &mutation) const;
protected:
ComponentDescriptor const &getComponentDescriptorForShadowView(
ShadowView const &shadowView) const;
std::pair<double, double> calculateAnimationProgress(
uint64_t now,
LayoutAnimation const &animation,
AnimationConfig const &mutationConfig) const;
ShadowView createInterpolatedShadowView(
double progress,
AnimationConfig const &animationConfig,
ShadowView startingView,
ShadowView finalView) const;
virtual void animationMutationsForFrame(
SurfaceId surfaceId,
ShadowViewMutation::List &mutationsList,
uint64_t now) const = 0;
virtual double getProgressThroughAnimation(
AnimationKeyFrame const &keyFrame,
LayoutAnimation const *layoutAnimation,
ShadowView const &animationStateView) const = 0;
SharedComponentDescriptorRegistry componentDescriptorRegistry_;
mutable better::optional<LayoutAnimation> currentAnimation_{};
mutable std::mutex currentAnimationMutex_;
/**
* All mutations of inflightAnimations_ are thread-safe as long as
* we keep the contract of: only mutate it within the context of
* `pullTransaction`. If that contract is held, this is implicitly protected
* by the MountingCoordinator's mutex.
*/
mutable std::vector<LayoutAnimation> inflightAnimations_{};
};
} // namespace react
} // namespace facebook

Просмотреть файл

@ -64,7 +64,6 @@ class RawValue {
private:
friend class RawProps;
friend class RawPropsParser;
friend class UIManagerBinding;
/*
* Arbitrary constructors are private only for RawProps and internal usage.
@ -74,9 +73,9 @@ class RawValue {
RawValue(folly::dynamic &&dynamic) noexcept : dynamic_(std::move(dynamic)){};
/*
* Copy constructor and copy assignment operator would be private and only for
* internal use, but it's needed for user-code that does `auto val =
* (better::map<std::string, RawValue>)rawVal;`
* Copy constructor and copy assignment operator are private and only for
* internal use. Basically, it's implementation details. Other particular
* implementations of the `RawValue` interface may not have them.
*/
RawValue(RawValue const &other) noexcept : dynamic_(other.dynamic_) {}

Просмотреть файл

@ -19,12 +19,9 @@
namespace facebook {
namespace react {
MountingCoordinator::MountingCoordinator(
ShadowTreeRevision baseRevision,
MountingOverrideDelegate *delegate)
MountingCoordinator::MountingCoordinator(ShadowTreeRevision baseRevision)
: surfaceId_(baseRevision.getRootShadowNode().getSurfaceId()),
baseRevision_(baseRevision),
mountingOverrideDelegate_(delegate) {
baseRevision_(baseRevision) {
#ifdef RN_SHADOW_TREE_INTROSPECTION
stubViewTree_ = stubViewTreeFromShadowNode(baseRevision_.getRootShadowNode());
#endif
@ -69,30 +66,31 @@ bool MountingCoordinator::waitForTransaction(
lock, timeout, [this]() { return lastRevision_.has_value(); });
}
void MountingCoordinator::updateBaseRevision(
ShadowTreeRevision const &baseRevision) const {
baseRevision_ = std::move(baseRevision);
}
better::optional<MountingTransaction> MountingCoordinator::pullTransaction()
const {
std::lock_guard<std::mutex> lock(mutex_);
void MountingCoordinator::resetLatestRevision() const {
lastRevision_.reset();
}
#ifdef RN_SHADOW_TREE_INTROSPECTION
void MountingCoordinator::validateTransactionAgainstStubViewTree(
ShadowViewMutationList const &mutations,
bool assertEquality) const {
std::string line;
std::stringstream ssMutations(getDebugDescription(mutations, {}));
while (std::getline(ssMutations, line, '\n')) {
LOG(ERROR) << "Mutations:" << line;
if (!lastRevision_.has_value()) {
return {};
}
number_++;
auto telemetry = lastRevision_->getTelemetry();
telemetry.willDiff();
auto mutations = calculateShadowViewMutations(
baseRevision_.getRootShadowNode(), lastRevision_->getRootShadowNode());
telemetry.didDiff();
#ifdef RN_SHADOW_TREE_INTROSPECTION
stubViewTree_.mutate(mutations);
auto stubViewTree =
stubViewTreeFromShadowNode(lastRevision_->getRootShadowNode());
std::string line;
std::stringstream ssOldTree(
baseRevision_.getRootShadowNode().getDebugDescription());
while (std::getline(ssOldTree, line, '\n')) {
@ -105,65 +103,19 @@ void MountingCoordinator::validateTransactionAgainstStubViewTree(
LOG(ERROR) << "New tree:" << line;
}
if (assertEquality) {
std::stringstream ssMutations(getDebugDescription(mutations, {}));
while (std::getline(ssMutations, line, '\n')) {
LOG(ERROR) << "Mutations:" << line;
}
assert(stubViewTree_ == stubViewTree);
}
}
#endif
better::optional<MountingTransaction> MountingCoordinator::pullTransaction()
const {
std::lock_guard<std::mutex> lock(mutex_);
bool shouldOverridePullTransaction = mountingOverrideDelegate_ != nullptr &&
mountingOverrideDelegate_->shouldOverridePullTransaction();
if (!shouldOverridePullTransaction && !lastRevision_.has_value()) {
return {};
}
number_++;
ShadowViewMutation::List diffMutations{};
auto telemetry =
(lastRevision_.hasValue() ? lastRevision_->getTelemetry()
: MountingTelemetry{});
if (lastRevision_.hasValue()) {
telemetry.willDiff();
diffMutations = calculateShadowViewMutations(
baseRevision_.getRootShadowNode(), lastRevision_->getRootShadowNode());
telemetry.didDiff();
}
better::optional<MountingTransaction> transaction{};
// The override delegate can provide custom mounting instructions,
// even if there's no `lastRevision_`. Consider cases of animation frames
// in between React tree updates.
if (shouldOverridePullTransaction) {
transaction = mountingOverrideDelegate_->pullTransaction(
surfaceId_, number_, telemetry, std::move(diffMutations));
} else if (lastRevision_.hasValue()) {
transaction = MountingTransaction{
surfaceId_, number_, std::move(diffMutations), telemetry};
}
if (lastRevision_.hasValue()) {
#ifdef RN_SHADOW_TREE_INTROSPECTION
// Only validate non-animated transactions - it's garbage to validate
// animated transactions, since the stub view tree likely won't match
// the committed tree during an animation.
this->validateTransactionAgainstStubViewTree(
transaction->getMutations(), !shouldOverridePullTransaction);
#endif
baseRevision_ = std::move(*lastRevision_);
lastRevision_.reset();
}
return transaction;
return MountingTransaction{
surfaceId_, number_, std::move(mutations), telemetry};
}
} // namespace react

Просмотреть файл

@ -11,10 +11,8 @@
#include <chrono>
#include <react/mounting/Differentiator.h>
#include <react/mounting/MountingOverrideDelegate.h>
#include <react/mounting/MountingTransaction.h>
#include <react/mounting/ShadowTreeRevision.h>
#include "ShadowTreeRevision.h"
#ifdef RN_SHADOW_TREE_INTROSPECTION
#include <react/mounting/stubs.h>
@ -35,12 +33,10 @@ class MountingCoordinator final {
using Shared = std::shared_ptr<MountingCoordinator const>;
/*
* The constructor is meant to be used only inside `ShadowTree`, and it's
* The constructor is ment to be used only inside `ShadowTree`, and it's
* `public` only to enable using with `std::make_shared<>`.
*/
MountingCoordinator(
ShadowTreeRevision baseRevision,
MountingOverrideDelegate *delegate);
MountingCoordinator(ShadowTreeRevision baseRevision);
/*
* Returns the id of the surface that the coordinator belongs to.
@ -69,20 +65,12 @@ class MountingCoordinator final {
*/
bool waitForTransaction(std::chrono::duration<double> timeout) const;
/*
* Methods from this section are meant to be used by
* `MountingOverrideDelegate` only.
*/
public:
void updateBaseRevision(ShadowTreeRevision const &baseRevision) const;
void resetLatestRevision() const;
private:
friend class ShadowTree;
/*
* Methods from this section are meant to be used by `ShadowTree` only.
*/
private:
friend class ShadowTree;
void push(ShadowTreeRevision &&revision) const;
/*
@ -90,7 +78,7 @@ class MountingCoordinator final {
* Generating a `MountingTransaction` requires some resources which the
* `MountingCoordinator` does not own (e.g. `ComponentDescriptor`s). Revoking
* committed revisions allows the owner (a Shadow Tree) to make sure that
* those resources will not be accessed (e.g. by the Mounting Layer).
* those resources will not be accessed (e.g. by the Mouting Layer).
*/
void revoke() const;
@ -102,12 +90,8 @@ class MountingCoordinator final {
mutable better::optional<ShadowTreeRevision> lastRevision_{};
mutable MountingTransaction::Number number_{0};
mutable std::condition_variable signal_;
mutable MountingOverrideDelegate *mountingOverrideDelegate_{nullptr};
#ifdef RN_SHADOW_TREE_INTROSPECTION
void validateTransactionAgainstStubViewTree(
ShadowViewMutationList const &mutations,
bool assertEquality) const;
mutable StubViewTree stubViewTree_; // Protected by `mutex_`.
#endif
};

Просмотреть файл

@ -1,46 +0,0 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <react/mounting/MountingTransaction.h>
#pragma once
namespace facebook {
namespace react {
class MountingCoordinator;
/**
* Generic interface for anything that needs to override specific
* MountingCoordinator methods. This is for platform-specific escape hatches
* like animations.
*/
class MountingOverrideDelegate {
public:
virtual bool shouldOverridePullTransaction() const = 0;
/**
* Delegates that override this method are responsible for:
*
* - Returning a MountingTransaction with mutations
* - Calling
* - Telemetry, if appropriate
*
* @param surfaceId
* @param number
* @param mountingCoordinator
* @return
*/
virtual better::optional<MountingTransaction> pullTransaction(
SurfaceId surfaceId,
MountingTransaction::Number number,
MountingTelemetry const &telemetry,
ShadowViewMutationList mutations) const = 0;
};
} // namespace react
} // namespace facebook

Просмотреть файл

@ -18,7 +18,7 @@ namespace react {
* particularly list of mutations and meta-data associated with the commit.
* Movable and copyable, but moving is strongly encouraged.
* Beware: A moved-from object of this type has unspecified value and accessing
* that is UB (Undefined Behaviour).
* that is UB.
*/
class MountingTransaction final {
public:

Просмотреть файл

@ -221,8 +221,7 @@ ShadowTree::ShadowTree(
LayoutConstraints const &layoutConstraints,
LayoutContext const &layoutContext,
RootComponentDescriptor const &rootComponentDescriptor,
ShadowTreeDelegate const &delegate,
MountingOverrideDelegate *mountingOverrideDelegate)
ShadowTreeDelegate const &delegate)
: surfaceId_(surfaceId), delegate_(delegate) {
const auto noopEventEmitter = std::make_shared<const ViewEventEmitter>(
nullptr, -1, std::shared_ptr<const EventDispatcher>());
@ -241,7 +240,7 @@ ShadowTree::ShadowTree(
family));
mountingCoordinator_ = std::make_shared<MountingCoordinator const>(
ShadowTreeRevision{rootShadowNode_, 0, {}}, mountingOverrideDelegate);
ShadowTreeRevision{rootShadowNode_, 0, {}});
}
ShadowTree::~ShadowTree() {
@ -358,7 +357,7 @@ bool ShadowTree::tryCommit(
mountingCoordinator_->push(
ShadowTreeRevision{newRootShadowNode, revisionNumber, telemetry});
notifyDelegatesOfUpdates();
delegate_.shadowTreeDidFinishTransaction(*this, mountingCoordinator_);
return true;
}
@ -399,9 +398,5 @@ void ShadowTree::emitLayoutEvents(
}
}
void ShadowTree::notifyDelegatesOfUpdates() const {
delegate_.shadowTreeDidFinishTransaction(*this, mountingCoordinator_);
}
} // namespace react
} // namespace facebook

Просмотреть файл

@ -18,7 +18,6 @@
#include <react/mounting/MountingCoordinator.h>
#include <react/mounting/ShadowTreeDelegate.h>
#include <react/mounting/ShadowTreeRevision.h>
#include "MountingOverrideDelegate.h"
namespace facebook {
namespace react {
@ -39,8 +38,7 @@ class ShadowTree final {
LayoutConstraints const &layoutConstraints,
LayoutContext const &layoutContext,
RootComponentDescriptor const &rootComponentDescriptor,
ShadowTreeDelegate const &delegate,
MountingOverrideDelegate *mountingOverrideDelegate);
ShadowTreeDelegate const &delegate);
~ShadowTree();
@ -71,13 +69,6 @@ class ShadowTree final {
*/
void commitEmptyTree() const;
/**
* Forces the ShadowTree to ping its delegate that an update is available.
* Useful for animations on Android.
* @return
*/
void notifyDelegatesOfUpdates() const;
MountingCoordinator::Shared getMountingCoordinator() const;
/*

Просмотреть файл

@ -22,10 +22,6 @@ MountingTelemetry const &ShadowTreeRevision::getTelemetry() const {
return telemetry_;
}
ShadowNode::Shared ShadowTreeRevision::getSharedRootShadowNode() {
return rootShadowNode_;
}
ShadowNode const &ShadowTreeRevision::getRootShadowNode() {
return *rootShadowNode_;
}

Просмотреть файл

@ -9,7 +9,6 @@
#include <better/optional.h>
#include <react/mounting/MountingOverrideDelegate.h>
#include <react/mounting/MountingTelemetry.h>
#include <react/mounting/MountingTransaction.h>
#include <react/mounting/ShadowViewMutation.h>
@ -43,21 +42,14 @@ class ShadowTreeRevision final {
*/
MountingTelemetry const &getTelemetry() const;
/*
* Methods from this section are meant to be used by
* `MountingOverrideDelegate` only.
*/
public:
ShadowNode const &getRootShadowNode();
ShadowNode::Shared getSharedRootShadowNode();
private:
friend class MountingCoordinator;
/*
* Methods from this section are meant to be used by `MountingCoordinator`
* only.
*/
private:
friend class MountingCoordinator;
ShadowNode const &getRootShadowNode();
Number getNumber() const;
private:

Просмотреть файл

@ -13,9 +13,9 @@ namespace facebook {
namespace react {
static LayoutMetrics layoutMetricsFromShadowNode(ShadowNode const &shadowNode) {
auto layoutableShadowNode =
auto layotableShadowNode =
traitCast<LayoutableShadowNode const *>(&shadowNode);
return layoutableShadowNode ? layoutableShadowNode->getLayoutMetrics()
return layotableShadowNode ? layotableShadowNode->getLayoutMetrics()
: EmptyLayoutMetrics;
}

Просмотреть файл

@ -66,7 +66,7 @@ struct ShadowViewNodePair final {
ShadowNode const *shadowNode;
/*
* The stored pointer to `ShadowNode` represents an identity of the pair.
* The stored pointer to `ShadowNode` represents an indentity of the pair.
*/
bool operator==(const ShadowViewNodePair &rhs) const;
bool operator!=(const ShadowViewNodePair &rhs) const;

Просмотреть файл

@ -103,8 +103,7 @@ TEST(StateReconciliationTest, testStateReconciliation) {
LayoutConstraints{},
LayoutContext{},
rootComponentDescriptor,
shadowTreeDelegate,
nullptr};
shadowTreeDelegate};
shadowTree.commit(
[&](RootShadowNode::Shared const &oldRootShadowNode) {

Просмотреть файл

@ -13,23 +13,15 @@
#include <react/componentregistry/ComponentDescriptorRegistry.h>
#include <react/core/LayoutContext.h>
#include <react/debug/SystraceSection.h>
#include <react/mounting/MountingOverrideDelegate.h>
#include <react/mounting/ShadowViewMutation.h>
#include <react/templateprocessor/UITemplateProcessor.h>
#include <react/uimanager/UIManager.h>
#include <react/uimanager/UIManagerBinding.h>
#ifdef RN_SHADOW_TREE_INTROSPECTION
#include <react/mounting/stubs.h>
#include <iostream>
#endif
namespace facebook {
namespace react {
Scheduler::Scheduler(
SchedulerToolbox schedulerToolbox,
UIManagerAnimationDelegate *animationDelegate,
SchedulerDelegate *delegate) {
runtimeExecutor_ = schedulerToolbox.runtimeExecutor;
@ -98,12 +90,6 @@ Scheduler::Scheduler(
delegate_ = delegate;
uiManager_ = uiManager;
if (animationDelegate != nullptr) {
animationDelegate->setComponentDescriptorRegistry(
componentDescriptorRegistry_);
}
uiManager_->setAnimationDelegate(animationDelegate);
#ifdef ANDROID
enableNewStateReconciliation_ = reactNativeConfig_->getBool(
"react_fabric:enable_new_state_reconciliation_android");
@ -172,8 +158,7 @@ void Scheduler::startSurface(
const std::string &moduleName,
const folly::dynamic &initialProps,
const LayoutConstraints &layoutConstraints,
const LayoutContext &layoutContext,
MountingOverrideDelegate *mountingOverrideDelegate) const {
const LayoutContext &layoutContext) const {
SystraceSection s("Scheduler::startSurface");
auto shadowTree = std::make_unique<ShadowTree>(
@ -181,8 +166,7 @@ void Scheduler::startSurface(
layoutConstraints,
layoutContext,
*rootComponentDescriptor_,
*uiManager_,
mountingOverrideDelegate);
*uiManager_);
shadowTree->setEnableNewStateReconciliation(enableNewStateReconciliation_);
@ -326,12 +310,6 @@ SchedulerDelegate *Scheduler::getDelegate() const {
return delegate_;
}
#pragma mark - UIManagerAnimationDelegate
void Scheduler::animationTick() const {
uiManager_->animationTick();
}
#pragma mark - UIManagerDelegate
void Scheduler::uiManagerDidFinishTransaction(
@ -342,6 +320,7 @@ void Scheduler::uiManagerDidFinishTransaction(
delegate_->schedulerDidFinishTransaction(mountingCoordinator);
}
}
void Scheduler::uiManagerDidCreateShadowNode(
const ShadowNode::Shared &shadowNode) {
SystraceSection s("Scheduler::uiManagerDidCreateShadowNode");

Просмотреть файл

@ -12,14 +12,13 @@
#include <ReactCommon/RuntimeExecutor.h>
#include <react/componentregistry/ComponentDescriptorFactory.h>
#include <react/componentregistry/ComponentDescriptorRegistry.h>
#include <react/components/root/RootComponentDescriptor.h>
#include <react/config/ReactNativeConfig.h>
#include <react/core/ComponentDescriptor.h>
#include <react/core/LayoutConstraints.h>
#include <react/mounting/MountingOverrideDelegate.h>
#include <react/scheduler/SchedulerDelegate.h>
#include <react/scheduler/SchedulerToolbox.h>
#include <react/uimanager/UIManagerAnimationDelegate.h>
#include <react/uimanager/UIManagerBinding.h>
#include <react/uimanager/UIManagerDelegate.h>
#include <react/utils/ContextContainer.h>
@ -32,10 +31,7 @@ namespace react {
*/
class Scheduler final : public UIManagerDelegate {
public:
Scheduler(
SchedulerToolbox schedulerToolbox,
UIManagerAnimationDelegate *animationDelegate,
SchedulerDelegate *delegate);
Scheduler(SchedulerToolbox schedulerToolbox, SchedulerDelegate *delegate);
~Scheduler();
#pragma mark - Surface Management
@ -45,8 +41,7 @@ class Scheduler final : public UIManagerDelegate {
const std::string &moduleName,
const folly::dynamic &initialProps,
const LayoutConstraints &layoutConstraints = {},
const LayoutContext &layoutContext = {},
MountingOverrideDelegate *mountingOverrideDelegate = nullptr) const;
const LayoutContext &layoutContext = {}) const;
void renderTemplateToSurface(
SurfaceId surfaceId,
@ -93,13 +88,6 @@ class Scheduler final : public UIManagerDelegate {
void setDelegate(SchedulerDelegate *delegate);
SchedulerDelegate *getDelegate() const;
#pragma mark - UIManagerAnimationDelegate
// This is not needed on iOS or any platform that has a "pull" instead of
// "push" MountingCoordinator model. This just tells the delegate an update
// is available and that it should `pullTransaction`; we may want to rename
// this to be more generic and not animation-specific.
void animationTick() const;
#pragma mark - UIManagerDelegate
void uiManagerDidFinishTransaction(

Просмотреть файл

@ -268,15 +268,9 @@ void UIManager::dispatchCommand(
}
void UIManager::configureNextLayoutAnimation(
RawValue const &config,
const folly::dynamic config,
SharedEventTarget successCallback,
SharedEventTarget errorCallback) const {
if (animationDelegate_) {
animationDelegate_->uiManagerDidConfigureNextLayoutAnimation(
config, successCallback, errorCallback);
}
}
SharedEventTarget errorCallback) const {}
void UIManager::setComponentDescriptorRegistry(
const SharedComponentDescriptorRegistry &componentDescriptorRegistry) {
componentDescriptorRegistry_ = componentDescriptorRegistry;
@ -316,22 +310,5 @@ void UIManager::shadowTreeDidFinishTransaction(
}
}
#pragma mark - UIManagerAnimationDelegate
void UIManager::setAnimationDelegate(
UIManagerAnimationDelegate *delegate) const {
animationDelegate_ = delegate;
}
void UIManager::animationTick() {
if (animationDelegate_ != nullptr &&
animationDelegate_->shouldAnimateFrame()) {
shadowTreeRegistry_.enumerate(
[&](ShadowTree const &shadowTree, bool &stop) {
shadowTree.notifyDelegatesOfUpdates();
});
}
}
} // namespace react
} // namespace facebook

Просмотреть файл

@ -12,13 +12,11 @@
#include <jsi/jsi.h>
#include <react/componentregistry/ComponentDescriptorRegistry.h>
#include <react/core/RawValue.h>
#include <react/core/ShadowNode.h>
#include <react/core/StateData.h>
#include <react/mounting/ShadowTree.h>
#include <react/mounting/ShadowTreeDelegate.h>
#include <react/mounting/ShadowTreeRegistry.h>
#include <react/uimanager/UIManagerAnimationDelegate.h>
#include <react/uimanager/UIManagerDelegate.h>
namespace facebook {
@ -41,15 +39,6 @@ class UIManager final : public ShadowTreeDelegate {
void setDelegate(UIManagerDelegate *delegate);
UIManagerDelegate *getDelegate();
/**
* Sets and gets the UIManager's Animation APIs delegate.
* The delegate is stored as a raw pointer, so the owner must null
* the pointer before being destroyed.
*/
void setAnimationDelegate(UIManagerAnimationDelegate *delegate) const;
void animationTick();
/*
* Provides access to a UIManagerBindging.
* The `callback` methods will not be called if the internal pointer to
@ -132,15 +121,13 @@ class UIManager final : public ShadowTreeDelegate {
* This API configures a global LayoutAnimation starting from the root node.
*/
void configureNextLayoutAnimation(
RawValue const &config,
const folly::dynamic config,
SharedEventTarget successCallback,
SharedEventTarget errorCallback) const;
ShadowTreeRegistry const &getShadowTreeRegistry() const;
SharedComponentDescriptorRegistry componentDescriptorRegistry_;
UIManagerDelegate *delegate_;
mutable UIManagerAnimationDelegate *animationDelegate_{nullptr};
UIManagerBinding *uiManagerBinding_;
ShadowTreeRegistry shadowTreeRegistry_{};
};

Просмотреть файл

@ -1,43 +0,0 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <react/componentregistry/ComponentDescriptorFactory.h>
#include <react/core/EventTarget.h>
#include <react/core/RawValue.h>
namespace facebook {
namespace react {
class UIManagerAnimationDelegate {
public:
/*
* Configure a LayoutAnimation.
* TODO: need SurfaceId here
*/
virtual void uiManagerDidConfigureNextLayoutAnimation(
RawValue const &config,
SharedEventTarget successCallback,
SharedEventTarget errorCallback) const = 0;
/**
* Set ComponentDescriptor registry.
*
* @param componentDescriptorRegistry
*/
virtual void setComponentDescriptorRegistry(
const SharedComponentDescriptorRegistry &componentDescriptorRegistry) = 0;
/**
* Only needed on Android to drive animations.
*/
virtual bool shouldAnimateFrame() const = 0;
};
} // namespace react
} // namespace facebook

Просмотреть файл

@ -619,14 +619,16 @@ jsi::Value UIManagerBinding::get(
const jsi::Value *arguments,
size_t count) -> jsi::Value {
uiManager->configureNextLayoutAnimation(
// TODO: pass in JSI value instead of folly::dynamic to RawValue
RawValue(commandArgsFromValue(runtime, arguments[0])),
commandArgsFromValue(
runtime,
arguments[0]), // TODO T66507273: do a better job of parsing
// these arguments into a real struct / use a
// C++ typed object instead of folly::dynamic
eventTargetFromValue(runtime, arguments[1], -1),
eventTargetFromValue(runtime, arguments[2], -1));
return jsi::Value::undefined();
});
}
return jsi::Value::undefined();
}

Просмотреть файл

@ -9,7 +9,6 @@
#include <folly/dynamic.h>
#include <jsi/jsi.h>
#include <react/core/RawValue.h>
#include <react/uimanager/UIManager.h>
#include <react/uimanager/primitives.h>