From 0a3ddce928c23d2b34b7e00c39599144cf8d90f9 Mon Sep 17 00:00:00 2001 From: Andrei Shikov Date: Mon, 20 Dec 2021 08:08:22 -0800 Subject: [PATCH] Refactor mount out of Binding.cpp Summary: Binding.cpp shares many responsibilities, including surface management and mount. This change refactors mount (and mount items) into separate files, while retaining the same functionality and interface. Changelog: [Internal][Android] - Refactor mount into FabricMountingManager Reviewed By: mdvacca Differential Revision: D32977902 fbshipit-source-id: 07cdf5a19033fccaa81901e5b9a4d44192ec7853 --- .../java/com/facebook/react/fabric/jni/BUCK | 1 + .../com/facebook/react/fabric/jni/Binding.cpp | 995 ++---------------- .../com/facebook/react/fabric/jni/Binding.h | 68 +- .../react/fabric/jni/FabricMountItem.cpp | 50 + .../react/fabric/jni/FabricMountItem.h | 74 ++ .../fabric/jni/FabricMountingManager.cpp | 821 +++++++++++++++ .../react/fabric/jni/FabricMountingManager.h | 71 ++ 7 files changed, 1129 insertions(+), 951 deletions(-) create mode 100644 ReactAndroid/src/main/java/com/facebook/react/fabric/jni/FabricMountItem.cpp create mode 100644 ReactAndroid/src/main/java/com/facebook/react/fabric/jni/FabricMountItem.h create mode 100644 ReactAndroid/src/main/java/com/facebook/react/fabric/jni/FabricMountingManager.cpp create mode 100644 ReactAndroid/src/main/java/com/facebook/react/fabric/jni/FabricMountingManager.h diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/BUCK b/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/BUCK index 693f6f967a5..2957f8fbe94 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/BUCK @@ -28,6 +28,7 @@ rn_xplat_cxx_library( react_native_xplat_target("react/renderer/animations:animations"), react_native_xplat_target("react/renderer/uimanager:uimanager"), react_native_xplat_target("react/renderer/scheduler:scheduler"), + react_native_xplat_target("react/renderer/mounting:mounting"), react_native_xplat_target("react/renderer/componentregistry:componentregistry"), react_native_xplat_target("react/renderer/components/scrollview:scrollview"), react_native_xplat_target("runtimeexecutor:runtimeexecutor"), diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Binding.cpp b/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Binding.cpp index 9fde6bc9feb..b599ca02091 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Binding.cpp +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Binding.cpp @@ -39,194 +39,11 @@ using namespace facebook::jsi; namespace facebook { namespace react { -namespace { - -struct JMountItem : public JavaClass { - static constexpr auto kJavaDescriptor = - "Lcom/facebook/react/fabric/mounting/mountitems/MountItem;"; -}; - -} // namespace - -CppMountItem CppMountItem::CreateMountItem(ShadowView const &shadowView) { - return {CppMountItem::Type::Create, {}, {}, shadowView, -1}; -} -CppMountItem CppMountItem::DeleteMountItem(ShadowView const &shadowView) { - return {CppMountItem::Type::Delete, {}, shadowView, {}, -1}; -} -CppMountItem CppMountItem::InsertMountItem( - ShadowView const &parentView, - ShadowView const &shadowView, - int index) { - return {CppMountItem::Type::Insert, parentView, {}, shadowView, index}; -} -CppMountItem CppMountItem::RemoveMountItem( - ShadowView const &parentView, - ShadowView const &shadowView, - int index) { - return {CppMountItem::Type::Remove, parentView, shadowView, {}, index}; -} -CppMountItem CppMountItem::UpdatePropsMountItem(ShadowView const &shadowView) { - return {CppMountItem::Type::UpdateProps, {}, {}, shadowView, -1}; -} -CppMountItem CppMountItem::UpdateStateMountItem(ShadowView const &shadowView) { - return {CppMountItem::Type::UpdateState, {}, {}, shadowView, -1}; -} -CppMountItem CppMountItem::UpdateLayoutMountItem(ShadowView const &shadowView) { - return {CppMountItem::Type::UpdateLayout, {}, {}, shadowView, -1}; -} -CppMountItem CppMountItem::UpdateEventEmitterMountItem( - ShadowView const &shadowView) { - return {CppMountItem::Type::UpdateEventEmitter, {}, {}, shadowView, -1}; -} -CppMountItem CppMountItem::UpdatePaddingMountItem( - ShadowView const &shadowView) { - return {CppMountItem::Type::UpdatePadding, {}, {}, shadowView, -1}; -} - -static inline int getIntBufferSizeForType(CppMountItem::Type mountItemType) { - switch (mountItemType) { - case CppMountItem::Type::Create: - return 2; // tag, isLayoutable - case CppMountItem::Type::Insert: - case CppMountItem::Type::Remove: - return 3; // tag, parentTag, index - case CppMountItem::Type::Delete: - case CppMountItem::Type::UpdateProps: - case CppMountItem::Type::UpdateState: - case CppMountItem::Type::UpdateEventEmitter: - return 1; // tag - case CppMountItem::Type::UpdatePadding: - return 5; // tag, top, left, bottom, right - case CppMountItem::Type::UpdateLayout: - return 6; // tag, x, y, w, h, DisplayType - case CppMountItem::Undefined: - case CppMountItem::Multiple: - return -1; - } -} - -static inline void updateBufferSizes( - CppMountItem::Type mountItemType, - int numInstructions, - int &batchMountItemIntsSize, - int &batchMountItemObjectsSize) { - if (numInstructions == 0) { - return; - } - - batchMountItemIntsSize += - numInstructions == 1 ? 1 : 2; // instructionType[, numInstructions] - batchMountItemIntsSize += - numInstructions * getIntBufferSizeForType(mountItemType); - - if (mountItemType == CppMountItem::Type::UpdateProps) { - batchMountItemObjectsSize += - numInstructions; // props object * numInstructions - } else if (mountItemType == CppMountItem::Type::UpdateState) { - batchMountItemObjectsSize += - numInstructions; // state object * numInstructions - } else if (mountItemType == CppMountItem::Type::UpdateEventEmitter) { - batchMountItemObjectsSize += - numInstructions; // EventEmitter object * numInstructions - } -} - -static inline void computeBufferSizes( - int &batchMountItemIntsSize, - int &batchMountItemObjectsSize, - std::vector &cppCommonMountItems, - std::vector &cppDeleteMountItems, - std::vector &cppUpdatePropsMountItems, - std::vector &cppUpdateStateMountItems, - std::vector &cppUpdatePaddingMountItems, - std::vector &cppUpdateLayoutMountItems, - std::vector &cppUpdateEventEmitterMountItems) { - CppMountItem::Type lastType = CppMountItem::Type::Undefined; - int numSameType = 0; - for (const auto &mountItem : cppCommonMountItems) { - const auto &mountItemType = mountItem.type; - - if (lastType == mountItemType) { - numSameType++; - if (numSameType == 2) { - batchMountItemIntsSize += 1; // numInstructions - } - } else { - numSameType = 1; - lastType = mountItemType; - batchMountItemIntsSize += 1; // instructionType - } - - batchMountItemIntsSize += getIntBufferSizeForType(mountItemType); - if (mountItemType == CppMountItem::Type::Create) { - batchMountItemObjectsSize += - 4; // component name, props, state, event emitter - } - } - - updateBufferSizes( - CppMountItem::Type::UpdateProps, - cppUpdatePropsMountItems.size(), - batchMountItemIntsSize, - batchMountItemObjectsSize); - updateBufferSizes( - CppMountItem::Type::UpdateState, - cppUpdateStateMountItems.size(), - batchMountItemIntsSize, - batchMountItemObjectsSize); - updateBufferSizes( - CppMountItem::Type::UpdatePadding, - cppUpdatePaddingMountItems.size(), - batchMountItemIntsSize, - batchMountItemObjectsSize); - updateBufferSizes( - CppMountItem::Type::UpdateLayout, - cppUpdateLayoutMountItems.size(), - batchMountItemIntsSize, - batchMountItemObjectsSize); - updateBufferSizes( - CppMountItem::Type::UpdateEventEmitter, - cppUpdateEventEmitterMountItems.size(), - batchMountItemIntsSize, - batchMountItemObjectsSize); - updateBufferSizes( - CppMountItem::Type::Delete, - cppDeleteMountItems.size(), - batchMountItemIntsSize, - batchMountItemObjectsSize); -} - -static inline void writeIntBufferTypePreamble( - int mountItemType, - int numItems, - _JNIEnv *env, - jintArray &intBufferArray, - int &intBufferPosition) { - jint temp[2]; - if (numItems == 1) { - temp[0] = mountItemType; - env->SetIntArrayRegion(intBufferArray, intBufferPosition, 1, temp); - intBufferPosition += 1; - } else { - temp[0] = mountItemType | CppMountItem::Type::Multiple; - temp[1] = numItems; - env->SetIntArrayRegion(intBufferArray, intBufferPosition, 2, temp); - intBufferPosition += 2; - } -} - jni::local_ref Binding::initHybrid( jni::alias_ref) { return makeCxxInstance(); } -// Thread-safe getter -jni::global_ref Binding::getJavaUIManager() { - std::shared_lock lock(installMutex_); - return javaUIManager_; -} - // Thread-safe getter std::shared_ptr Binding::getScheduler() { std::shared_lock lock(installMutex_); @@ -261,6 +78,27 @@ Binding::getInspectorDataForInstance( return ReadableNativeMap::newObjectCxxArgs(result); } +bool isMapBufferSerializationEnabled() { + static const auto reactFeatureFlagsJavaDescriptor = + jni::findClassStatic(Binding::ReactFeatureFlagsJavaDescriptor); + static const auto isMapBufferSerializationEnabledMethod = + reactFeatureFlagsJavaDescriptor->getStaticMethod( + "isMapBufferSerializationEnabled"); + bool value = + isMapBufferSerializationEnabledMethod(reactFeatureFlagsJavaDescriptor); + return value; +} + +void Binding::setPixelDensity(float pointScaleFactor) { + pointScaleFactor_ = pointScaleFactor; +} + +void Binding::driveCxxAnimations() { + scheduler_->animationTick(); +} + +#pragma mark - Surface management + void Binding::startSurface( jint surfaceId, jni::alias_ref moduleName, @@ -416,22 +254,6 @@ void Binding::unregisterSurface(SurfaceHandlerBinding *surfaceHandlerBinding) { scheduler->unregisterSurface(surfaceHandlerBinding->getSurfaceHandler()); } -static inline float scale(Float value, Float pointScaleFactor) { - std::feclearexcept(FE_ALL_EXCEPT); - float result = value * pointScaleFactor; - if (std::fetestexcept(FE_OVERFLOW)) { - LOG(ERROR) << "Binding::scale - FE_OVERFLOW - value: " << value - << " pointScaleFactor: " << pointScaleFactor - << " result: " << result; - } - if (std::fetestexcept(FE_UNDERFLOW)) { - LOG(ERROR) << "Binding::scale - FE_UNDERFLOW - value: " << value - << " pointScaleFactor: " << pointScaleFactor - << " result: " << result; - } - return result; -} - void Binding::setConstraints( jint surfaceId, jfloat minWidth, @@ -482,16 +304,7 @@ void Binding::setConstraints( } } -bool isMapBufferSerializationEnabled() { - static const auto reactFeatureFlagsJavaDescriptor = - jni::findClassStatic(Binding::ReactFeatureFlagsJavaDescriptor); - static const auto isMapBufferSerializationEnabledMethod = - reactFeatureFlagsJavaDescriptor->getStaticMethod( - "isMapBufferSerializationEnabled"); - bool value = - isMapBufferSerializationEnabledMethod(reactFeatureFlagsJavaDescriptor); - return value; -} +#pragma mark - Install/uninstall java binding void Binding::installFabricUIManager( jni::alias_ref runtimeExecutorHolder, @@ -523,7 +336,9 @@ void Binding::installFabricUIManager( // at the same time std::unique_lock lock(installMutex_); - javaUIManager_ = make_global(javaUIManager); + auto globalJavaUiManager = make_global(javaUIManager); + mountingManager_ = + std::make_shared(config, globalJavaUiManager); ContextContainer::Shared contextContainer = std::make_shared(); @@ -547,39 +362,38 @@ void Binding::installFabricUIManager( config->getBool("react_fabric:enable_asynchronous_event_beat_v2_android"); // TODO: T31905686 Create synchronous Event Beat - jni::global_ref localJavaUIManager = javaUIManager_; EventBeat::Factory synchronousBeatFactory = [eventBeatManager, runtimeExecutor, - localJavaUIManager, + globalJavaUiManager, enableV2AsynchronousEventBeat](EventBeat::SharedOwnerBox const &ownerBox) -> std::unique_ptr { if (enableV2AsynchronousEventBeat) { return std::make_unique( - ownerBox, eventBeatManager, runtimeExecutor, localJavaUIManager); + ownerBox, eventBeatManager, runtimeExecutor, globalJavaUiManager); } else { return std::make_unique( - ownerBox, eventBeatManager, runtimeExecutor, localJavaUIManager); + ownerBox, eventBeatManager, runtimeExecutor, globalJavaUiManager); } }; EventBeat::Factory asynchronousBeatFactory = [eventBeatManager, runtimeExecutor, - localJavaUIManager, + globalJavaUiManager, enableV2AsynchronousEventBeat](EventBeat::SharedOwnerBox const &ownerBox) -> std::unique_ptr { if (enableV2AsynchronousEventBeat) { return std::make_unique( - ownerBox, eventBeatManager, runtimeExecutor, localJavaUIManager); + ownerBox, eventBeatManager, runtimeExecutor, globalJavaUiManager); } else { return std::make_unique( - ownerBox, eventBeatManager, runtimeExecutor, localJavaUIManager); + ownerBox, eventBeatManager, runtimeExecutor, globalJavaUiManager); } }; contextContainer->insert("ReactNativeConfig", config); - contextContainer->insert("FabricUIManager", javaUIManager_); + contextContainer->insert("FabricUIManager", globalJavaUiManager); // Keep reference to config object and cache some feature flags here reactNativeConfig_ = config; @@ -590,9 +404,6 @@ void Binding::installFabricUIManager( disablePreallocateViews_ = reactNativeConfig_->getBool( "react_fabric:disabled_view_preallocation_android"); - enableEarlyEventEmitterUpdate_ = reactNativeConfig_->getBool( - "react_fabric:enable_early_event_emitter_update"); - dispatchPreallocationInBackground_ = reactNativeConfig_->getBool( "react_native_new_architecture:dispatch_preallocation_in_bg"); @@ -613,8 +424,8 @@ void Binding::installFabricUIManager( animationDriver_ = std::make_shared( runtimeExecutor, contextContainer, this); - scheduler_ = std::make_shared( - toolbox, (animationDriver_ ? animationDriver_.get() : nullptr), this); + scheduler_ = + std::make_shared(toolbox, animationDriver_.get(), this); } void Binding::uninstallFabricUIManager() { @@ -626,608 +437,28 @@ void Binding::uninstallFabricUIManager() { std::unique_lock lock(installMutex_); animationDriver_ = nullptr; scheduler_ = nullptr; - javaUIManager_ = nullptr; + mountingManager_ = nullptr; reactNativeConfig_ = nullptr; } -inline local_ref castReadableMap( - local_ref const &nativeMap) { - return make_local(reinterpret_cast(nativeMap.get())); -} - -inline local_ref castReadableArray( - local_ref const &nativeArray) { - return make_local( - reinterpret_cast(nativeArray.get())); -} - -// TODO: this method will be removed when binding for components are code-gen -local_ref getPlatformComponentName(const ShadowView &shadowView) { - static std::string scrollViewComponentName = std::string("ScrollView"); - - local_ref componentName; - if (scrollViewComponentName == shadowView.componentName) { - auto newViewProps = - std::static_pointer_cast(shadowView.props); - if (newViewProps->getProbablyMoreHorizontalThanVertical_DEPRECATED()) { - componentName = make_jstring("AndroidHorizontalScrollView"); - return componentName; - } +std::shared_ptr Binding::verifyMountingManager( + std::string const &hint) { + std::shared_lock lock(installMutex_); + if (!mountingManager_) { + LOG(ERROR) << hint << " mounting manager disappeared."; } - - componentName = make_jstring(shadowView.componentName); - return componentName; + return mountingManager_; } void Binding::schedulerDidFinishTransaction( MountingCoordinator::Shared const &mountingCoordinator) { - std::lock_guard lock(commitMutex_); - - SystraceSection s( - "FabricUIManagerBinding::schedulerDidFinishTransactionIntBuffer"); - auto finishTransactionStartTime = telemetryTimePointNow(); - - jni::global_ref localJavaUIManager = getJavaUIManager(); - if (!localJavaUIManager) { - LOG(ERROR) - << "Binding::schedulerDidFinishTransaction: JavaUIManager disappeared"; + auto mountingManager = + verifyMountingManager("Binding::schedulerDidFinishTransaction"); + if (!mountingManager) { return; } - auto mountingTransaction = mountingCoordinator->pullTransaction(); - - if (!mountingTransaction.has_value()) { - return; - } - - auto env = Environment::current(); - - auto telemetry = mountingTransaction->getTelemetry(); - auto surfaceId = mountingTransaction->getSurfaceId(); - auto &mutations = mountingTransaction->getMutations(); - - auto revisionNumber = telemetry.getRevisionNumber(); - - std::vector cppCommonMountItems; - std::vector cppDeleteMountItems; - std::vector cppUpdatePropsMountItems; - std::vector cppUpdateStateMountItems; - std::vector cppUpdatePaddingMountItems; - std::vector cppUpdateLayoutMountItems; - std::vector cppUpdateEventEmitterMountItems; - - for (const auto &mutation : mutations) { - const auto &parentShadowView = mutation.parentShadowView; - const auto &oldChildShadowView = mutation.oldChildShadowView; - const auto &newChildShadowView = mutation.newChildShadowView; - auto &mutationType = mutation.type; - auto &index = mutation.index; - - bool isVirtual = mutation.mutatedViewIsVirtual(); - - bool noRevisionCheck = - disablePreallocateViews_ || disableRevisionCheckForPreallocation_; - - switch (mutationType) { - case ShadowViewMutation::Create: { - if (noRevisionCheck || newChildShadowView.props->revision > 1) { - cppCommonMountItems.push_back( - CppMountItem::CreateMountItem(newChildShadowView)); - } - break; - } - case ShadowViewMutation::Remove: { - if (!isVirtual) { - cppCommonMountItems.push_back(CppMountItem::RemoveMountItem( - parentShadowView, oldChildShadowView, index)); - } - break; - } - case ShadowViewMutation::Delete: { - cppDeleteMountItems.push_back( - CppMountItem::DeleteMountItem(oldChildShadowView)); - break; - } - case ShadowViewMutation::Update: { - if (!isVirtual) { - if (oldChildShadowView.props != newChildShadowView.props) { - cppUpdatePropsMountItems.push_back( - CppMountItem::UpdatePropsMountItem(newChildShadowView)); - } - if (oldChildShadowView.state != newChildShadowView.state) { - cppUpdateStateMountItems.push_back( - CppMountItem::UpdateStateMountItem(newChildShadowView)); - } - - // Padding: padding mountItems must be executed before layout props - // are updated in the view. This is necessary to ensure that events - // (resulting from layout changes) are dispatched with the correct - // padding information. - if (oldChildShadowView.layoutMetrics.contentInsets != - newChildShadowView.layoutMetrics.contentInsets) { - cppUpdatePaddingMountItems.push_back( - CppMountItem::UpdatePaddingMountItem(newChildShadowView)); - } - - if (oldChildShadowView.layoutMetrics != - newChildShadowView.layoutMetrics) { - cppUpdateLayoutMountItems.push_back( - CppMountItem::UpdateLayoutMountItem( - mutation.newChildShadowView)); - } - } - - if (oldChildShadowView.eventEmitter != - newChildShadowView.eventEmitter) { - cppUpdateEventEmitterMountItems.push_back( - CppMountItem::UpdateEventEmitterMountItem( - mutation.newChildShadowView)); - } - break; - } - case ShadowViewMutation::Insert: { - if (!isVirtual) { - // Insert item - cppCommonMountItems.push_back(CppMountItem::InsertMountItem( - parentShadowView, newChildShadowView, index)); - - if (noRevisionCheck || newChildShadowView.props->revision > 1) { - cppUpdatePropsMountItems.push_back( - CppMountItem::UpdatePropsMountItem(newChildShadowView)); - } - - // State - if (newChildShadowView.state) { - cppUpdateStateMountItems.push_back( - CppMountItem::UpdateStateMountItem(newChildShadowView)); - } - - // Padding: padding mountItems must be executed before layout props - // are updated in the view. This is necessary to ensure that events - // (resulting from layout changes) are dispatched with the correct - // padding information. - cppUpdatePaddingMountItems.push_back( - CppMountItem::UpdatePaddingMountItem( - mutation.newChildShadowView)); - - // Layout - cppUpdateLayoutMountItems.push_back( - CppMountItem::UpdateLayoutMountItem(mutation.newChildShadowView)); - } - - // EventEmitter - cppUpdateEventEmitterMountItems.push_back( - CppMountItem::UpdateEventEmitterMountItem( - mutation.newChildShadowView)); - - break; - } - default: { - break; - } - } - } - - // We now have all the information we need, including ordering of mount items, - // to know exactly how much space must be allocated - int batchMountItemIntsSize = 0; - int batchMountItemObjectsSize = 0; - computeBufferSizes( - batchMountItemIntsSize, - batchMountItemObjectsSize, - cppCommonMountItems, - cppDeleteMountItems, - cppUpdatePropsMountItems, - cppUpdateStateMountItems, - cppUpdatePaddingMountItems, - cppUpdateLayoutMountItems, - cppUpdateEventEmitterMountItems); - - static auto createMountItemsIntBufferBatchContainer = - jni::findClassStatic(Binding::UIManagerJavaDescriptor) - ->getMethod( - jint, jintArray, jtypeArray, jint)>( - "createIntBufferBatchMountItem"); - - static auto scheduleMountItem = - jni::findClassStatic(Binding::UIManagerJavaDescriptor) - ->getMethod("scheduleMountItem"); - - if (batchMountItemIntsSize == 0) { - auto finishTransactionEndTime = telemetryTimePointNow(); - - scheduleMountItem( - localJavaUIManager, - nullptr, - telemetry.getRevisionNumber(), - telemetryTimePointToMilliseconds(telemetry.getCommitStartTime()), - telemetryTimePointToMilliseconds(telemetry.getDiffStartTime()), - telemetryTimePointToMilliseconds(telemetry.getDiffEndTime()), - telemetryTimePointToMilliseconds(telemetry.getLayoutStartTime()), - telemetryTimePointToMilliseconds(telemetry.getLayoutEndTime()), - telemetryTimePointToMilliseconds(finishTransactionStartTime), - telemetryTimePointToMilliseconds(finishTransactionEndTime)); - return; - } - - // Allocate the intBuffer and object array, now that we know exact sizes - // necessary - // TODO: don't allocate at all if size is zero - jintArray intBufferArray = env->NewIntArray(batchMountItemIntsSize); - local_ref> objBufferArray = - JArrayClass::newArray(batchMountItemObjectsSize); - - // Fill in arrays - int intBufferPosition = 0; - int objBufferPosition = 0; - int prevMountItemType = -1; - jint temp[7]; - for (int i = 0; i < cppCommonMountItems.size(); i++) { - const auto &mountItem = cppCommonMountItems[i]; - const auto &mountItemType = mountItem.type; - - // Get type here, and count forward how many items of this type are in a - // row. Write preamble to any common type here. - if (prevMountItemType != mountItemType) { - int numSameItemTypes = 1; - for (int j = i + 1; j < cppCommonMountItems.size() && - cppCommonMountItems[j].type == mountItemType; - j++) { - numSameItemTypes++; - } - - writeIntBufferTypePreamble( - mountItemType, - numSameItemTypes, - env, - intBufferArray, - intBufferPosition); - } - prevMountItemType = mountItemType; - - // TODO: multi-create, multi-insert, etc - if (mountItemType == CppMountItem::Type::Create) { - local_ref componentName = - getPlatformComponentName(mountItem.newChildShadowView); - - int isLayoutable = - mountItem.newChildShadowView.layoutMetrics != EmptyLayoutMetrics ? 1 - : 0; - - local_ref props = - castReadableMap(ReadableNativeMap::newObjectCxxArgs( - mountItem.newChildShadowView.props->rawProps)); - - // Do not hold onto Java object from C - // We DO want to hold onto C object from Java, since we don't know the - // lifetime of the Java object - local_ref javaStateWrapper = nullptr; - if (mountItem.newChildShadowView.state != nullptr) { - javaStateWrapper = StateWrapperImpl::newObjectJavaArgs(); - StateWrapperImpl *cStateWrapper = cthis(javaStateWrapper); - cStateWrapper->state_ = mountItem.newChildShadowView.state; - } - - // Do not hold a reference to javaEventEmitter from the C++ side. - SharedEventEmitter eventEmitter = - mountItem.newChildShadowView.eventEmitter; - auto javaEventEmitter = EventEmitterWrapper::newObjectJavaArgs(); - EventEmitterWrapper *cEventEmitter = cthis(javaEventEmitter); - if (enableEventEmitterRawPointer_) { - cEventEmitter->eventEmitterPointer = eventEmitter.get(); - } else { - cEventEmitter->eventEmitter = eventEmitter; - } - temp[0] = mountItem.newChildShadowView.tag; - temp[1] = isLayoutable; - env->SetIntArrayRegion(intBufferArray, intBufferPosition, 2, temp); - intBufferPosition += 2; - - (*objBufferArray)[objBufferPosition++] = componentName.get(); - (*objBufferArray)[objBufferPosition++] = props.get(); - (*objBufferArray)[objBufferPosition++] = - javaStateWrapper != nullptr ? javaStateWrapper.get() : nullptr; - (*objBufferArray)[objBufferPosition++] = javaEventEmitter.get(); - } else if (mountItemType == CppMountItem::Type::Insert) { - temp[0] = mountItem.newChildShadowView.tag; - temp[1] = mountItem.parentShadowView.tag; - temp[2] = mountItem.index; - env->SetIntArrayRegion(intBufferArray, intBufferPosition, 3, temp); - intBufferPosition += 3; - } else if (mountItemType == CppMountItem::Remove) { - temp[0] = mountItem.oldChildShadowView.tag; - temp[1] = mountItem.parentShadowView.tag; - temp[2] = mountItem.index; - env->SetIntArrayRegion(intBufferArray, intBufferPosition, 3, temp); - intBufferPosition += 3; - } else { - LOG(ERROR) << "Unexpected CppMountItem type"; - } - } - if (!cppUpdatePropsMountItems.empty()) { - writeIntBufferTypePreamble( - CppMountItem::Type::UpdateProps, - cppUpdatePropsMountItems.size(), - env, - intBufferArray, - intBufferPosition); - - for (const auto &mountItem : cppUpdatePropsMountItems) { - temp[0] = mountItem.newChildShadowView.tag; - env->SetIntArrayRegion(intBufferArray, intBufferPosition, 1, temp); - intBufferPosition += 1; - - auto newProps = mountItem.newChildShadowView.props->rawProps; - local_ref newPropsReadableMap = - castReadableMap(ReadableNativeMap::newObjectCxxArgs(newProps)); - (*objBufferArray)[objBufferPosition++] = newPropsReadableMap.get(); - } - } - if (!cppUpdateStateMountItems.empty()) { - writeIntBufferTypePreamble( - CppMountItem::Type::UpdateState, - cppUpdateStateMountItems.size(), - env, - intBufferArray, - intBufferPosition); - - for (const auto &mountItem : cppUpdateStateMountItems) { - temp[0] = mountItem.newChildShadowView.tag; - env->SetIntArrayRegion(intBufferArray, intBufferPosition, 1, temp); - intBufferPosition += 1; - - auto state = mountItem.newChildShadowView.state; - // Do not hold onto Java object from C - // We DO want to hold onto C object from Java, since we don't know the - // lifetime of the Java object - local_ref javaStateWrapper = nullptr; - if (state != nullptr) { - javaStateWrapper = StateWrapperImpl::newObjectJavaArgs(); - StateWrapperImpl *cStateWrapper = cthis(javaStateWrapper); - cStateWrapper->state_ = state; - } - - (*objBufferArray)[objBufferPosition++] = - (javaStateWrapper != nullptr ? javaStateWrapper.get() : nullptr); - } - } - if (!cppUpdatePaddingMountItems.empty()) { - writeIntBufferTypePreamble( - CppMountItem::Type::UpdatePadding, - cppUpdatePaddingMountItems.size(), - env, - intBufferArray, - intBufferPosition); - - for (const auto &mountItem : cppUpdatePaddingMountItems) { - auto layoutMetrics = mountItem.newChildShadowView.layoutMetrics; - auto pointScaleFactor = layoutMetrics.pointScaleFactor; - auto contentInsets = layoutMetrics.contentInsets; - - int left = floor(scale(contentInsets.left, pointScaleFactor)); - int top = floor(scale(contentInsets.top, pointScaleFactor)); - int right = floor(scale(contentInsets.right, pointScaleFactor)); - int bottom = floor(scale(contentInsets.bottom, pointScaleFactor)); - - temp[0] = mountItem.newChildShadowView.tag; - temp[1] = left; - temp[2] = top; - temp[3] = right; - temp[4] = bottom; - env->SetIntArrayRegion(intBufferArray, intBufferPosition, 5, temp); - intBufferPosition += 5; - } - } - if (!cppUpdateLayoutMountItems.empty()) { - writeIntBufferTypePreamble( - CppMountItem::Type::UpdateLayout, - cppUpdateLayoutMountItems.size(), - env, - intBufferArray, - intBufferPosition); - - for (const auto &mountItem : cppUpdateLayoutMountItems) { - auto layoutMetrics = mountItem.newChildShadowView.layoutMetrics; - auto pointScaleFactor = layoutMetrics.pointScaleFactor; - auto frame = layoutMetrics.frame; - - int x = (int)round(scale(frame.origin.x, pointScaleFactor)); - int y = (int)round(scale(frame.origin.y, pointScaleFactor)); - int w = (int)round(scale(frame.size.width, pointScaleFactor)); - int h = (int)round(scale(frame.size.height, pointScaleFactor)); - int displayType = - toInt(mountItem.newChildShadowView.layoutMetrics.displayType); - - temp[0] = mountItem.newChildShadowView.tag; - temp[1] = x; - temp[2] = y; - temp[3] = w; - temp[4] = h; - temp[5] = displayType; - env->SetIntArrayRegion(intBufferArray, intBufferPosition, 6, temp); - intBufferPosition += 6; - } - } - if (!cppUpdateEventEmitterMountItems.empty()) { - writeIntBufferTypePreamble( - CppMountItem::Type::UpdateEventEmitter, - cppUpdateEventEmitterMountItems.size(), - env, - intBufferArray, - intBufferPosition); - - for (const auto &mountItem : cppUpdateEventEmitterMountItems) { - temp[0] = mountItem.newChildShadowView.tag; - env->SetIntArrayRegion(intBufferArray, intBufferPosition, 1, temp); - intBufferPosition += 1; - - SharedEventEmitter eventEmitter = - mountItem.newChildShadowView.eventEmitter; - - // Do not hold a reference to javaEventEmitter from the C++ side. - auto javaEventEmitter = EventEmitterWrapper::newObjectJavaArgs(); - EventEmitterWrapper *cEventEmitter = cthis(javaEventEmitter); - if (enableEventEmitterRawPointer_) { - cEventEmitter->eventEmitterPointer = eventEmitter.get(); - } else { - cEventEmitter->eventEmitter = eventEmitter; - } - - (*objBufferArray)[objBufferPosition++] = javaEventEmitter.get(); - } - } - - // Write deletes last - so that all prop updates, etc, for the tag in the same - // batch don't fail. Without additional machinery, moving deletes here - // requires that the differ never produces "DELETE...CREATE" in that order for - // the same tag. It's nice to be able to batch all similar operations together - // for space efficiency. - if (!cppDeleteMountItems.empty()) { - writeIntBufferTypePreamble( - CppMountItem::Type::Delete, - cppDeleteMountItems.size(), - env, - intBufferArray, - intBufferPosition); - - for (const auto &mountItem : cppDeleteMountItems) { - temp[0] = mountItem.oldChildShadowView.tag; - env->SetIntArrayRegion(intBufferArray, intBufferPosition, 1, temp); - intBufferPosition += 1; - } - } - - // If there are no items, we pass a nullptr instead of passing the object - // through the JNI - auto batch = createMountItemsIntBufferBatchContainer( - localJavaUIManager, - surfaceId, - batchMountItemIntsSize == 0 ? nullptr : intBufferArray, - batchMountItemObjectsSize == 0 ? nullptr : objBufferArray.get(), - revisionNumber); - - auto finishTransactionEndTime = telemetryTimePointNow(); - - scheduleMountItem( - localJavaUIManager, - batch.get(), - telemetry.getRevisionNumber(), - telemetryTimePointToMilliseconds(telemetry.getCommitStartTime()), - telemetryTimePointToMilliseconds(telemetry.getDiffStartTime()), - telemetryTimePointToMilliseconds(telemetry.getDiffEndTime()), - telemetryTimePointToMilliseconds(telemetry.getLayoutStartTime()), - telemetryTimePointToMilliseconds(telemetry.getLayoutEndTime()), - telemetryTimePointToMilliseconds(finishTransactionStartTime), - telemetryTimePointToMilliseconds(finishTransactionEndTime)); - - env->DeleteLocalRef(intBufferArray); -} - -void Binding::setPixelDensity(float pointScaleFactor) { - pointScaleFactor_ = pointScaleFactor; -} - -void Binding::onAnimationStarted() { - jni::global_ref localJavaUIManager = getJavaUIManager(); - if (!localJavaUIManager) { - LOG(ERROR) << "Binding::animationsStarted: JavaUIManager disappeared"; - return; - } - - static auto layoutAnimationsStartedJNI = - jni::findClassStatic(Binding::UIManagerJavaDescriptor) - ->getMethod("onAnimationStarted"); - - layoutAnimationsStartedJNI(localJavaUIManager); -} -void Binding::onAllAnimationsComplete() { - jni::global_ref localJavaUIManager = getJavaUIManager(); - if (!localJavaUIManager) { - LOG(ERROR) << "Binding::allAnimationsComplete: JavaUIManager disappeared"; - return; - } - - static auto allAnimationsCompleteJNI = - jni::findClassStatic(Binding::UIManagerJavaDescriptor) - ->getMethod("onAllAnimationsComplete"); - - allAnimationsCompleteJNI(localJavaUIManager); -} - -void Binding::driveCxxAnimations() { - scheduler_->animationTick(); -} - -void Binding::preallocateShadowView( - const SurfaceId surfaceId, - const ShadowView &shadowView) { - jni::global_ref localJavaUIManager = getJavaUIManager(); - if (!localJavaUIManager) { - LOG(ERROR) - << "Binding::schedulerDidRequestPreliminaryViewAllocation: JavaUIManager disappeared"; - return; - } - - bool isLayoutableShadowNode = shadowView.layoutMetrics != EmptyLayoutMetrics; - - static auto preallocateView = - jni::findClassStatic(Binding::UIManagerJavaDescriptor) - ->getMethod("preallocateView"); - - // Do not hold onto Java object from C - // We DO want to hold onto C object from Java, since we don't know the - // lifetime of the Java object - local_ref javaStateWrapper = nullptr; - if (shadowView.state != nullptr) { - javaStateWrapper = StateWrapperImpl::newObjectJavaArgs(); - StateWrapperImpl *cStateWrapper = cthis(javaStateWrapper); - cStateWrapper->state_ = shadowView.state; - } - - // Do not hold a reference to javaEventEmitter from the C++ side. - local_ref javaEventEmitter = nullptr; - if (enableEarlyEventEmitterUpdate_) { - SharedEventEmitter eventEmitter = shadowView.eventEmitter; - if (eventEmitter != nullptr) { - javaEventEmitter = EventEmitterWrapper::newObjectJavaArgs(); - EventEmitterWrapper *cEventEmitter = cthis(javaEventEmitter); - if (enableEventEmitterRawPointer_) { - cEventEmitter->eventEmitterPointer = eventEmitter.get(); - } else { - cEventEmitter->eventEmitter = eventEmitter; - } - } - } - - local_ref props = castReadableMap( - ReadableNativeMap::newObjectCxxArgs(shadowView.props->rawProps)); - auto component = getPlatformComponentName(shadowView); - - preallocateView( - localJavaUIManager, - surfaceId, - shadowView.tag, - component.get(), - props.get(), - (javaStateWrapper != nullptr ? javaStateWrapper.get() : nullptr), - (javaEventEmitter != nullptr ? javaEventEmitter.get() : nullptr), - isLayoutableShadowNode); + mountingManager->executeMount(mountingCoordinator); } void Binding::schedulerDidRequestPreliminaryViewAllocation( @@ -1237,26 +468,17 @@ void Binding::schedulerDidRequestPreliminaryViewAllocation( return; } - auto shadowView = ShadowView(shadowNode); - - if (!shadowView.traits.check(ShadowNodeTraits::Trait::FormsView)) { + if (!shadowNode.getTraits().check(ShadowNodeTraits::Trait::FormsView)) { return; } - if (dispatchPreallocationInBackground_) { - auto backgroundExecutor = backgroundExecutor_->get(); - backgroundExecutor([this, surfaceId, shadowView = std::move(shadowView)] { - preallocateShadowView(surfaceId, shadowView); - }); - } else { - preallocateShadowView(surfaceId, shadowView); - } + preallocateView(surfaceId, shadowNode); } void Binding::schedulerDidCloneShadowNode( SurfaceId surfaceId, - const ShadowNode &oldShadowNode, - const ShadowNode &newShadowNode) { + ShadowNode const &oldShadowNode, + ShadowNode const &newShadowNode) { // This is only necessary if view preallocation was skipped during // createShadowNode @@ -1277,15 +499,30 @@ void Binding::schedulerDidCloneShadowNode( // If the new node is concrete and the old wasn't, we can preallocate if (!oldShadowNode.getTraits().check(ShadowNodeTraits::Trait::FormsView) && newShadowNode.getTraits().check(ShadowNodeTraits::Trait::FormsView)) { - auto shadowView = ShadowView(newShadowNode); - if (dispatchPreallocationInBackground_) { - auto backgroundExecutor = backgroundExecutor_->get(); - backgroundExecutor([this, surfaceId, shadowView = std::move(shadowView)] { - preallocateShadowView(surfaceId, shadowView); - }); - } else { - preallocateShadowView(surfaceId, shadowView); + preallocateView(surfaceId, newShadowNode); + } +} + +void Binding::preallocateView( + SurfaceId surfaceId, + ShadowNode const &shadowNode) { + auto shadowView = ShadowView(shadowNode); + auto preallocationFunction = [this, + surfaceId, + shadowView = std::move(shadowView)] { + auto mountingManager = verifyMountingManager("Binding::preallocateView"); + if (!mountingManager) { + return; } + + mountingManager->preallocateShadowView(surfaceId, shadowView); + }; + + if (dispatchPreallocationInBackground_) { + auto backgroundExecutor = backgroundExecutor_->get(); + backgroundExecutor(preallocationFunction); + } else { + preallocationFunction(); } } @@ -1293,87 +530,57 @@ void Binding::schedulerDidDispatchCommand( const ShadowView &shadowView, std::string const &commandName, folly::dynamic const &args) { - jni::global_ref localJavaUIManager = getJavaUIManager(); - if (!localJavaUIManager) { - LOG(ERROR) - << "Binding::schedulerDidDispatchCommand: JavaUIManager disappeared"; + auto mountingManager = + verifyMountingManager("Binding::schedulerDidDispatchCommand"); + if (!mountingManager) { return; } - static auto dispatchCommand = - jni::findClassStatic(Binding::UIManagerJavaDescriptor) - ->getMethod( - "dispatchCommand"); - - local_ref command = make_jstring(commandName); - - local_ref argsArray = - castReadableArray(ReadableNativeArray::newObjectCxxArgs(args)); - - dispatchCommand( - localJavaUIManager, - shadowView.surfaceId, - shadowView.tag, - command.get(), - argsArray.get()); + mountingManager->dispatchCommand(shadowView, commandName, args); } void Binding::schedulerDidSendAccessibilityEvent( const ShadowView &shadowView, std::string const &eventType) { - jni::global_ref localJavaUIManager = getJavaUIManager(); - if (!localJavaUIManager) { - LOG(ERROR) - << "Binding::schedulerDidDispatchCommand: JavaUIManager disappeared"; + auto mountingManager = + verifyMountingManager("Binding::schedulerDidSendAccessibilityEvent"); + if (!mountingManager) { return; } - local_ref eventTypeStr = make_jstring(eventType); - - static auto sendAccessibilityEventFromJS = - jni::findClassStatic(Binding::UIManagerJavaDescriptor) - ->getMethod( - "sendAccessibilityEventFromJS"); - - sendAccessibilityEventFromJS( - localJavaUIManager, - shadowView.surfaceId, - shadowView.tag, - eventTypeStr.get()); + mountingManager->sendAccessibilityEvent(shadowView, eventType); } void Binding::schedulerDidSetIsJSResponder( ShadowView const &shadowView, bool isJSResponder, bool blockNativeResponder) { - jni::global_ref localJavaUIManager = getJavaUIManager(); - if (!localJavaUIManager) { - LOG(ERROR) << "Binding::schedulerSetJSResponder: JavaUIManager disappeared"; + auto mountingManager = + verifyMountingManager("Binding::schedulerDidSetIsJSResponder"); + if (!mountingManager) { return; } - static auto setJSResponder = - jni::findClassStatic(Binding::UIManagerJavaDescriptor) - ->getMethod("setJSResponder"); + mountingManager->setIsJSResponder( + shadowView, isJSResponder, blockNativeResponder); +} - static auto clearJSResponder = - jni::findClassStatic(Binding::UIManagerJavaDescriptor) - ->getMethod("clearJSResponder"); - - if (isJSResponder) { - setJSResponder( - localJavaUIManager, - shadowView.surfaceId, - shadowView.tag, - // The closest non-flattened ancestor of the same value if the node is - // not flattened. For now, we don't support the case when the node can - // be flattened because the only component that uses this feature - - // ScrollView - cannot be flattened. - shadowView.tag, - (jboolean)blockNativeResponder); - } else { - clearJSResponder(localJavaUIManager); +void Binding::onAnimationStarted() { + auto mountingManager = verifyMountingManager("Binding::onAnimationStarted"); + if (!mountingManager) { + return; } + + mountingManager->onAnimationStarted(); +} + +void Binding::onAllAnimationsComplete() { + auto mountingManager = verifyMountingManager("Binding::onAnimationComplete"); + if (!mountingManager) { + return; + } + + mountingManager->onAllAnimationsComplete(); } void Binding::registerNatives() { diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Binding.h b/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Binding.h index 598974f7027..5c47720f727 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Binding.h +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Binding.h @@ -7,6 +7,8 @@ #pragma once +#include "FabricMountingManager.h" + #include #include #include @@ -29,49 +31,6 @@ namespace react { class Instance; -struct CppMountItem final { -#pragma mark - Designated Initializers - static CppMountItem CreateMountItem(ShadowView const &shadowView); - static CppMountItem DeleteMountItem(ShadowView const &shadowView); - static CppMountItem InsertMountItem( - ShadowView const &parentView, - ShadowView const &shadowView, - int index); - static CppMountItem RemoveMountItem( - ShadowView const &parentView, - ShadowView const &shadowView, - int index); - static CppMountItem UpdatePropsMountItem(ShadowView const &shadowView); - static CppMountItem UpdateStateMountItem(ShadowView const &shadowView); - static CppMountItem UpdateLayoutMountItem(ShadowView const &shadowView); - static CppMountItem UpdateEventEmitterMountItem(ShadowView const &shadowView); - static CppMountItem UpdatePaddingMountItem(ShadowView const &shadowView); - -#pragma mark - Type - - enum Type { - Undefined = -1, - Multiple = 1, - Create = 2, - Delete = 4, - Insert = 8, - Remove = 16, - UpdateProps = 32, - UpdateState = 64, - UpdateLayout = 128, - UpdateEventEmitter = 256, - UpdatePadding = 512 - }; - -#pragma mark - Fields - - Type type = {Create}; - ShadowView parentShadowView = {}; - ShadowView oldChildShadowView = {}; - ShadowView newChildShadowView = {}; - int index = {}; -}; - class Binding : public jni::HybridClass, public SchedulerDelegate, public LayoutAnimationStatusDelegate { @@ -79,18 +38,12 @@ class Binding : public jni::HybridClass, constexpr static const char *const kJavaDescriptor = "Lcom/facebook/react/fabric/Binding;"; - constexpr static auto UIManagerJavaDescriptor = - "com/facebook/react/fabric/FabricUIManager"; - constexpr static auto ReactFeatureFlagsJavaDescriptor = "com/facebook/react/config/ReactFeatureFlags"; static void registerNatives(); private: - jni::global_ref getJavaUIManager(); - std::shared_ptr getScheduler(); - void setConstraints( jint surfaceId, jfloat minWidth, @@ -144,10 +97,6 @@ class Binding : public jni::HybridClass, void schedulerDidFinishTransaction( MountingCoordinator::Shared const &mountingCoordinator) override; - void preallocateShadowView( - const SurfaceId surfaceId, - const ShadowView &shadowView); - void schedulerDidRequestPreliminaryViewAllocation( const SurfaceId surfaceId, const ShadowNode &shadowNode) override; @@ -171,6 +120,8 @@ class Binding : public jni::HybridClass, bool isJSResponder, bool blockNativeResponder) override; + void preallocateView(SurfaceId surfaceId, ShadowNode const &shadowNode); + void setPixelDensity(float pointScaleFactor); void driveCxxAnimations(); @@ -179,27 +130,30 @@ class Binding : public jni::HybridClass, // Private member variables better::shared_mutex installMutex_; - jni::global_ref javaUIManager_; + std::shared_ptr mountingManager_; std::shared_ptr scheduler_; + std::shared_ptr getScheduler(); + std::shared_ptr verifyMountingManager( + std::string const &locationHint); + // LayoutAnimations void onAnimationStarted() override; void onAllAnimationsComplete() override; + std::shared_ptr animationDriver_; + std::unique_ptr backgroundExecutor_; better::map surfaceHandlerRegistry_{}; better::shared_mutex surfaceHandlerRegistryMutex_; // Protects `surfaceHandlerRegistry_`. - std::recursive_mutex commitMutex_; - float pointScaleFactor_ = 1; std::shared_ptr reactNativeConfig_{nullptr}; bool disablePreallocateViews_{false}; bool enableFabricLogs_{false}; - bool enableEarlyEventEmitterUpdate_{false}; bool disableRevisionCheckForPreallocation_{false}; bool enableEventEmitterRawPointer_{false}; bool dispatchPreallocationInBackground_{false}; diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/FabricMountItem.cpp b/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/FabricMountItem.cpp new file mode 100644 index 00000000000..2d3dda6596b --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/FabricMountItem.cpp @@ -0,0 +1,50 @@ +/* + * 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 "FabricMountItem.h" + +namespace facebook { +namespace react { + +CppMountItem CppMountItem::CreateMountItem(ShadowView const &shadowView) { + return {CppMountItem::Type::Create, {}, {}, shadowView, -1}; +} +CppMountItem CppMountItem::DeleteMountItem(ShadowView const &shadowView) { + return {CppMountItem::Type::Delete, {}, shadowView, {}, -1}; +} +CppMountItem CppMountItem::InsertMountItem( + ShadowView const &parentView, + ShadowView const &shadowView, + int index) { + return {CppMountItem::Type::Insert, parentView, {}, shadowView, index}; +} +CppMountItem CppMountItem::RemoveMountItem( + ShadowView const &parentView, + ShadowView const &shadowView, + int index) { + return {CppMountItem::Type::Remove, parentView, shadowView, {}, index}; +} +CppMountItem CppMountItem::UpdatePropsMountItem(ShadowView const &shadowView) { + return {CppMountItem::Type::UpdateProps, {}, {}, shadowView, -1}; +} +CppMountItem CppMountItem::UpdateStateMountItem(ShadowView const &shadowView) { + return {CppMountItem::Type::UpdateState, {}, {}, shadowView, -1}; +} +CppMountItem CppMountItem::UpdateLayoutMountItem(ShadowView const &shadowView) { + return {CppMountItem::Type::UpdateLayout, {}, {}, shadowView, -1}; +} +CppMountItem CppMountItem::UpdateEventEmitterMountItem( + ShadowView const &shadowView) { + return {CppMountItem::Type::UpdateEventEmitter, {}, {}, shadowView, -1}; +} +CppMountItem CppMountItem::UpdatePaddingMountItem( + ShadowView const &shadowView) { + return {CppMountItem::Type::UpdatePadding, {}, {}, shadowView, -1}; +} + +} // namespace react +} // namespace facebook diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/FabricMountItem.h b/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/FabricMountItem.h new file mode 100644 index 00000000000..6a851270ff1 --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/FabricMountItem.h @@ -0,0 +1,74 @@ +/* + * 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 +#include + +namespace facebook { +namespace react { + +struct JMountItem : public jni::JavaClass { + static constexpr auto kJavaDescriptor = + "Lcom/facebook/react/fabric/mounting/mountitems/MountItem;"; +}; + +struct CppMountItem final { +#pragma mark - Designated Initializers + + static CppMountItem CreateMountItem(ShadowView const &shadowView); + + static CppMountItem DeleteMountItem(ShadowView const &shadowView); + + static CppMountItem InsertMountItem( + ShadowView const &parentView, + ShadowView const &shadowView, + int index); + + static CppMountItem RemoveMountItem( + ShadowView const &parentView, + ShadowView const &shadowView, + int index); + + static CppMountItem UpdatePropsMountItem(ShadowView const &shadowView); + + static CppMountItem UpdateStateMountItem(ShadowView const &shadowView); + + static CppMountItem UpdateLayoutMountItem(ShadowView const &shadowView); + + static CppMountItem UpdateEventEmitterMountItem(ShadowView const &shadowView); + + static CppMountItem UpdatePaddingMountItem(ShadowView const &shadowView); + +#pragma mark - Type + + enum Type { + Undefined = -1, + Multiple = 1, + Create = 2, + Delete = 4, + Insert = 8, + Remove = 16, + UpdateProps = 32, + UpdateState = 64, + UpdateLayout = 128, + UpdateEventEmitter = 256, + UpdatePadding = 512 + }; + +#pragma mark - Fields + + Type type = {Create}; + ShadowView parentShadowView = {}; + ShadowView oldChildShadowView = {}; + ShadowView newChildShadowView = {}; + int index = {}; +}; + +} // namespace react +} // namespace facebook diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/FabricMountingManager.cpp b/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/FabricMountingManager.cpp new file mode 100644 index 00000000000..e78540767a2 --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/FabricMountingManager.cpp @@ -0,0 +1,821 @@ +/* + * 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 "FabricMountingManager.h" +#include "EventEmitterWrapper.h" +#include "StateWrapperImpl.h" + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +using namespace facebook::jni; + +namespace facebook { +namespace react { + +static inline int getIntBufferSizeForType(CppMountItem::Type mountItemType) { + switch (mountItemType) { + case CppMountItem::Type::Create: + return 2; // tag, isLayoutable + case CppMountItem::Type::Insert: + case CppMountItem::Type::Remove: + return 3; // tag, parentTag, index + case CppMountItem::Type::Delete: + case CppMountItem::Type::UpdateProps: + case CppMountItem::Type::UpdateState: + case CppMountItem::Type::UpdateEventEmitter: + return 1; // tag + case CppMountItem::Type::UpdatePadding: + return 5; // tag, top, left, bottom, right + case CppMountItem::Type::UpdateLayout: + return 6; // tag, x, y, w, h, DisplayType + case CppMountItem::Undefined: + case CppMountItem::Multiple: + return -1; + } +} + +static inline void updateBufferSizes( + CppMountItem::Type mountItemType, + int numInstructions, + int &batchMountItemIntsSize, + int &batchMountItemObjectsSize) { + if (numInstructions == 0) { + return; + } + + batchMountItemIntsSize += + numInstructions == 1 ? 1 : 2; // instructionType[, numInstructions] + batchMountItemIntsSize += + numInstructions * getIntBufferSizeForType(mountItemType); + + if (mountItemType == CppMountItem::Type::UpdateProps) { + batchMountItemObjectsSize += + numInstructions; // props object * numInstructions + } else if (mountItemType == CppMountItem::Type::UpdateState) { + batchMountItemObjectsSize += + numInstructions; // state object * numInstructions + } else if (mountItemType == CppMountItem::Type::UpdateEventEmitter) { + batchMountItemObjectsSize += + numInstructions; // EventEmitter object * numInstructions + } +} + +static inline void computeBufferSizes( + int &batchMountItemIntsSize, + int &batchMountItemObjectsSize, + std::vector &cppCommonMountItems, + std::vector &cppDeleteMountItems, + std::vector &cppUpdatePropsMountItems, + std::vector &cppUpdateStateMountItems, + std::vector &cppUpdatePaddingMountItems, + std::vector &cppUpdateLayoutMountItems, + std::vector &cppUpdateEventEmitterMountItems) { + CppMountItem::Type lastType = CppMountItem::Type::Undefined; + int numSameType = 0; + for (auto const &mountItem : cppCommonMountItems) { + const auto &mountItemType = mountItem.type; + + if (lastType == mountItemType) { + numSameType++; + if (numSameType == 2) { + batchMountItemIntsSize += 1; // numInstructions + } + } else { + numSameType = 1; + lastType = mountItemType; + batchMountItemIntsSize += 1; // instructionType + } + + batchMountItemIntsSize += getIntBufferSizeForType(mountItemType); + if (mountItemType == CppMountItem::Type::Create) { + batchMountItemObjectsSize += + 4; // component name, props, state, event emitter + } + } + + updateBufferSizes( + CppMountItem::Type::UpdateProps, + cppUpdatePropsMountItems.size(), + batchMountItemIntsSize, + batchMountItemObjectsSize); + updateBufferSizes( + CppMountItem::Type::UpdateState, + cppUpdateStateMountItems.size(), + batchMountItemIntsSize, + batchMountItemObjectsSize); + updateBufferSizes( + CppMountItem::Type::UpdatePadding, + cppUpdatePaddingMountItems.size(), + batchMountItemIntsSize, + batchMountItemObjectsSize); + updateBufferSizes( + CppMountItem::Type::UpdateLayout, + cppUpdateLayoutMountItems.size(), + batchMountItemIntsSize, + batchMountItemObjectsSize); + updateBufferSizes( + CppMountItem::Type::UpdateEventEmitter, + cppUpdateEventEmitterMountItems.size(), + batchMountItemIntsSize, + batchMountItemObjectsSize); + updateBufferSizes( + CppMountItem::Type::Delete, + cppDeleteMountItems.size(), + batchMountItemIntsSize, + batchMountItemObjectsSize); +} + +static inline void writeIntBufferTypePreamble( + int mountItemType, + int numItems, + _JNIEnv *env, + jintArray &intBufferArray, + int &intBufferPosition) { + jint temp[2]; + if (numItems == 1) { + temp[0] = mountItemType; + env->SetIntArrayRegion(intBufferArray, intBufferPosition, 1, temp); + intBufferPosition += 1; + } else { + temp[0] = mountItemType | CppMountItem::Type::Multiple; + temp[1] = numItems; + env->SetIntArrayRegion(intBufferArray, intBufferPosition, 2, temp); + intBufferPosition += 2; + } +} + +inline local_ref castReadableMap( + local_ref const &nativeMap) { + return make_local(reinterpret_cast(nativeMap.get())); +} + +inline local_ref castReadableArray( + local_ref const &nativeArray) { + return make_local( + reinterpret_cast(nativeArray.get())); +} + +// TODO: this method will be removed when binding for components are code-gen +local_ref getPlatformComponentName(ShadowView const &shadowView) { + static std::string scrollViewComponentName = std::string("ScrollView"); + + local_ref componentName; + if (scrollViewComponentName == shadowView.componentName) { + auto newViewProps = + std::static_pointer_cast(shadowView.props); + if (newViewProps->getProbablyMoreHorizontalThanVertical_DEPRECATED()) { + componentName = make_jstring("AndroidHorizontalScrollView"); + return componentName; + } + } + + componentName = make_jstring(shadowView.componentName); + return componentName; +} + +static inline float scale(Float value, Float pointScaleFactor) { + std::feclearexcept(FE_ALL_EXCEPT); + float result = value * pointScaleFactor; + if (std::fetestexcept(FE_OVERFLOW)) { + LOG(ERROR) << "Binding::scale - FE_OVERFLOW - value: " << value + << " pointScaleFactor: " << pointScaleFactor + << " result: " << result; + } + if (std::fetestexcept(FE_UNDERFLOW)) { + LOG(ERROR) << "Binding::scale - FE_UNDERFLOW - value: " << value + << " pointScaleFactor: " << pointScaleFactor + << " result: " << result; + } + return result; +} + +void FabricMountingManager::executeMount( + MountingCoordinator::Shared const &mountingCoordinator) { + std::lock_guard lock(commitMutex_); + + SystraceSection s( + "FabricUIManagerBinding::schedulerDidFinishTransactionIntBuffer"); + auto finishTransactionStartTime = telemetryTimePointNow(); + + auto mountingTransaction = mountingCoordinator->pullTransaction(); + + if (!mountingTransaction.has_value()) { + return; + } + + auto env = Environment::current(); + + auto telemetry = mountingTransaction->getTelemetry(); + auto surfaceId = mountingTransaction->getSurfaceId(); + auto &mutations = mountingTransaction->getMutations(); + + auto revisionNumber = telemetry.getRevisionNumber(); + + std::vector cppCommonMountItems; + std::vector cppDeleteMountItems; + std::vector cppUpdatePropsMountItems; + std::vector cppUpdateStateMountItems; + std::vector cppUpdatePaddingMountItems; + std::vector cppUpdateLayoutMountItems; + std::vector cppUpdateEventEmitterMountItems; + + for (const auto &mutation : mutations) { + const auto &parentShadowView = mutation.parentShadowView; + const auto &oldChildShadowView = mutation.oldChildShadowView; + const auto &newChildShadowView = mutation.newChildShadowView; + auto &mutationType = mutation.type; + auto &index = mutation.index; + + bool isVirtual = mutation.mutatedViewIsVirtual(); + + bool noRevisionCheck = + disablePreallocateViews_ || disableRevisionCheckForPreallocation_; + + switch (mutationType) { + case ShadowViewMutation::Create: { + if (noRevisionCheck || newChildShadowView.props->revision > 1) { + cppCommonMountItems.push_back( + CppMountItem::CreateMountItem(newChildShadowView)); + } + break; + } + case ShadowViewMutation::Remove: { + if (!isVirtual) { + cppCommonMountItems.push_back(CppMountItem::RemoveMountItem( + parentShadowView, oldChildShadowView, index)); + } + break; + } + case ShadowViewMutation::Delete: { + cppDeleteMountItems.push_back( + CppMountItem::DeleteMountItem(oldChildShadowView)); + break; + } + case ShadowViewMutation::Update: { + if (!isVirtual) { + if (oldChildShadowView.props != newChildShadowView.props) { + cppUpdatePropsMountItems.push_back( + CppMountItem::UpdatePropsMountItem(newChildShadowView)); + } + if (oldChildShadowView.state != newChildShadowView.state) { + cppUpdateStateMountItems.push_back( + CppMountItem::UpdateStateMountItem(newChildShadowView)); + } + + // Padding: padding mountItems must be executed before layout props + // are updated in the view. This is necessary to ensure that events + // (resulting from layout changes) are dispatched with the correct + // padding information. + if (oldChildShadowView.layoutMetrics.contentInsets != + newChildShadowView.layoutMetrics.contentInsets) { + cppUpdatePaddingMountItems.push_back( + CppMountItem::UpdatePaddingMountItem(newChildShadowView)); + } + + if (oldChildShadowView.layoutMetrics != + newChildShadowView.layoutMetrics) { + cppUpdateLayoutMountItems.push_back( + CppMountItem::UpdateLayoutMountItem( + mutation.newChildShadowView)); + } + } + + if (oldChildShadowView.eventEmitter != + newChildShadowView.eventEmitter) { + cppUpdateEventEmitterMountItems.push_back( + CppMountItem::UpdateEventEmitterMountItem( + mutation.newChildShadowView)); + } + break; + } + case ShadowViewMutation::Insert: { + if (!isVirtual) { + // Insert item + cppCommonMountItems.push_back(CppMountItem::InsertMountItem( + parentShadowView, newChildShadowView, index)); + + if (noRevisionCheck || newChildShadowView.props->revision > 1) { + cppUpdatePropsMountItems.push_back( + CppMountItem::UpdatePropsMountItem(newChildShadowView)); + } + + // State + if (newChildShadowView.state) { + cppUpdateStateMountItems.push_back( + CppMountItem::UpdateStateMountItem(newChildShadowView)); + } + + // Padding: padding mountItems must be executed before layout props + // are updated in the view. This is necessary to ensure that events + // (resulting from layout changes) are dispatched with the correct + // padding information. + cppUpdatePaddingMountItems.push_back( + CppMountItem::UpdatePaddingMountItem( + mutation.newChildShadowView)); + + // Layout + cppUpdateLayoutMountItems.push_back( + CppMountItem::UpdateLayoutMountItem(mutation.newChildShadowView)); + } + + // EventEmitter + cppUpdateEventEmitterMountItems.push_back( + CppMountItem::UpdateEventEmitterMountItem( + mutation.newChildShadowView)); + + break; + } + default: { + break; + } + } + } + + // We now have all the information we need, including ordering of mount items, + // to know exactly how much space must be allocated + int batchMountItemIntsSize = 0; + int batchMountItemObjectsSize = 0; + computeBufferSizes( + batchMountItemIntsSize, + batchMountItemObjectsSize, + cppCommonMountItems, + cppDeleteMountItems, + cppUpdatePropsMountItems, + cppUpdateStateMountItems, + cppUpdatePaddingMountItems, + cppUpdateLayoutMountItems, + cppUpdateEventEmitterMountItems); + + static auto createMountItemsIntBufferBatchContainer = + jni::findClassStatic(UIManagerJavaDescriptor) + ->getMethod( + jint, jintArray, jtypeArray, jint)>( + "createIntBufferBatchMountItem"); + + static auto scheduleMountItem = jni::findClassStatic(UIManagerJavaDescriptor) + ->getMethod("scheduleMountItem"); + + if (batchMountItemIntsSize == 0) { + auto finishTransactionEndTime = telemetryTimePointNow(); + + scheduleMountItem( + javaUIManager_, + nullptr, + telemetry.getRevisionNumber(), + telemetryTimePointToMilliseconds(telemetry.getCommitStartTime()), + telemetryTimePointToMilliseconds(telemetry.getDiffStartTime()), + telemetryTimePointToMilliseconds(telemetry.getDiffEndTime()), + telemetryTimePointToMilliseconds(telemetry.getLayoutStartTime()), + telemetryTimePointToMilliseconds(telemetry.getLayoutEndTime()), + telemetryTimePointToMilliseconds(finishTransactionStartTime), + telemetryTimePointToMilliseconds(finishTransactionEndTime)); + return; + } + + // Allocate the intBuffer and object array, now that we know exact sizes + // necessary + // TODO: don't allocate at all if size is zero + jintArray intBufferArray = env->NewIntArray(batchMountItemIntsSize); + local_ref> objBufferArray = + JArrayClass::newArray(batchMountItemObjectsSize); + + // Fill in arrays + int intBufferPosition = 0; + int objBufferPosition = 0; + int prevMountItemType = -1; + jint temp[7]; + for (int i = 0; i < cppCommonMountItems.size(); i++) { + const auto &mountItem = cppCommonMountItems[i]; + const auto &mountItemType = mountItem.type; + + // Get type here, and count forward how many items of this type are in a + // row. Write preamble to any common type here. + if (prevMountItemType != mountItemType) { + int numSameItemTypes = 1; + for (int j = i + 1; j < cppCommonMountItems.size() && + cppCommonMountItems[j].type == mountItemType; + j++) { + numSameItemTypes++; + } + + writeIntBufferTypePreamble( + mountItemType, + numSameItemTypes, + env, + intBufferArray, + intBufferPosition); + } + prevMountItemType = mountItemType; + + // TODO: multi-create, multi-insert, etc + if (mountItemType == CppMountItem::Type::Create) { + local_ref componentName = + getPlatformComponentName(mountItem.newChildShadowView); + + int isLayoutable = + mountItem.newChildShadowView.layoutMetrics != EmptyLayoutMetrics ? 1 + : 0; + + local_ref props = + castReadableMap(ReadableNativeMap::newObjectCxxArgs( + mountItem.newChildShadowView.props->rawProps)); + + // Do not hold onto Java object from C + // We DO want to hold onto C object from Java, since we don't know the + // lifetime of the Java object + local_ref javaStateWrapper = nullptr; + if (mountItem.newChildShadowView.state != nullptr) { + javaStateWrapper = StateWrapperImpl::newObjectJavaArgs(); + StateWrapperImpl *cStateWrapper = cthis(javaStateWrapper); + cStateWrapper->state_ = mountItem.newChildShadowView.state; + } + + // Do not hold a reference to javaEventEmitter from the C++ side. + SharedEventEmitter eventEmitter = + mountItem.newChildShadowView.eventEmitter; + auto javaEventEmitter = EventEmitterWrapper::newObjectJavaArgs(); + EventEmitterWrapper *cEventEmitter = cthis(javaEventEmitter); + if (enableEventEmitterRawPointer_) { + cEventEmitter->eventEmitterPointer = eventEmitter.get(); + } else { + cEventEmitter->eventEmitter = eventEmitter; + } + temp[0] = mountItem.newChildShadowView.tag; + temp[1] = isLayoutable; + env->SetIntArrayRegion(intBufferArray, intBufferPosition, 2, temp); + intBufferPosition += 2; + + (*objBufferArray)[objBufferPosition++] = componentName.get(); + (*objBufferArray)[objBufferPosition++] = props.get(); + (*objBufferArray)[objBufferPosition++] = + javaStateWrapper != nullptr ? javaStateWrapper.get() : nullptr; + (*objBufferArray)[objBufferPosition++] = javaEventEmitter.get(); + } else if (mountItemType == CppMountItem::Type::Insert) { + temp[0] = mountItem.newChildShadowView.tag; + temp[1] = mountItem.parentShadowView.tag; + temp[2] = mountItem.index; + env->SetIntArrayRegion(intBufferArray, intBufferPosition, 3, temp); + intBufferPosition += 3; + } else if (mountItemType == CppMountItem::Remove) { + temp[0] = mountItem.oldChildShadowView.tag; + temp[1] = mountItem.parentShadowView.tag; + temp[2] = mountItem.index; + env->SetIntArrayRegion(intBufferArray, intBufferPosition, 3, temp); + intBufferPosition += 3; + } else { + LOG(ERROR) << "Unexpected CppMountItem type"; + } + } + if (!cppUpdatePropsMountItems.empty()) { + writeIntBufferTypePreamble( + CppMountItem::Type::UpdateProps, + cppUpdatePropsMountItems.size(), + env, + intBufferArray, + intBufferPosition); + + for (const auto &mountItem : cppUpdatePropsMountItems) { + temp[0] = mountItem.newChildShadowView.tag; + env->SetIntArrayRegion(intBufferArray, intBufferPosition, 1, temp); + intBufferPosition += 1; + + auto newProps = mountItem.newChildShadowView.props->rawProps; + local_ref newPropsReadableMap = + castReadableMap(ReadableNativeMap::newObjectCxxArgs(newProps)); + (*objBufferArray)[objBufferPosition++] = newPropsReadableMap.get(); + } + } + if (!cppUpdateStateMountItems.empty()) { + writeIntBufferTypePreamble( + CppMountItem::Type::UpdateState, + cppUpdateStateMountItems.size(), + env, + intBufferArray, + intBufferPosition); + + for (const auto &mountItem : cppUpdateStateMountItems) { + temp[0] = mountItem.newChildShadowView.tag; + env->SetIntArrayRegion(intBufferArray, intBufferPosition, 1, temp); + intBufferPosition += 1; + + auto state = mountItem.newChildShadowView.state; + // Do not hold onto Java object from C + // We DO want to hold onto C object from Java, since we don't know the + // lifetime of the Java object + local_ref javaStateWrapper = nullptr; + if (state != nullptr) { + javaStateWrapper = StateWrapperImpl::newObjectJavaArgs(); + StateWrapperImpl *cStateWrapper = cthis(javaStateWrapper); + cStateWrapper->state_ = state; + } + + (*objBufferArray)[objBufferPosition++] = + (javaStateWrapper != nullptr ? javaStateWrapper.get() : nullptr); + } + } + if (!cppUpdatePaddingMountItems.empty()) { + writeIntBufferTypePreamble( + CppMountItem::Type::UpdatePadding, + cppUpdatePaddingMountItems.size(), + env, + intBufferArray, + intBufferPosition); + + for (const auto &mountItem : cppUpdatePaddingMountItems) { + auto layoutMetrics = mountItem.newChildShadowView.layoutMetrics; + auto pointScaleFactor = layoutMetrics.pointScaleFactor; + auto contentInsets = layoutMetrics.contentInsets; + + int left = floor(scale(contentInsets.left, pointScaleFactor)); + int top = floor(scale(contentInsets.top, pointScaleFactor)); + int right = floor(scale(contentInsets.right, pointScaleFactor)); + int bottom = floor(scale(contentInsets.bottom, pointScaleFactor)); + + temp[0] = mountItem.newChildShadowView.tag; + temp[1] = left; + temp[2] = top; + temp[3] = right; + temp[4] = bottom; + env->SetIntArrayRegion(intBufferArray, intBufferPosition, 5, temp); + intBufferPosition += 5; + } + } + if (!cppUpdateLayoutMountItems.empty()) { + writeIntBufferTypePreamble( + CppMountItem::Type::UpdateLayout, + cppUpdateLayoutMountItems.size(), + env, + intBufferArray, + intBufferPosition); + + for (const auto &mountItem : cppUpdateLayoutMountItems) { + auto layoutMetrics = mountItem.newChildShadowView.layoutMetrics; + auto pointScaleFactor = layoutMetrics.pointScaleFactor; + auto frame = layoutMetrics.frame; + + int x = round(scale(frame.origin.x, pointScaleFactor)); + int y = round(scale(frame.origin.y, pointScaleFactor)); + int w = round(scale(frame.size.width, pointScaleFactor)); + int h = round(scale(frame.size.height, pointScaleFactor)); + int displayType = + toInt(mountItem.newChildShadowView.layoutMetrics.displayType); + + temp[0] = mountItem.newChildShadowView.tag; + temp[1] = x; + temp[2] = y; + temp[3] = w; + temp[4] = h; + temp[5] = displayType; + env->SetIntArrayRegion(intBufferArray, intBufferPosition, 6, temp); + intBufferPosition += 6; + } + } + if (!cppUpdateEventEmitterMountItems.empty()) { + writeIntBufferTypePreamble( + CppMountItem::Type::UpdateEventEmitter, + cppUpdateEventEmitterMountItems.size(), + env, + intBufferArray, + intBufferPosition); + + for (const auto &mountItem : cppUpdateEventEmitterMountItems) { + temp[0] = mountItem.newChildShadowView.tag; + env->SetIntArrayRegion(intBufferArray, intBufferPosition, 1, temp); + intBufferPosition += 1; + + SharedEventEmitter eventEmitter = + mountItem.newChildShadowView.eventEmitter; + + // Do not hold a reference to javaEventEmitter from the C++ side. + auto javaEventEmitter = EventEmitterWrapper::newObjectJavaArgs(); + EventEmitterWrapper *cEventEmitter = cthis(javaEventEmitter); + if (enableEventEmitterRawPointer_) { + cEventEmitter->eventEmitterPointer = eventEmitter.get(); + } else { + cEventEmitter->eventEmitter = eventEmitter; + } + + (*objBufferArray)[objBufferPosition++] = javaEventEmitter.get(); + } + } + + // Write deletes last - so that all prop updates, etc, for the tag in the same + // batch don't fail. Without additional machinery, moving deletes here + // requires that the differ never produces "DELETE...CREATE" in that order for + // the same tag. It's nice to be able to batch all similar operations together + // for space efficiency. + if (!cppDeleteMountItems.empty()) { + writeIntBufferTypePreamble( + CppMountItem::Type::Delete, + cppDeleteMountItems.size(), + env, + intBufferArray, + intBufferPosition); + + for (const auto &mountItem : cppDeleteMountItems) { + temp[0] = mountItem.oldChildShadowView.tag; + env->SetIntArrayRegion(intBufferArray, intBufferPosition, 1, temp); + intBufferPosition += 1; + } + } + + // If there are no items, we pass a nullptr instead of passing the object + // through the JNI + auto batch = createMountItemsIntBufferBatchContainer( + javaUIManager_, + surfaceId, + batchMountItemIntsSize == 0 ? nullptr : intBufferArray, + batchMountItemObjectsSize == 0 ? nullptr : objBufferArray.get(), + revisionNumber); + + auto finishTransactionEndTime = telemetryTimePointNow(); + + scheduleMountItem( + javaUIManager_, + batch.get(), + telemetry.getRevisionNumber(), + telemetryTimePointToMilliseconds(telemetry.getCommitStartTime()), + telemetryTimePointToMilliseconds(telemetry.getDiffStartTime()), + telemetryTimePointToMilliseconds(telemetry.getDiffEndTime()), + telemetryTimePointToMilliseconds(telemetry.getLayoutStartTime()), + telemetryTimePointToMilliseconds(telemetry.getLayoutEndTime()), + telemetryTimePointToMilliseconds(finishTransactionStartTime), + telemetryTimePointToMilliseconds(finishTransactionEndTime)); + + env->DeleteLocalRef(intBufferArray); +} + +void FabricMountingManager::preallocateShadowView( + SurfaceId surfaceId, + ShadowView const &shadowView) { + bool isLayoutableShadowNode = shadowView.layoutMetrics != EmptyLayoutMetrics; + + static auto preallocateView = jni::findClassStatic(UIManagerJavaDescriptor) + ->getMethod("preallocateView"); + + // Do not hold onto Java object from C + // We DO want to hold onto C object from Java, since we don't know the + // lifetime of the Java object + local_ref javaStateWrapper = nullptr; + if (shadowView.state != nullptr) { + javaStateWrapper = StateWrapperImpl::newObjectJavaArgs(); + StateWrapperImpl *cStateWrapper = cthis(javaStateWrapper); + cStateWrapper->state_ = shadowView.state; + } + + // Do not hold a reference to javaEventEmitter from the C++ side. + local_ref javaEventEmitter = nullptr; + if (enableEarlyEventEmitterUpdate_) { + SharedEventEmitter eventEmitter = shadowView.eventEmitter; + if (eventEmitter != nullptr) { + javaEventEmitter = EventEmitterWrapper::newObjectJavaArgs(); + EventEmitterWrapper *cEventEmitter = cthis(javaEventEmitter); + if (enableEventEmitterRawPointer_) { + cEventEmitter->eventEmitterPointer = eventEmitter.get(); + } else { + cEventEmitter->eventEmitter = eventEmitter; + } + } + } + + local_ref props = castReadableMap( + ReadableNativeMap::newObjectCxxArgs(shadowView.props->rawProps)); + auto component = getPlatformComponentName(shadowView); + + preallocateView( + javaUIManager_, + surfaceId, + shadowView.tag, + component.get(), + props.get(), + (javaStateWrapper != nullptr ? javaStateWrapper.get() : nullptr), + (javaEventEmitter != nullptr ? javaEventEmitter.get() : nullptr), + isLayoutableShadowNode); +} + +void FabricMountingManager::dispatchCommand( + ShadowView const &shadowView, + std::string const &commandName, + folly::dynamic const &args) { + static auto dispatchCommand = + jni::findClassStatic(UIManagerJavaDescriptor) + ->getMethod( + "dispatchCommand"); + + local_ref command = make_jstring(commandName); + + local_ref argsArray = + castReadableArray(ReadableNativeArray::newObjectCxxArgs(args)); + + dispatchCommand( + javaUIManager_, + shadowView.surfaceId, + shadowView.tag, + command.get(), + argsArray.get()); +} + +void FabricMountingManager::sendAccessibilityEvent( + ShadowView const &shadowView, + std::string const &eventType) { + local_ref eventTypeStr = make_jstring(eventType); + + static auto sendAccessibilityEventFromJS = + jni::findClassStatic(UIManagerJavaDescriptor) + ->getMethod( + "sendAccessibilityEventFromJS"); + + sendAccessibilityEventFromJS( + javaUIManager_, shadowView.surfaceId, shadowView.tag, eventTypeStr.get()); +} + +void FabricMountingManager::setIsJSResponder( + ShadowView const &shadowView, + bool isJSResponder, + bool blockNativeResponder) { + static auto setJSResponder = + jni::findClassStatic(UIManagerJavaDescriptor) + ->getMethod("setJSResponder"); + + static auto clearJSResponder = jni::findClassStatic(UIManagerJavaDescriptor) + ->getMethod("clearJSResponder"); + + if (isJSResponder) { + setJSResponder( + javaUIManager_, + shadowView.surfaceId, + shadowView.tag, + // The closest non-flattened ancestor of the same value if the node is + // not flattened. For now, we don't support the case when the node can + // be flattened because the only component that uses this feature - + // ScrollView - cannot be flattened. + shadowView.tag, + (jboolean)blockNativeResponder); + } else { + clearJSResponder(javaUIManager_); + } +} + +void FabricMountingManager::onAnimationStarted() { + static auto layoutAnimationsStartedJNI = + jni::findClassStatic(UIManagerJavaDescriptor) + ->getMethod("onAnimationStarted"); + + layoutAnimationsStartedJNI(javaUIManager_); +} + +void FabricMountingManager::onAllAnimationsComplete() { + static auto allAnimationsCompleteJNI = + jni::findClassStatic(UIManagerJavaDescriptor) + ->getMethod("onAllAnimationsComplete"); + + allAnimationsCompleteJNI(javaUIManager_); +} + +FabricMountingManager::FabricMountingManager( + std::shared_ptr &config, + global_ref &javaUIManager) + : javaUIManager_(javaUIManager) { + enableEarlyEventEmitterUpdate_ = + config->getBool("react_fabric:enable_early_event_emitter_update"); + enableEventEmitterRawPointer_ = + config->getBool("react_fabric:enable_event_emitter_wrapper_raw_pointer"); + disablePreallocateViews_ = + config->getBool("react_fabric:disabled_view_preallocation_android"); + disableRevisionCheckForPreallocation_ = + config->getBool("react_fabric:disable_revision_check_for_preallocation"); +} + +} // namespace react +} // namespace facebook diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/FabricMountingManager.h b/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/FabricMountingManager.h new file mode 100644 index 00000000000..e95ad1ef173 --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/FabricMountingManager.h @@ -0,0 +1,71 @@ +/* + * 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 "FabricMountItem.h" + +#include +#include +#include +#include +#include +#include + +#include + +#include + +namespace facebook { +namespace react { + +class FabricMountingManager { + public: + constexpr static auto UIManagerJavaDescriptor = + "com/facebook/react/fabric/FabricUIManager"; + + FabricMountingManager( + std::shared_ptr &config, + jni::global_ref &javaUIManager); + + void preallocateShadowView(SurfaceId surfaceId, const ShadowView &shadowView); + + void executeMount(MountingCoordinator::Shared const &mountingCoordinator); + + void dispatchCommand( + ShadowView const &shadowView, + std::string const &commandName, + folly::dynamic const &args); + + void sendAccessibilityEvent( + const ShadowView &shadowView, + std::string const &eventType); + + void setIsJSResponder( + ShadowView const &shadowView, + bool isJSResponder, + bool blockNativeResponder); + + void onAnimationStarted(); + + void onAllAnimationsComplete(); + + virtual ~FabricMountingManager() = default; + + private: + jni::global_ref javaUIManager_; + + std::recursive_mutex commitMutex_; + + bool enableEventEmitterRawPointer_{false}; + bool enableEarlyEventEmitterUpdate_{false}; + bool disablePreallocateViews_{false}; + bool disableRevisionCheckForPreallocation_{false}; +}; + +} // namespace react +} // namespace facebook