Ship IntBufferBatchMountItem experiment

Summary:
This experiment has been successfully running for several weeks and show small but statsig perf improvements. Delete the old code and ship this 100% in code to simplify Fabric code.

Changelog: [Internal]

Reviewed By: mdvacca

Differential Revision: D25775668

fbshipit-source-id: d2b41dfe691775e52b1e89c2fb6790a6500e560e
This commit is contained in:
Joshua Gross
2021-01-05 10:44:44 -08:00
committed by Facebook GitHub Bot
parent a2164f429a
commit 0db56f1888
13 changed files with 6 additions and 1193 deletions
@@ -18,21 +18,13 @@ import com.facebook.react.fabric.events.EventEmitterWrapper;
import com.facebook.react.fabric.events.FabricEventEmitter;
import com.facebook.react.fabric.mounting.LayoutMetricsConversions;
import com.facebook.react.fabric.mounting.MountingManager;
import com.facebook.react.fabric.mounting.mountitems.BatchMountItem;
import com.facebook.react.fabric.mounting.mountitems.CreateMountItem;
import com.facebook.react.fabric.mounting.mountitems.DispatchCommandMountItem;
import com.facebook.react.fabric.mounting.mountitems.DispatchIntCommandMountItem;
import com.facebook.react.fabric.mounting.mountitems.DispatchStringCommandMountItem;
import com.facebook.react.fabric.mounting.mountitems.InsertMountItem;
import com.facebook.react.fabric.mounting.mountitems.IntBufferBatchMountItem;
import com.facebook.react.fabric.mounting.mountitems.MountItem;
import com.facebook.react.fabric.mounting.mountitems.PreAllocateViewMountItem;
import com.facebook.react.fabric.mounting.mountitems.RemoveDeleteMultiMountItem;
import com.facebook.react.fabric.mounting.mountitems.SendAccessibilityEvent;
import com.facebook.react.fabric.mounting.mountitems.UpdateEventEmitterMountItem;
import com.facebook.react.fabric.mounting.mountitems.UpdateLayoutMountItem;
import com.facebook.react.fabric.mounting.mountitems.UpdatePaddingMountItem;
import com.facebook.react.fabric.mounting.mountitems.UpdatePropsMountItem;
import com.facebook.react.fabric.mounting.mountitems.UpdateStateMountItem;
import com.facebook.react.uimanager.StateWrapper;
import com.facebook.react.uimanager.UIManagerModule;
import com.facebook.react.uimanager.events.BatchEventDispatchedListener;
@@ -107,21 +99,12 @@ public class FabricJSIModuleProvider implements JSIModuleProvider<UIManager> {
EventBeatManager.class.getClass();
EventEmitterWrapper.class.getClass();
FabricEventEmitter.class.getClass();
BatchMountItem.class.getClass();
CreateMountItem.class.getClass();
DispatchCommandMountItem.class.getClass();
DispatchIntCommandMountItem.class.getClass();
DispatchStringCommandMountItem.class.getClass();
InsertMountItem.class.getClass();
MountItem.class.getClass();
PreAllocateViewMountItem.class.getClass();
RemoveDeleteMultiMountItem.class.getClass();
SendAccessibilityEvent.class.getClass();
UpdateEventEmitterMountItem.class.getClass();
UpdateLayoutMountItem.class.getClass();
UpdatePaddingMountItem.class.getClass();
UpdatePropsMountItem.class.getClass();
UpdateStateMountItem.class.getClass();
LayoutMetricsConversions.class.getClass();
MountingManager.class.getClass();
Binding.class.getClass();
@@ -134,5 +117,6 @@ public class FabricJSIModuleProvider implements JSIModuleProvider<UIManager> {
StateWrapperImpl.class.getClass();
BatchEventDispatchedListener.class.getClass();
ReactNativeConfig.class.getClass();
IntBufferBatchMountItem.class.getClass();
}
}
@@ -57,22 +57,13 @@ import com.facebook.react.fabric.events.EventBeatManager;
import com.facebook.react.fabric.events.EventEmitterWrapper;
import com.facebook.react.fabric.events.FabricEventEmitter;
import com.facebook.react.fabric.mounting.MountingManager;
import com.facebook.react.fabric.mounting.mountitems.BatchMountItem;
import com.facebook.react.fabric.mounting.mountitems.CreateMountItem;
import com.facebook.react.fabric.mounting.mountitems.DispatchCommandMountItem;
import com.facebook.react.fabric.mounting.mountitems.DispatchIntCommandMountItem;
import com.facebook.react.fabric.mounting.mountitems.DispatchStringCommandMountItem;
import com.facebook.react.fabric.mounting.mountitems.InsertMountItem;
import com.facebook.react.fabric.mounting.mountitems.IntBufferBatchMountItem;
import com.facebook.react.fabric.mounting.mountitems.MountItem;
import com.facebook.react.fabric.mounting.mountitems.PreAllocateViewMountItem;
import com.facebook.react.fabric.mounting.mountitems.RemoveDeleteMultiMountItem;
import com.facebook.react.fabric.mounting.mountitems.SendAccessibilityEvent;
import com.facebook.react.fabric.mounting.mountitems.UpdateEventEmitterMountItem;
import com.facebook.react.fabric.mounting.mountitems.UpdateLayoutMountItem;
import com.facebook.react.fabric.mounting.mountitems.UpdatePaddingMountItem;
import com.facebook.react.fabric.mounting.mountitems.UpdatePropsMountItem;
import com.facebook.react.fabric.mounting.mountitems.UpdateStateMountItem;
import com.facebook.react.modules.core.ReactChoreographer;
import com.facebook.react.modules.i18nmanager.I18nUtil;
import com.facebook.react.uimanager.PixelUtil;
@@ -352,100 +343,6 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
isLayoutable));
}
@DoNotStrip
@SuppressWarnings("unused")
@AnyThread
@ThreadConfined(ANY)
private MountItem createMountItem(
String componentName,
@Nullable ReadableMap props,
@Nullable Object stateWrapper,
int reactRootTag,
int reactTag,
boolean isLayoutable) {
String component = getFabricComponentName(componentName);
// This could be null if teardown/navigation away from a surface on the main thread happens
// while a commit is being processed in a different thread. By contract we expect this to be
// possible at teardown, but this race should *never* happen at startup.
@Nullable ThemedReactContext reactContext = mReactContextForRootTag.get(reactRootTag);
return new CreateMountItem(
reactContext,
reactRootTag,
reactTag,
component,
props,
(StateWrapper) stateWrapper,
isLayoutable);
}
@DoNotStrip
@SuppressWarnings("unused")
@AnyThread
@ThreadConfined(ANY)
private MountItem insertMountItem(int reactTag, int parentReactTag, int index) {
return new InsertMountItem(reactTag, parentReactTag, index);
}
@DoNotStrip
@SuppressWarnings("unused")
@AnyThread
@ThreadConfined(ANY)
private MountItem removeDeleteMultiMountItem(int[] metadata) {
return new RemoveDeleteMultiMountItem(metadata);
}
@DoNotStrip
@SuppressWarnings("unused")
@AnyThread
@ThreadConfined(ANY)
private MountItem updateLayoutMountItem(
int reactTag, int x, int y, int width, int height, int layoutDirection) {
return new UpdateLayoutMountItem(reactTag, x, y, width, height, layoutDirection);
}
@DoNotStrip
@SuppressWarnings("unused")
@AnyThread
@ThreadConfined(ANY)
private MountItem updatePaddingMountItem(int reactTag, int left, int top, int right, int bottom) {
return new UpdatePaddingMountItem(reactTag, left, top, right, bottom);
}
@DoNotStrip
@SuppressWarnings("unused")
@AnyThread
@ThreadConfined(ANY)
private MountItem updatePropsMountItem(int reactTag, ReadableMap map) {
return new UpdatePropsMountItem(reactTag, map);
}
@DoNotStrip
@SuppressWarnings("unused")
@AnyThread
@ThreadConfined(ANY)
private MountItem updateStateMountItem(int reactTag, @Nullable Object stateWrapper) {
return new UpdateStateMountItem(reactTag, (StateWrapper) stateWrapper);
}
@DoNotStrip
@SuppressWarnings("unused")
@AnyThread
@ThreadConfined(ANY)
private MountItem updateEventEmitterMountItem(int reactTag, Object eventEmitter) {
return new UpdateEventEmitterMountItem(reactTag, (EventEmitterWrapper) eventEmitter);
}
@DoNotStrip
@SuppressWarnings("unused")
@AnyThread
@ThreadConfined(ANY)
private MountItem createBatchMountItem(
int rootTag, MountItem[] items, int size, int commitNumber) {
return new BatchMountItem(rootTag, items, size, commitNumber);
}
@DoNotStrip
@SuppressWarnings("unused")
@AnyThread
@@ -599,7 +496,7 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
@Override
public void execute(@NonNull MountingManager mountingManager) {
try {
updatePropsMountItem(reactTag, props).execute(mountingManager);
mountingManager.updateProps(reactTag, props);
} catch (Exception ex) {
// TODO T42943890: Fix animations in Fabric and remove this try/catch
ReactSoftException.logSoftException(
@@ -664,12 +561,9 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
// When Binding.cpp calls scheduleMountItems during a commit phase, it always calls with
// a BatchMountItem. No other sites call into this with a BatchMountItem, and Binding.cpp only
// calls scheduleMountItems with a BatchMountItem.
boolean isClassicBatchMountItem = mountItem instanceof BatchMountItem;
boolean isIntBufferMountItem = mountItem instanceof IntBufferBatchMountItem;
boolean isBatchMountItem = isClassicBatchMountItem || isIntBufferMountItem;
boolean isBatchMountItem = mountItem instanceof IntBufferBatchMountItem;
boolean shouldSchedule =
(isClassicBatchMountItem && ((BatchMountItem) mountItem).shouldSchedule())
|| (isIntBufferMountItem && ((IntBufferBatchMountItem) mountItem).shouldSchedule())
(isBatchMountItem && ((IntBufferBatchMountItem) mountItem).shouldSchedule())
|| (!isBatchMountItem && mountItem != null);
// In case of sync rendering, this could be called on the UI thread. Otherwise,
@@ -939,16 +833,6 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
}
try {
// Make sure surface associated with this MountItem has been started, and not stopped.
// TODO T68118357: clean up this logic and simplify this method overall
if (mountItem instanceof BatchMountItem) {
BatchMountItem batchMountItem = (BatchMountItem) mountItem;
if (!isSurfaceActiveForExecution(
batchMountItem.getRootTag(), "dispatchMountItems BatchMountItem")) {
continue;
}
}
// Make sure surface associated with this MountItem has been started, and not stopped.
// TODO T68118357: clean up this logic and simplify this method overall
if (mountItem instanceof IntBufferBatchMountItem) {
@@ -437,9 +437,6 @@ void Binding::installFabricUIManager(
// Keep reference to config object and cache some feature flags here
reactNativeConfig_ = config;
useIntBufferBatchMountItem_ = reactNativeConfig_->getBool(
"react_fabric:use_int_buffer_batch_mountitem_android");
disablePreallocateViews_ = reactNativeConfig_->getBool(
"react_fabric:disabled_view_preallocation_android");
@@ -506,226 +503,7 @@ local_ref<JString> getPlatformComponentName(const ShadowView &shadowView) {
return componentName;
}
local_ref<JMountItem::javaobject> createUpdateEventEmitterMountItem(
const jni::global_ref<jobject> &javaUIManager,
const ShadowViewMutation &mutation) {
if (!mutation.newChildShadowView.eventEmitter) {
return nullptr;
}
SharedEventEmitter eventEmitter = mutation.newChildShadowView.eventEmitter;
// Do not hold a reference to javaEventEmitter from the C++ side.
auto javaEventEmitter = EventEmitterWrapper::newObjectJavaArgs();
EventEmitterWrapper *cEventEmitter = cthis(javaEventEmitter);
cEventEmitter->eventEmitter = eventEmitter;
static auto updateEventEmitterInstruction =
jni::findClassStatic(Binding::UIManagerJavaDescriptor)
->getMethod<alias_ref<JMountItem>(jint, jobject)>(
"updateEventEmitterMountItem");
return updateEventEmitterInstruction(
javaUIManager, mutation.newChildShadowView.tag, javaEventEmitter.get());
}
local_ref<JMountItem::javaobject> createUpdatePropsMountItem(
const jni::global_ref<jobject> &javaUIManager,
const ShadowViewMutation &mutation) {
auto shadowView = mutation.newChildShadowView;
// TODO: move props from map to a typed object.
auto newProps = shadowView.props->rawProps;
local_ref<ReadableMap::javaobject> readableMap =
castReadableMap(ReadableNativeMap::newObjectCxxArgs(newProps));
static auto updatePropsInstruction =
jni::findClassStatic(Binding::UIManagerJavaDescriptor)
->getMethod<alias_ref<JMountItem>(jint, ReadableMap::javaobject)>(
"updatePropsMountItem");
return updatePropsInstruction(
javaUIManager, mutation.newChildShadowView.tag, readableMap.get());
}
local_ref<JMountItem::javaobject> createUpdateLayoutMountItem(
const jni::global_ref<jobject> &javaUIManager,
const ShadowViewMutation &mutation) {
auto oldChildShadowView = mutation.oldChildShadowView;
auto newChildShadowView = mutation.newChildShadowView;
if (newChildShadowView.layoutMetrics != EmptyLayoutMetrics &&
oldChildShadowView.layoutMetrics != newChildShadowView.layoutMetrics) {
static auto updateLayoutInstruction =
jni::findClassStatic(Binding::UIManagerJavaDescriptor)
->getMethod<alias_ref<JMountItem>(
jint, jint, jint, jint, jint, jint)>("updateLayoutMountItem");
auto layoutMetrics = newChildShadowView.layoutMetrics;
auto pointScaleFactor = layoutMetrics.pointScaleFactor;
auto frame = layoutMetrics.frame;
int x = round(frame.origin.x * pointScaleFactor);
int y = round(frame.origin.y * pointScaleFactor);
int w = round(frame.size.width * pointScaleFactor);
int h = round(frame.size.height * pointScaleFactor);
auto layoutDirection =
toInt(newChildShadowView.layoutMetrics.layoutDirection);
return updateLayoutInstruction(
javaUIManager, newChildShadowView.tag, x, y, w, h, layoutDirection);
}
return nullptr;
}
local_ref<JMountItem::javaobject> createUpdatePaddingMountItem(
const jni::global_ref<jobject> &javaUIManager,
const ShadowViewMutation &mutation) {
auto oldChildShadowView = mutation.oldChildShadowView;
auto newChildShadowView = mutation.newChildShadowView;
if (oldChildShadowView.layoutMetrics.contentInsets ==
newChildShadowView.layoutMetrics.contentInsets &&
mutation.type != ShadowViewMutation::Type::Insert) {
return nullptr;
}
static auto updatePaddingInstruction =
jni::findClassStatic(Binding::UIManagerJavaDescriptor)
->getMethod<alias_ref<JMountItem>(jint, jint, jint, jint, jint)>(
"updatePaddingMountItem");
auto layoutMetrics = newChildShadowView.layoutMetrics;
auto pointScaleFactor = layoutMetrics.pointScaleFactor;
auto contentInsets = layoutMetrics.contentInsets;
int left = floor(contentInsets.left * pointScaleFactor);
int top = floor(contentInsets.top * pointScaleFactor);
int right = floor(contentInsets.right * pointScaleFactor);
int bottom = floor(contentInsets.bottom * pointScaleFactor);
return updatePaddingInstruction(
javaUIManager, newChildShadowView.tag, left, top, right, bottom);
}
local_ref<JMountItem::javaobject> createInsertMountItem(
const jni::global_ref<jobject> &javaUIManager,
const ShadowViewMutation &mutation) {
static auto insertInstruction =
jni::findClassStatic(Binding::UIManagerJavaDescriptor)
->getMethod<alias_ref<JMountItem>(jint, jint, jint)>(
"insertMountItem");
return insertInstruction(
javaUIManager,
mutation.newChildShadowView.tag,
mutation.parentShadowView.tag,
mutation.index);
}
local_ref<JMountItem::javaobject> createUpdateStateMountItem(
const jni::global_ref<jobject> &javaUIManager,
const ShadowViewMutation &mutation) {
static auto updateStateInstruction =
jni::findClassStatic(Binding::UIManagerJavaDescriptor)
->getMethod<alias_ref<JMountItem>(jint, jobject)>(
"updateStateMountItem");
auto state = mutation.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<StateWrapperImpl::JavaPart> javaStateWrapper = nullptr;
if (state != nullptr) {
javaStateWrapper = StateWrapperImpl::newObjectJavaArgs();
StateWrapperImpl *cStateWrapper = cthis(javaStateWrapper);
cStateWrapper->state_ = state;
}
return updateStateInstruction(
javaUIManager,
mutation.newChildShadowView.tag,
(javaStateWrapper != nullptr ? javaStateWrapper.get() : nullptr));
}
local_ref<JMountItem::javaobject> createRemoveAndDeleteMultiMountItem(
const jni::global_ref<jobject> &javaUIManager,
const std::vector<RemoveDeleteMetadata> &metadata) {
auto env = Environment::current();
auto removeAndDeleteArray = env->NewIntArray(metadata.size() * 4);
int position = 0;
jint temp[4];
for (const auto &x : metadata) {
temp[0] = x.tag;
temp[1] = x.parentTag;
temp[2] = x.index;
temp[3] = (x.shouldRemove ? 1 : 0) | (x.shouldDelete ? 2 : 0);
env->SetIntArrayRegion(removeAndDeleteArray, position, 4, temp);
position += 4;
}
static auto removeDeleteMultiInstruction =
jni::findClassStatic(Binding::UIManagerJavaDescriptor)
->getMethod<alias_ref<JMountItem>(jintArray)>(
"removeDeleteMultiMountItem");
auto ret = removeDeleteMultiInstruction(javaUIManager, removeAndDeleteArray);
// It is not strictly necessary to manually delete the ref here, in this
// particular case. If JNI memory is being allocated in a loop, it's easy to
// overload the localref table and crash; this is not possible in this case
// since the JNI would automatically clear this ref when it goes out of scope,
// anyway. However, this is being left here as a reminder of good hygiene and
// to be careful with JNI-allocated memory in general.
env->DeleteLocalRef(removeAndDeleteArray);
return ret;
}
// TODO T48019320: because we pass initial props and state to the Create (and
// preallocate) mount instruction, we technically don't need to pass the first
// Update to any components. Dedupe?
local_ref<JMountItem::javaobject> createCreateMountItem(
const jni::global_ref<jobject> &javaUIManager,
const ShadowViewMutation &mutation,
const Tag surfaceId) {
static auto createJavaInstruction =
jni::findClassStatic(Binding::UIManagerJavaDescriptor)
->getMethod<alias_ref<JMountItem>(
jstring, ReadableMap::javaobject, jobject, jint, jint, jboolean)>(
"createMountItem");
auto newChildShadowView = mutation.newChildShadowView;
local_ref<JString> componentName =
getPlatformComponentName(newChildShadowView);
jboolean isLayoutable =
newChildShadowView.layoutMetrics != EmptyLayoutMetrics;
local_ref<ReadableMap::javaobject> props = castReadableMap(
ReadableNativeMap::newObjectCxxArgs(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<StateWrapperImpl::JavaPart> javaStateWrapper = nullptr;
if (newChildShadowView.state != nullptr) {
javaStateWrapper = StateWrapperImpl::newObjectJavaArgs();
StateWrapperImpl *cStateWrapper = cthis(javaStateWrapper);
cStateWrapper->state_ = newChildShadowView.state;
}
return createJavaInstruction(
javaUIManager,
componentName.get(),
props.get(),
(javaStateWrapper != nullptr ? javaStateWrapper.get() : nullptr),
surfaceId,
newChildShadowView.tag,
isLayoutable);
}
void Binding::schedulerDidFinishTransactionIntBuffer(
void Binding::schedulerDidFinishTransaction(
MountingCoordinator::Shared const &mountingCoordinator) {
std::lock_guard<std::recursive_mutex> lock(commitMutex_);
@@ -1183,251 +961,6 @@ void Binding::schedulerDidFinishTransactionIntBuffer(
env->DeleteLocalRef(intBufferArray);
}
void Binding::schedulerDidFinishTransaction(
MountingCoordinator::Shared const &mountingCoordinator) {
std::lock_guard<std::recursive_mutex> lock(commitMutex_);
if (useIntBufferBatchMountItem_) {
return schedulerDidFinishTransactionIntBuffer(mountingCoordinator);
}
SystraceSection s("FabricUIManagerBinding::schedulerDidFinishTransaction");
auto finishTransactionStartTime = telemetryTimePointNow();
jni::global_ref<jobject> localJavaUIManager = getJavaUIManager();
if (!localJavaUIManager) {
LOG(ERROR)
<< "Binding::schedulerDidFinishTransaction: JavaUIManager disappeared";
return;
}
auto mountingTransaction = mountingCoordinator->pullTransaction();
if (!mountingTransaction.has_value()) {
return;
}
auto telemetry = mountingTransaction->getTelemetry();
auto surfaceId = mountingTransaction->getSurfaceId();
auto &mutations = mountingTransaction->getMutations();
auto revisionNumber = telemetry.getRevisionNumber();
std::vector<local_ref<jobject>> queue;
// Upper bound estimation of mount items to be delivered to Java side.
int size = mutations.size() * 3 + 42;
local_ref<JArrayClass<JMountItem::javaobject>> mountItemsArray =
JArrayClass<JMountItem::javaobject>::newArray(size);
auto mountItems = *(mountItemsArray);
std::unordered_set<Tag> deletedViewTags;
// Find the set of tags that are removed and deleted in one block
std::vector<RemoveDeleteMetadata> toRemove;
int position = 0;
for (const auto &mutation : mutations) {
auto oldChildShadowView = mutation.oldChildShadowView;
auto newChildShadowView = mutation.newChildShadowView;
bool isVirtual = newChildShadowView.layoutMetrics == EmptyLayoutMetrics &&
oldChildShadowView.layoutMetrics == EmptyLayoutMetrics;
// Handle accumulated removals/deletions
if (mutation.type != ShadowViewMutation::Remove &&
mutation.type != ShadowViewMutation::Delete) {
if (toRemove.size() > 0) {
mountItems[position++] =
createRemoveAndDeleteMultiMountItem(localJavaUIManager, toRemove);
toRemove.clear();
}
}
switch (mutation.type) {
case ShadowViewMutation::Create: {
if (disablePreallocateViews_ ||
mutation.newChildShadowView.props->revision > 1 ||
deletedViewTags.find(mutation.newChildShadowView.tag) !=
deletedViewTags.end()) {
mountItems[position++] =
createCreateMountItem(localJavaUIManager, mutation, surfaceId);
}
break;
}
case ShadowViewMutation::Remove: {
if (!isVirtual) {
toRemove.push_back(
RemoveDeleteMetadata{mutation.oldChildShadowView.tag,
mutation.parentShadowView.tag,
mutation.index,
true,
false});
}
break;
}
case ShadowViewMutation::Delete: {
// It is impossible to delete without removing node first
const auto &it = std::find_if(
std::begin(toRemove),
std::end(toRemove),
[&mutation](const auto &x) {
return x.tag == mutation.oldChildShadowView.tag;
});
if (it != std::end(toRemove)) {
it->shouldDelete = true;
} else {
toRemove.push_back(RemoveDeleteMetadata{
mutation.oldChildShadowView.tag, -1, -1, false, true});
}
deletedViewTags.insert(mutation.oldChildShadowView.tag);
break;
}
case ShadowViewMutation::Update: {
if (!isVirtual) {
if (mutation.oldChildShadowView.props !=
mutation.newChildShadowView.props) {
mountItems[position++] =
createUpdatePropsMountItem(localJavaUIManager, mutation);
}
if (mutation.oldChildShadowView.state !=
mutation.newChildShadowView.state) {
mountItems[position++] =
createUpdateStateMountItem(localJavaUIManager, mutation);
}
// 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.
auto updatePaddingMountItem =
createUpdatePaddingMountItem(localJavaUIManager, mutation);
if (updatePaddingMountItem) {
mountItems[position++] = updatePaddingMountItem;
}
auto updateLayoutMountItem =
createUpdateLayoutMountItem(localJavaUIManager, mutation);
if (updateLayoutMountItem) {
mountItems[position++] = updateLayoutMountItem;
}
}
if (mutation.oldChildShadowView.eventEmitter !=
mutation.newChildShadowView.eventEmitter) {
auto updateEventEmitterMountItem =
createUpdateEventEmitterMountItem(localJavaUIManager, mutation);
if (updateEventEmitterMountItem) {
mountItems[position++] = updateEventEmitterMountItem;
}
}
break;
}
case ShadowViewMutation::Insert: {
if (!isVirtual) {
// Insert item
mountItems[position++] =
createInsertMountItem(localJavaUIManager, mutation);
if (disablePreallocateViews_ ||
mutation.newChildShadowView.props->revision > 1 ||
deletedViewTags.find(mutation.newChildShadowView.tag) !=
deletedViewTags.end()) {
mountItems[position++] =
createUpdatePropsMountItem(localJavaUIManager, mutation);
}
// State
if (mutation.newChildShadowView.state) {
mountItems[position++] =
createUpdateStateMountItem(localJavaUIManager, mutation);
}
// 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.
auto updatePaddingMountItem =
createUpdatePaddingMountItem(localJavaUIManager, mutation);
if (updatePaddingMountItem) {
mountItems[position++] = updatePaddingMountItem;
}
// Layout
auto updateLayoutMountItem =
createUpdateLayoutMountItem(localJavaUIManager, mutation);
if (updateLayoutMountItem) {
mountItems[position++] = updateLayoutMountItem;
}
}
// EventEmitter
auto updateEventEmitterMountItem =
createUpdateEventEmitterMountItem(localJavaUIManager, mutation);
if (updateEventEmitterMountItem) {
mountItems[position++] = updateEventEmitterMountItem;
}
break;
}
default: {
break;
}
}
}
// Handle remaining removals and deletions
if (toRemove.size() > 0) {
mountItems[position++] =
createRemoveAndDeleteMultiMountItem(localJavaUIManager, toRemove);
toRemove.clear();
}
static auto createMountItemsBatchContainer =
jni::findClassStatic(Binding::UIManagerJavaDescriptor)
->getMethod<alias_ref<JMountItem>(
jint, jtypeArray<JMountItem::javaobject>, jint, jint)>(
"createBatchMountItem");
// If there are no items, we pass a nullptr instead of passing the object
// through the JNI
auto batch = createMountItemsBatchContainer(
localJavaUIManager,
surfaceId,
position == 0 ? nullptr : mountItemsArray.get(),
position,
revisionNumber);
static auto scheduleMountItem =
jni::findClassStatic(Binding::UIManagerJavaDescriptor)
->getMethod<void(
JMountItem::javaobject,
jint,
jlong,
jlong,
jlong,
jlong,
jlong,
jlong,
jlong)>("scheduleMountItem");
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));
}
void Binding::setPixelDensity(float pointScaleFactor) {
pointScaleFactor_ = pointScaleFactor;
}
@@ -169,14 +169,9 @@ class Binding : public jni::HybridClass<Binding>,
float pointScaleFactor_ = 1;
std::shared_ptr<const ReactNativeConfig> reactNativeConfig_{nullptr};
bool useIntBufferBatchMountItem_{false};
bool disablePreallocateViews_{false};
bool disableVirtualNodePreallocation_{false};
bool enableFabricLogs_{false};
private:
void schedulerDidFinishTransactionIntBuffer(
MountingCoordinator::Shared const &mountingCoordinator);
};
} // namespace react
@@ -1,117 +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.
*/
package com.facebook.react.fabric.mounting.mountitems;
import androidx.annotation.NonNull;
import com.facebook.common.logging.FLog;
import com.facebook.proguard.annotations.DoNotStrip;
import com.facebook.react.bridge.ReactMarker;
import com.facebook.react.bridge.ReactMarkerConstants;
import com.facebook.react.fabric.mounting.MountingManager;
import com.facebook.systrace.Systrace;
/**
* This class represents a batch of {@link MountItem}s
*
* <p>A MountItem batch contains an array of {@link MountItem} and a size. The size determines the
* amount of items that needs to be processed in the array.
*
* <p>The purpose of encapsulating the array of MountItems this way, is to reduce the amount of
* allocations in C++
*/
@DoNotStrip
public class BatchMountItem implements MountItem {
static final String TAG = "FabricBatchMountItem";
private final int mRootTag;
@NonNull private final MountItem[] mMountItems;
private final int mSize;
private final int mCommitNumber;
public BatchMountItem(int rootTag, MountItem[] items, int size, int commitNumber) {
int itemsLength = (items == null ? 0 : items.length);
if (size < 0 || size > itemsLength) {
throw new IllegalArgumentException(
"Invalid size received by parameter size: " + size + " items.size = " + itemsLength);
}
mRootTag = rootTag;
mMountItems = items;
mSize = size;
mCommitNumber = commitNumber;
}
private void beginMarkers(String reason) {
Systrace.beginSection(
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
"FabricUIManager::" + reason + " - " + mSize + " items");
if (mCommitNumber > 0) {
ReactMarker.logFabricMarker(
ReactMarkerConstants.FABRIC_BATCH_EXECUTION_START, null, mCommitNumber);
}
}
private void endMarkers() {
if (mCommitNumber > 0) {
ReactMarker.logFabricMarker(
ReactMarkerConstants.FABRIC_BATCH_EXECUTION_END, null, mCommitNumber);
}
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
@Override
public void execute(@NonNull MountingManager mountingManager) {
beginMarkers("mountViews");
int mountItemIndex = 0;
try {
for (; mountItemIndex < mSize; mountItemIndex++) {
mMountItems[mountItemIndex].execute(mountingManager);
}
} catch (RuntimeException e) {
FLog.e(
TAG,
"Caught exception executing mountItem @"
+ mountItemIndex
+ ": "
+ mMountItems[mountItemIndex].toString(),
e);
throw e;
}
endMarkers();
}
public int getRootTag() {
return mRootTag;
}
public boolean shouldSchedule() {
return mSize != 0;
}
@Override
public String toString() {
StringBuilder s = new StringBuilder();
for (int i = 0; i < mSize; i++) {
if (s.length() > 0) {
s.append("\n");
}
s.append("BatchMountItem [S:" + mRootTag + "] (")
.append(i + 1)
.append("/")
.append(mSize)
.append("): ")
.append(mMountItems[i]);
}
return s.toString();
}
}
@@ -1,68 +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.
*/
package com.facebook.react.fabric.mounting.mountitems;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.fabric.mounting.MountingManager;
import com.facebook.react.uimanager.StateWrapper;
import com.facebook.react.uimanager.ThemedReactContext;
public class CreateMountItem implements MountItem {
@NonNull private final String mComponent;
private final int mRootTag;
private final int mReactTag;
@NonNull private final ThemedReactContext mContext;
private final @Nullable ReadableMap mProps;
private final @Nullable StateWrapper mStateWrapper;
private final boolean mIsLayoutable;
public CreateMountItem(
@Nullable ThemedReactContext context,
int rootTag,
int reactTag,
@NonNull String component,
@Nullable ReadableMap props,
@NonNull StateWrapper stateWrapper,
boolean isLayoutable) {
mContext = context;
mComponent = component;
mRootTag = rootTag;
mReactTag = reactTag;
mProps = props;
mStateWrapper = stateWrapper;
mIsLayoutable = isLayoutable;
}
@Override
public void execute(@NonNull MountingManager mountingManager) {
if (mContext == null) {
throw new IllegalStateException(
"Cannot execute PreAllocateViewMountItem without Context for ReactTag: "
+ mReactTag
+ " and rootTag: "
+ mRootTag);
}
mountingManager.createView(
mContext, mComponent, mReactTag, mProps, mStateWrapper, mIsLayoutable);
}
@Override
public String toString() {
return "CreateMountItem ["
+ mReactTag
+ "] - component: "
+ mComponent
+ " - rootTag: "
+ mRootTag
+ " - isLayoutable: "
+ mIsLayoutable;
}
}
@@ -1,47 +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.
*/
package com.facebook.react.fabric.mounting.mountitems;
import androidx.annotation.NonNull;
import com.facebook.react.fabric.mounting.MountingManager;
public class InsertMountItem implements MountItem {
private int mReactTag;
private int mParentReactTag;
private int mIndex;
public InsertMountItem(int reactTag, int parentReactTag, int index) {
mReactTag = reactTag;
mParentReactTag = parentReactTag;
mIndex = index;
}
@Override
public void execute(@NonNull MountingManager mountingManager) {
mountingManager.addViewAt(mParentReactTag, mReactTag, mIndex);
}
public int getParentReactTag() {
return mParentReactTag;
}
public int getIndex() {
return mIndex;
}
@Override
public String toString() {
return "InsertMountItem ["
+ mReactTag
+ "] - parentTag: ["
+ mParentReactTag
+ "] - index: "
+ mIndex;
}
}
@@ -1,90 +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.
*/
package com.facebook.react.fabric.mounting.mountitems;
import androidx.annotation.NonNull;
import com.facebook.react.fabric.mounting.MountingManager;
public class RemoveDeleteMultiMountItem implements MountItem {
// Metadata is an array of ints, grouped into 4 ints per instruction (so the length of metadata
// is always divisible by 4):
//
// `instruction*4 + 0`: react tag of view instruction
// `instruction*4 + 1`: react tag of view's parent
// `instruction*4 + 2`: index of view in parents' children instruction
// `instruction*4 + 3`: flags indicating if the view should be removed, and/or deleted
@NonNull private int[] mMetadata;
// Bitfields of "flag", indicating if a view should be removed and/or deleted
private static final int REMOVE_FLAG = 1;
private static final int DELETE_FLAG = 2;
// Indices for each parameter within an "instruction"
private static final int INSTRUCTION_FIELDS_LEN = 4;
private static final int TAG_INDEX = 0;
private static final int PARENT_TAG_INDEX = 1;
private static final int VIEW_INDEX_INDEX = 2;
private static final int FLAGS_INDEX = 3;
public RemoveDeleteMultiMountItem(@NonNull int[] metadata) {
mMetadata = metadata;
}
@Override
public void execute(@NonNull MountingManager mountingManager) {
// First, go through instructions and remove all views that are marked
// for removal.
// Not all views that are removed are deleted, and not all deleted views
// are removed first.
// *All* views must be removed here before we can delete any views.
// Removal of a view from a parent is based on indices within the parents' children,
// and deletion causes reordering; so we must perform all removals first.
for (int i = 0; i < mMetadata.length; i += INSTRUCTION_FIELDS_LEN) {
int flags = mMetadata[i + FLAGS_INDEX];
if ((flags & REMOVE_FLAG) != 0) {
int parentTag = mMetadata[i + PARENT_TAG_INDEX];
int tag = mMetadata[i + TAG_INDEX];
int index = mMetadata[i + VIEW_INDEX_INDEX];
mountingManager.removeViewAt(tag, parentTag, index);
}
}
// After removing all views, delete all views marked for deletion.
for (int i = 0; i < mMetadata.length; i += 4) {
int flags = mMetadata[i + FLAGS_INDEX];
if ((flags & DELETE_FLAG) != 0) {
int tag = mMetadata[i + TAG_INDEX];
mountingManager.deleteView(tag);
}
}
}
@Override
public String toString() {
StringBuilder s = new StringBuilder();
for (int i = 0; i < mMetadata.length; i += 4) {
if (s.length() > 0) {
s.append("\n");
}
s.append("RemoveDeleteMultiMountItem (")
.append(i / INSTRUCTION_FIELDS_LEN + 1)
.append("/")
.append(mMetadata.length / INSTRUCTION_FIELDS_LEN)
.append("): [")
.append(mMetadata[i + TAG_INDEX])
.append("] parent [")
.append(mMetadata[i + PARENT_TAG_INDEX])
.append("] idx ")
.append(mMetadata[i + VIEW_INDEX_INDEX])
.append(" ")
.append(mMetadata[i + FLAGS_INDEX]);
}
return s.toString();
}
}
@@ -1,33 +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.
*/
package com.facebook.react.fabric.mounting.mountitems;
import androidx.annotation.NonNull;
import com.facebook.react.fabric.events.EventEmitterWrapper;
import com.facebook.react.fabric.mounting.MountingManager;
public class UpdateEventEmitterMountItem implements MountItem {
@NonNull private final EventEmitterWrapper mEventHandler;
private final int mReactTag;
public UpdateEventEmitterMountItem(int reactTag, @NonNull EventEmitterWrapper EventHandler) {
mReactTag = reactTag;
mEventHandler = EventHandler;
}
@Override
public void execute(@NonNull MountingManager mountingManager) {
mountingManager.updateEventEmitter(mReactTag, mEventHandler);
}
@Override
public String toString() {
return "UpdateEventEmitterMountItem [" + mReactTag + "]";
}
}
@@ -1,90 +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.
*/
package com.facebook.react.fabric.mounting.mountitems;
import android.annotation.TargetApi;
import android.os.Build;
import android.util.LayoutDirection;
import androidx.annotation.NonNull;
import com.facebook.react.fabric.mounting.MountingManager;
public class UpdateLayoutMountItem implements MountItem {
private final int mReactTag;
private final int mX;
private final int mY;
private final int mWidth;
private final int mHeight;
private final int mLayoutDirection;
public UpdateLayoutMountItem(
int reactTag, int x, int y, int width, int height, int layoutDirection) {
mReactTag = reactTag;
mX = x;
mY = y;
mWidth = width;
mHeight = height;
mLayoutDirection = convertLayoutDirection(layoutDirection);
}
// TODO move this from here
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private static int convertLayoutDirection(int layoutDirection) {
switch (layoutDirection) {
case 0:
return LayoutDirection.INHERIT;
case 1:
return LayoutDirection.LTR;
case 2:
return LayoutDirection.RTL;
default:
throw new IllegalArgumentException("Unsupported layout direction: " + layoutDirection);
}
}
@Override
public void execute(@NonNull MountingManager mountingManager) {
mountingManager.updateLayout(mReactTag, mX, mY, mWidth, mHeight);
}
public int getX() {
return mX;
}
public int getY() {
return mY;
}
public int getHeight() {
return mHeight;
}
public int getWidth() {
return mWidth;
}
public int getLayoutDirection() {
return mLayoutDirection;
}
@Override
public String toString() {
return "UpdateLayoutMountItem ["
+ mReactTag
+ "] - x: "
+ mX
+ " - y: "
+ mY
+ " - height: "
+ mHeight
+ " - width: "
+ mWidth
+ " - layoutDirection: "
+ +mLayoutDirection;
}
}
@@ -1,53 +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.
*/
package com.facebook.react.fabric.mounting.mountitems;
import androidx.annotation.NonNull;
import com.facebook.react.fabric.mounting.MountingManager;
/**
* A MountItem that represents setting the padding properties of a view (left, top, right, bottom).
* This is distinct from layout because it happens far less frequently from layout; so it is a perf
* optimization to transfer this data and execute the methods strictly less than the layout-related
* properties.
*/
public class UpdatePaddingMountItem implements MountItem {
private final int mReactTag;
private final int mLeft;
private final int mTop;
private final int mRight;
private final int mBottom;
public UpdatePaddingMountItem(int reactTag, int left, int top, int right, int bottom) {
mReactTag = reactTag;
mLeft = left;
mTop = top;
mRight = right;
mBottom = bottom;
}
@Override
public void execute(@NonNull MountingManager mountingManager) {
mountingManager.updatePadding(mReactTag, mLeft, mTop, mRight, mBottom);
}
@Override
public String toString() {
return "UpdatePaddingMountItem ["
+ mReactTag
+ "] - left: "
+ mLeft
+ " - top: "
+ mTop
+ " - right: "
+ mRight
+ " - bottom: "
+ mBottom;
}
}
@@ -1,42 +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.
*/
package com.facebook.react.fabric.mounting.mountitems;
import static com.facebook.react.fabric.FabricUIManager.IS_DEVELOPMENT_ENVIRONMENT;
import androidx.annotation.NonNull;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.fabric.mounting.MountingManager;
public class UpdatePropsMountItem implements MountItem {
private final int mReactTag;
@NonNull private final ReadableMap mUpdatedProps;
public UpdatePropsMountItem(int reactTag, @NonNull ReadableMap updatedProps) {
mReactTag = reactTag;
mUpdatedProps = updatedProps;
}
@Override
public void execute(@NonNull MountingManager mountingManager) {
mountingManager.updateProps(mReactTag, mUpdatedProps);
}
@Override
public String toString() {
StringBuilder result =
new StringBuilder("UpdatePropsMountItem [").append(mReactTag).append("]");
if (IS_DEVELOPMENT_ENVIRONMENT) {
result.append(" props: ").append(mUpdatedProps);
}
return result.toString();
}
}
@@ -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.
*/
package com.facebook.react.fabric.mounting.mountitems;
import static com.facebook.react.fabric.FabricUIManager.IS_DEVELOPMENT_ENVIRONMENT;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.facebook.react.fabric.mounting.MountingManager;
import com.facebook.react.uimanager.StateWrapper;
public class UpdateStateMountItem implements MountItem {
private final int mReactTag;
@Nullable private final StateWrapper mStateWrapper;
public UpdateStateMountItem(int reactTag, @Nullable StateWrapper stateWrapper) {
mReactTag = reactTag;
mStateWrapper = stateWrapper;
}
@Override
public void execute(@NonNull MountingManager mountingManager) {
mountingManager.updateState(mReactTag, mStateWrapper);
}
@Override
public String toString() {
StringBuilder result =
new StringBuilder("UpdateStateMountItem [").append(mReactTag).append("]");
if (IS_DEVELOPMENT_ENVIRONMENT) {
result.append(" state: ").append(mStateWrapper != null ? mStateWrapper.getState() : "<null>");
}
return result.toString();
}
}