Skip to content

Commit

Permalink
Use mapbuffer for ReactViewGroup
Browse files Browse the repository at this point in the history
Summary:
Provides an alternative way of diffing props on Android side. Instead of passing dynamic JSON values from JS, the new approach diff C++ props and serializes difference into a `MapBuffer`. Current implementation does this only for `RCTView` component to test performance, correctness and memory impact (as MapBuffers are supposed to be more compact and performant).

This way of passing props allows for additional alignment between platforms, as C++ props are now source of truth for the view state, similar to iOS. At the same time, changing serialization method requires reimplementing `ViewManager` codegen (done manually for now) to account for `MapBuffer`s.

Changelog: [Added][Android] - Added an experimental prop serialization path based on MapBuffer

Reviewed By: mdvacca

Differential Revision: D33735245

fbshipit-source-id: 1515b5fe92f6557ae55abe215ce48277c83974a6
  • Loading branch information
Andrei Shikov authored and facebook-github-bot committed Feb 22, 2022
1 parent b1a7793 commit cbcdaae
Show file tree
Hide file tree
Showing 14 changed files with 777 additions and 68 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ public static JavaOnlyMap of(Object... keysAndValues) {
return new JavaOnlyMap(keysAndValues);
}

public static JavaOnlyMap from(Map<String, Object> map) {
return new JavaOnlyMap(map);
}

public static JavaOnlyMap deepClone(ReadableMap map) {
JavaOnlyMap res = new JavaOnlyMap();
ReadableMapKeySetIterator iter = map.keySetIterator();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,36 @@ public boolean equals(@Nullable Object obj) {
return thisByteBuffer.equals(otherByteBuffer);
}

@Override
public String toString() {
StringBuilder builder = new StringBuilder("{");
for (MapBufferEntry entry : this) {
int key = entry.getKey();
builder.append(key);
builder.append('=');
switch (entry.getType()) {
case BOOL:
builder.append(entry.getBoolean());
break;
case INT:
builder.append(entry.getInt());
break;
case DOUBLE:
builder.append(entry.getDouble());
break;
case STRING:
builder.append(entry.getString());
break;
case MAP:
builder.append(entry.getReadableMapBuffer().toString());
break;
}
builder.append(',');
}
builder.append('}');
return builder.toString();
}

/** @return an {@link Iterator<MapBufferEntry>} for the entries of this MapBuffer. */
@Override
public Iterator<MapBufferEntry> iterator() {
Expand Down Expand Up @@ -372,15 +402,15 @@ public boolean getBoolean() {
}

/** @return the String value that is stored in this {@link MapBufferEntry}. */
public @Nullable String getString() {
public String getString() {
assertType(DataType.STRING);
return readStringValue(mBucketOffset + VALUE_OFFSET);
}

/**
* @return the {@link ReadableMapBuffer} value that is stored in this {@link MapBufferEntry}.
*/
public @Nullable ReadableMapBuffer getReadableMapBuffer() {
public ReadableMapBuffer getReadableMapBuffer() {
assertType(DataType.MAP);
return readMapBufferValue(mBucketOffset + VALUE_OFFSET);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -704,7 +704,7 @@ private void preallocateView(
int rootTag,
int reactTag,
final String componentName,
@Nullable ReadableMap props,
@Nullable Object props,
@Nullable Object stateWrapper,
@Nullable Object eventEmitterWrapper,
boolean isLayoutable) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,11 @@ CppMountItem CppMountItem::RemoveMountItem(
int index) {
return {CppMountItem::Type::Remove, parentView, shadowView, {}, index};
}
CppMountItem CppMountItem::UpdatePropsMountItem(ShadowView const &shadowView) {
return {CppMountItem::Type::UpdateProps, {}, {}, shadowView, -1};
CppMountItem CppMountItem::UpdatePropsMountItem(
ShadowView const &oldShadowView,
ShadowView const &newShadowView) {
return {
CppMountItem::Type::UpdateProps, {}, oldShadowView, newShadowView, -1};
}
CppMountItem CppMountItem::UpdateStateMountItem(ShadowView const &shadowView) {
return {CppMountItem::Type::UpdateState, {}, {}, shadowView, -1};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ struct CppMountItem final {
ShadowView const &shadowView,
int index);

static CppMountItem UpdatePropsMountItem(ShadowView const &shadowView);
static CppMountItem UpdatePropsMountItem(
ShadowView const &oldShadowView,
ShadowView const &newShadowView);

static CppMountItem UpdateStateMountItem(ShadowView const &shadowView);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "FabricMountingManager.h"
#include "EventEmitterWrapper.h"
#include "StateWrapperImpl.h"
#include "viewPropConversions.h"

#include <react/jni/ReadableNativeMap.h>
#include <react/renderer/components/scrollview/ScrollViewProps.h>
Expand Down Expand Up @@ -177,11 +178,6 @@ static inline void writeIntBufferTypePreamble(
}
}

inline local_ref<ReadableMap::javaobject> castReadableMap(
local_ref<ReadableNativeMap::javaobject> const &nativeMap) {
return make_local(reinterpret_cast<ReadableMap::javaobject>(nativeMap.get()));
}

inline local_ref<ReadableArray::javaobject> castReadableArray(
local_ref<ReadableNativeArray::javaobject> const &nativeArray) {
return make_local(
Expand Down Expand Up @@ -222,6 +218,22 @@ static inline float scale(Float value, Float pointScaleFactor) {
return result;
}

local_ref<jobject> FabricMountingManager::getProps(
ShadowView const &oldShadowView,
ShadowView const &newShadowView) {
if (useMapBufferForViewProps_ &&
newShadowView.traits.check(ShadowNodeTraits::Trait::View)) {
auto oldProps = oldShadowView.props != nullptr
? static_cast<ViewProps const &>(*oldShadowView.props)
: ViewProps{};
auto newProps = static_cast<ViewProps const &>(*newShadowView.props);
return ReadableMapBuffer::createWithContents(
viewPropsDiff(oldProps, newProps));
} else {
return ReadableNativeMap::newObjectCxxArgs(newShadowView.props->rawProps);
}
}

void FabricMountingManager::executeMount(
MountingCoordinator::Shared const &mountingCoordinator) {
std::lock_guard<std::recursive_mutex> lock(commitMutex_);
Expand Down Expand Up @@ -308,7 +320,8 @@ void FabricMountingManager::executeMount(
if (!isVirtual) {
if (oldChildShadowView.props != newChildShadowView.props) {
cppUpdatePropsMountItems.push_back(
CppMountItem::UpdatePropsMountItem(newChildShadowView));
CppMountItem::UpdatePropsMountItem(
oldChildShadowView, newChildShadowView));
}
if (oldChildShadowView.state != newChildShadowView.state) {
cppUpdateStateMountItems.push_back(
Expand Down Expand Up @@ -367,7 +380,7 @@ void FabricMountingManager::executeMount(
shouldRememberAllocatedViews_ ? allocationCheck : revisionCheck;
if (shouldCreateView) {
cppUpdatePropsMountItems.push_back(
CppMountItem::UpdatePropsMountItem(newChildShadowView));
CppMountItem::UpdatePropsMountItem({}, newChildShadowView));
}

// State
Expand Down Expand Up @@ -484,7 +497,6 @@ void FabricMountingManager::executeMount(

// 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<JArrayClass<jobject>> objBufferArray =
JArrayClass<jobject>::newArray(batchMountItemObjectsSize);
Expand Down Expand Up @@ -525,10 +537,8 @@ void FabricMountingManager::executeMount(
int isLayoutable =
mountItem.newChildShadowView.layoutMetrics != EmptyLayoutMetrics ? 1
: 0;

local_ref<ReadableMap::javaobject> props =
castReadableMap(ReadableNativeMap::newObjectCxxArgs(
mountItem.newChildShadowView.props->rawProps));
local_ref<JObject> props =
getProps(mountItem.oldChildShadowView, mountItem.newChildShadowView);

// Do not hold onto Java object from C
// We DO want to hold onto C object from Java, since we don't know the
Expand Down Expand Up @@ -584,11 +594,8 @@ void FabricMountingManager::executeMount(
temp[0] = mountItem.newChildShadowView.tag;
env->SetIntArrayRegion(intBufferArray, intBufferPosition, 1, temp);
intBufferPosition += 1;

auto newProps = mountItem.newChildShadowView.props->rawProps;
local_ref<ReadableMap::javaobject> newPropsReadableMap =
castReadableMap(ReadableNativeMap::newObjectCxxArgs(newProps));
(*objBufferArray)[objBufferPosition++] = newPropsReadableMap.get();
(*objBufferArray)[objBufferPosition++] =
getProps(mountItem.oldChildShadowView, mountItem.newChildShadowView);
}
}
if (!cppUpdateStateMountItems.empty()) {
Expand Down Expand Up @@ -795,15 +802,11 @@ void FabricMountingManager::preallocateShadowView(

bool isLayoutableShadowNode = shadowView.layoutMetrics != EmptyLayoutMetrics;

static auto preallocateView = jni::findClassStatic(UIManagerJavaDescriptor)
->getMethod<void(
jint,
jint,
jstring,
ReadableMap::javaobject,
jobject,
jobject,
jboolean)>("preallocateView");
static auto preallocateView =
jni::findClassStatic(UIManagerJavaDescriptor)
->getMethod<void(
jint, jint, jstring, jobject, jobject, jobject, jboolean)>(
"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
Expand All @@ -826,8 +829,8 @@ void FabricMountingManager::preallocateShadowView(
}
}

local_ref<ReadableMap::javaobject> props = castReadableMap(
ReadableNativeMap::newObjectCxxArgs(shadowView.props->rawProps));
local_ref<JObject> props = getProps({}, shadowView);

auto component = getPlatformComponentName(shadowView);

preallocateView(
Expand Down Expand Up @@ -942,6 +945,8 @@ FabricMountingManager::FabricMountingManager(
useOverflowInset_ = doesUseOverflowInset();
shouldRememberAllocatedViews_ = config->getBool(
"react_native_new_architecture:remember_views_on_mount_android");
useMapBufferForViewProps_ = config->getBool(
"react_native_new_architecture:use_mapbuffer_for_viewprops");
}

} // namespace react
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ class FabricMountingManager {
bool disableRevisionCheckForPreallocation_{false};
bool useOverflowInset_{false};
bool shouldRememberAllocatedViews_{false};
bool useMapBufferForViewProps_{false};

jni::local_ref<jobject> getProps(
ShadowView const &oldShadowView,
ShadowView const &newShadowView);
};

} // namespace react
Expand Down
Loading

0 comments on commit cbcdaae

Please sign in to comment.