Skip to content

Commit

Permalink
Batch update props JSI calls (#4503)
Browse files Browse the repository at this point in the history
<!-- Thanks for submitting a pull request! We appreciate you spending
the time to work on these changes. Please follow the template so that
the reviewers can easily understand what the code changes affect. -->

## Summary

Fixes #4495.

<!-- Explain the motivation for this PR. Include "Fixes #<number>" if
applicable. -->

## Test plan

<!-- Provide a minimal but complete code snippet that can be used to
test out this change along with instructions how to run it and a
description of the expected behavior. -->
  • Loading branch information
tomekzaw authored Jun 21, 2023
1 parent 74e5f1a commit 5fe5d0e
Show file tree
Hide file tree
Showing 12 changed files with 241 additions and 91 deletions.
45 changes: 24 additions & 21 deletions Common/cpp/NativeModules/NativeReanimatedModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,8 @@ NativeReanimatedModule::NativeReanimatedModule(
};

#ifdef RCT_NEW_ARCH_ENABLED
auto updateProps = [this](
jsi::Runtime &rt,
const jsi::Value &shadowNodeValue,
const jsi::Value &props) {
this->updateProps(rt, shadowNodeValue, props);
auto updateProps = [this](jsi::Runtime &rt, const jsi::Value &operations) {
this->updateProps(rt, operations);
};

auto removeShadowNodeFromRegistry =
Expand Down Expand Up @@ -458,8 +455,8 @@ void NativeReanimatedModule::cleanupSensors() {
#ifdef RCT_NEW_ARCH_ENABLED
bool NativeReanimatedModule::isThereAnyLayoutProp(
jsi::Runtime &rt,
const jsi::Value &props) {
const jsi::Array propNames = props.asObject(rt).getPropertyNames(rt);
const jsi::Object &props) {
const jsi::Array propNames = props.getPropertyNames(rt);
for (size_t i = 0; i < propNames.size(rt); ++i) {
const std::string propName =
propNames.getValueAtIndex(rt, i).asString(rt).utf8(rt);
Expand Down Expand Up @@ -519,20 +516,26 @@ bool NativeReanimatedModule::handleRawEvent(

void NativeReanimatedModule::updateProps(
jsi::Runtime &rt,
const jsi::Value &shadowNodeValue,
const jsi::Value &props) {
ShadowNode::Shared shadowNode = shadowNodeFromValue(rt, shadowNodeValue);

// TODO: support multiple surfaces
surfaceId_ = shadowNode->getSurfaceId();

if (isThereAnyLayoutProp(rt, props)) {
operationsInBatch_.emplace_back(
shadowNode, std::make_unique<jsi::Value>(rt, props));
} else {
// TODO: batch with layout props changes?
Tag tag = shadowNode->getTag();
synchronouslyUpdateUIPropsFunction(rt, tag, props);
const jsi::Value &operations) {
auto array = operations.asObject(rt).asArray(rt);
size_t length = array.size(rt);
for (size_t i = 0; i < length; ++i) {
auto item = array.getValueAtIndex(rt, i).asObject(rt);
auto shadowNodeWrapper = item.getProperty(rt, "shadowNodeWrapper");
ShadowNode::Shared shadowNode = shadowNodeFromValue(rt, shadowNodeWrapper);
const jsi::Object &props = item.getProperty(rt, "updates").asObject(rt);

// TODO: support multiple surfaces
surfaceId_ = shadowNode->getSurfaceId();

if (isThereAnyLayoutProp(rt, props)) {
operationsInBatch_.emplace_back(
shadowNode, std::make_unique<jsi::Value>(rt, props));
} else {
// TODO: batch with layout props changes?
Tag tag = shadowNode->getTag();
synchronouslyUpdateUIPropsFunction(rt, tag, props);
}
}
}

Expand Down
7 changes: 2 additions & 5 deletions Common/cpp/NativeModules/NativeReanimatedModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,7 @@ class NativeReanimatedModule : public NativeReanimatedModuleSpec,
#ifdef RCT_NEW_ARCH_ENABLED
bool handleRawEvent(const RawEvent &rawEvent, double currentTime);

void updateProps(
jsi::Runtime &rt,
const jsi::Value &shadowNodeValue,
const jsi::Value &props);
void updateProps(jsi::Runtime &rt, const jsi::Value &operations);

void removeShadowNodeFromRegistry(jsi::Runtime &rt, const jsi::Value &tag);

Expand Down Expand Up @@ -164,7 +161,7 @@ class NativeReanimatedModule : public NativeReanimatedModuleSpec,

private:
#ifdef RCT_NEW_ARCH_ENABLED
bool isThereAnyLayoutProp(jsi::Runtime &rt, const jsi::Value &props);
bool isThereAnyLayoutProp(jsi::Runtime &rt, const jsi::Object &props);
#endif // RCT_NEW_ARCH_ENABLED

std::unique_ptr<EventHandlerRegistry> eventHandlerRegistry;
Expand Down
15 changes: 5 additions & 10 deletions Common/cpp/Tools/PlatformDepMethodsHolder.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,9 @@ namespace reanimated {
#ifdef RCT_NEW_ARCH_ENABLED

using SynchronouslyUpdateUIPropsFunction =
std::function<void(jsi::Runtime &rt, Tag tag, const jsi::Value &props)>;
using UpdatePropsFunction = std::function<void(
jsi::Runtime &rt,
const jsi::Value &shadowNodeValue,
const jsi::Value &props)>;
std::function<void(jsi::Runtime &rt, Tag tag, const jsi::Object &props)>;
using UpdatePropsFunction =
std::function<void(jsi::Runtime &rt, const jsi::Value &operations)>;
using RemoveShadowNodeFromRegistryFunction =
std::function<void(jsi::Runtime &rt, const jsi::Value &tag)>;
using DispatchCommandFunction = std::function<void(
Expand All @@ -38,11 +36,8 @@ using MeasureFunction = std::function<

#else

using UpdatePropsFunction = std::function<void(
jsi::Runtime &rt,
int viewTag,
const jsi::Value &viewName,
jsi::Object object)>;
using UpdatePropsFunction =
std::function<void(jsi::Runtime &rt, const jsi::Value &operations)>;
using ScrollToFunction = std::function<void(int, double, double, bool)>;
using MeasureFunction =
std::function<std::vector<std::pair<std::string, double>>(int)>;
Expand Down
1 change: 1 addition & 0 deletions Example/android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,6 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<profileable android:shell="true" />
</application>
</manifest>
26 changes: 16 additions & 10 deletions android/src/main/cpp/NativeProxy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -234,16 +234,21 @@ void NativeProxy::configureProps(
.get());
}

void NativeProxy::updateProps(
jsi::Runtime &rt,
int viewTag,
const jsi::Value &viewName,
const jsi::Object &props) {
void NativeProxy::updateProps(jsi::Runtime &rt, const jsi::Value &operations) {
static const auto method =
getJniMethod<void(int, JMap<JString, JObject>::javaobject)>(
"updateProps");
method(
javaPart_.get(), viewTag, JNIHelper::ConvertToPropsMap(rt, props).get());
auto array = operations.asObject(rt).asArray(rt);
size_t length = array.size(rt);
for (size_t i = 0; i < length; ++i) {
auto item = array.getValueAtIndex(rt, i).asObject(rt);
int viewTag = item.getProperty(rt, "tag").asNumber();
const jsi::Object &props = item.getProperty(rt, "updates").asObject(rt);
method(
javaPart_.get(),
viewTag,
JNIHelper::ConvertToPropsMap(rt, props).get());
}
}

void NativeProxy::scrollTo(int viewTag, double x, double y, bool animated) {
Expand Down Expand Up @@ -279,12 +284,13 @@ inline jni::local_ref<ReadableMap::javaobject> castReadableMap(
void NativeProxy::synchronouslyUpdateUIProps(
jsi::Runtime &rt,
Tag tag,
const jsi::Value &props) {
const jsi::Object &props) {
static const auto method =
getJniMethod<void(int, jni::local_ref<ReadableMap::javaobject>)>(
"synchronouslyUpdateUIProps");
jni::local_ref<ReadableMap::javaobject> uiProps = castReadableMap(
ReadableNativeMap::newObjectCxxArgs(jsi::dynamicFromValue(rt, props)));
jni::local_ref<ReadableMap::javaobject> uiProps =
castReadableMap(ReadableNativeMap::newObjectCxxArgs(
jsi::dynamicFromValue(rt, jsi::Value(rt, props))));
method(javaPart_.get(), tag, uiProps);
}
#endif
Expand Down
8 changes: 2 additions & 6 deletions android/src/main/cpp/NativeProxy.h
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ class NativeProxy : public jni::HybridClass<NativeProxy> {
void synchronouslyUpdateUIProps(
jsi::Runtime &rt,
Tag viewTag,
const jsi::Value &uiProps);
const jsi::Object &props);
#else
void installJSIBindings(
jni::alias_ref<JavaMessageQueueThread::javaobject> messageQueueThread);
Expand Down Expand Up @@ -220,11 +220,7 @@ class NativeProxy : public jni::HybridClass<NativeProxy> {
jsi::Runtime &rt,
const jsi::Value &uiProps,
const jsi::Value &nativeProps);
void updateProps(
jsi::Runtime &rt,
int viewTag,
const jsi::Value &viewName,
const jsi::Object &props);
void updateProps(jsi::Runtime &rt, const jsi::Value &operations);
void scrollTo(int viewTag, double x, double y, bool animated);
std::vector<std::pair<std::string, double>> measure(int viewTag);
#endif
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,8 @@ public void runGuarded() {
}

private void onAnimationFrame(long frameTimeNanos) {
// Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "onAnimationFrame");

double currentFrameTimeMs = frameTimeNanos / 1000000.;

if (currentFrameTimeMs > lastFrameTimeMs) {
Expand Down Expand Up @@ -263,6 +265,8 @@ private void onAnimationFrame(long frameTimeNanos) {
// enqueue next frame
startUpdatingOnAnimationFrame();
}

// Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}

public void enqueueUpdateViewOnNativeThread(
Expand Down
66 changes: 66 additions & 0 deletions app/src/examples/UpdatePropsPerfExample.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import Animated, {
useAnimatedStyle,
withRepeat,
withTiming,
} from 'react-native-reanimated';
import { Animated as RNAnimated, View, StyleSheet } from 'react-native';
import React, { useEffect, useRef } from 'react';

const N = 1500;
const array = Array(N).fill(0);

function AnimatedBar() {
const scaleX = useRef(new RNAnimated.Value(1.5)).current;

useEffect(() => {
RNAnimated.loop(
RNAnimated.timing(scaleX, {
duration: 500,
toValue: Math.random() * 10,
useNativeDriver: true,
}),
{ iterations: -1 }
).start();
}, [scaleX]);

return <RNAnimated.View style={[styles.bar, { transform: [{ scaleX }] }]} />;
}

function ReanimatedBar() {
const style = useAnimatedStyle(() => {
return {
transform: [
{
scaleX: withRepeat(
withTiming(Math.random() * 10, { duration: 500 }),
-1,
true
),
},
],
};
});

return <Animated.View style={[styles.bar, style]} />;
}

export default function UpdatePropsPerfExample() {
return (
<View style={styles.container}>
{false && array.map((_, i) => <AnimatedBar key={i} />)}
{true && array.map((_, i) => <ReanimatedBar key={i} />)}
</View>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'column',
},
bar: {
width: 100,
height: 1,
backgroundColor: 'blue',
},
});
6 changes: 6 additions & 0 deletions app/src/examples/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ import WobbleExample from './WobbleExample';
import WorkletExample from './WorkletExample';
import ProfilesExample from './SharedElementTransitions/Profiles';
import VolumeExample from './VolumeExample';
import UpdatePropsPerfExample from './UpdatePropsPerfExample';

interface Example {
icon?: string;
Expand Down Expand Up @@ -152,6 +153,11 @@ export const EXAMPLES: Record<string, Example> = {
title: 'Letters',
screen: LettersExample,
},
UpdatePropsPerfExample: {
icon: '🏎️',
title: 'Update props performance',
screen: UpdatePropsPerfExample,
},

// Basic examples

Expand Down
30 changes: 19 additions & 11 deletions ios/native/NativeProxy.mm
Original file line number Diff line number Diff line change
Expand Up @@ -97,15 +97,23 @@ static CFTimeInterval calculateTimestampWithSlowAnimations(CFTimeInterval curren
// nothing
#else
RCTUIManager *uiManager = reanimatedModule.nodesManager.uiManager;
auto updatePropsFunction =
[reanimatedModule](jsi::Runtime &rt, int viewTag, const jsi::Value &viewName, const jsi::Object &props) -> void {
NSString *nsViewName = [NSString stringWithCString:viewName.asString(rt).utf8(rt).c_str()
encoding:[NSString defaultCStringEncoding]];

NSDictionary *propsDict = convertJSIObjectToNSDictionary(rt, props);
[reanimatedModule.nodesManager updateProps:propsDict
ofViewWithTag:[NSNumber numberWithInt:viewTag]
withName:nsViewName];
auto updatePropsFunction = [reanimatedModule](jsi::Runtime &rt, const jsi::Value &operations) -> void {
auto array = operations.asObject(rt).asArray(rt);
size_t length = array.size(rt);
for (size_t i = 0; i < length; ++i) {
auto item = array.getValueAtIndex(rt, i).asObject(rt);
int viewTag = item.getProperty(rt, "tag").asNumber();
const jsi::Value &viewName = item.getProperty(rt, "name");
const jsi::Object &props = item.getProperty(rt, "updates").asObject(rt);

NSString *nsViewName = [NSString stringWithCString:viewName.asString(rt).utf8(rt).c_str()
encoding:[NSString defaultCStringEncoding]];

NSDictionary *propsDict = convertJSIObjectToNSDictionary(rt, props);
[reanimatedModule.nodesManager updateProps:propsDict
ofViewWithTag:[NSNumber numberWithInt:viewTag]
withName:nsViewName];
}
};

auto measureFunction = [uiManager](int viewTag) -> std::vector<std::pair<std::string, double>> {
Expand Down Expand Up @@ -161,9 +169,9 @@ static CFTimeInterval calculateTimestampWithSlowAnimations(CFTimeInterval curren
};

#ifdef RCT_NEW_ARCH_ENABLED
auto synchronouslyUpdateUIPropsFunction = [nodesManager](jsi::Runtime &rt, Tag tag, const jsi::Value &props) {
auto synchronouslyUpdateUIPropsFunction = [nodesManager](jsi::Runtime &rt, Tag tag, const jsi::Object &props) {
NSNumber *viewTag = @(tag);
NSDictionary *uiProps = convertJSIObjectToNSDictionary(rt, props.asObject(rt));
NSDictionary *uiProps = convertJSIObjectToNSDictionary(rt, props);
[nodesManager synchronouslyUpdateViewOnUIThread:viewTag props:uiProps];
};

Expand Down
Loading

0 comments on commit 5fe5d0e

Please sign in to comment.