Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: allow using multiple performance profilers #76

Merged
merged 6 commits into from
Jun 14, 2022
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion fixture/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ buildscript {
}
repositories {
google()
jcenter()
mavenCentral()
maven {
url "https://plugins.gradle.org/m2/"
}
Expand Down Expand Up @@ -45,6 +45,7 @@ allprojects {
}
}
google()
mavenCentral()
maven { url 'https://www.jitpack.io' }
}

Expand Down
19 changes: 18 additions & 1 deletion fixture/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import FastRenderPassesScreen from './examples/FastRenderPassesScreen';
import ConditionalRenderingScreen from './examples/ConditionalRenderingScreen';
import DrawerNavigator from './examples/DrawerNavigator';
import NestedNavigationScreen from './examples/NestedNavigationScreen';
import NestedContextScreen, {InnerNestedContextScreen} from './examples/NestedContextScreen';

const Stack = createStackNavigator<RootStackParamList>();

Expand All @@ -29,11 +30,27 @@ const NavigationTree = () => {
<Stack.Screen name={NavigationKeys.CONDITIONAL_RENDERING_SCREEN} component={ConditionalRenderingScreen} />
<Stack.Screen name={NavigationKeys.FLAT_LIST_SCREEN} component={FlatListScreen} />
<Stack.Screen name={NavigationKeys.NESTED_NAVIGATION_SCREEN} component={NestedNavigationScreen} />
<Stack.Screen
name={NavigationKeys.NESTED_PROFILER_CONTEXT}
component={NestedProfilerNavigationTree}
options={{headerShown: false}}
/>
</Stack.Navigator>
</NavigationContainer>
);
};

function NestedProfilerNavigationTree() {
return (
<Stack.Navigator>
<Stack.Screen name={NavigationKeys.NESTED_CONTEXT_SCREEN} component={NestedContextScreen} />
<Stack.Group screenOptions={{presentation: 'modal'}}>
<Stack.Screen name={NavigationKeys.INNER_NESTED_CONTEXT_SCREEN} component={InnerNestedContextScreen} />
</Stack.Group>
</Stack.Navigator>
);
}

const App = () => {
const apolloClient = useMemo(() => {
return new ApolloClient({
Expand All @@ -53,7 +70,7 @@ const App = () => {
return (
<>
<ApolloProvider client={apolloClient}>
<PerformanceProfiler logLevel={LogLevel.Debug}>
<PerformanceProfiler logLevel={LogLevel.Error}>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should at least keep LogLevel.Warn?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, absolutely, I used it for testing and forgot to remove 👍

<ListsProfiler onInteractive={onInteractiveCallback} onBlankArea={onBlankAreaCallback}>
<NavigationTree />
</ListsProfiler>
Expand Down
3 changes: 3 additions & 0 deletions fixture/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ export const NavigationKeys = {
DRAWER_NAVIGATOR_SCREEN_2: 'DrawerNavigatorScreen2' as const,
FLAT_LIST_SCREEN: 'FlatListScreen' as const,
NESTED_NAVIGATION_SCREEN: 'NestedNavigationScreen' as const,
NESTED_PROFILER_CONTEXT: 'NestedProfilerContext' as const,
NESTED_CONTEXT_SCREEN: 'NestedContextScreen' as const,
INNER_NESTED_CONTEXT_SCREEN: 'InnerNestedContextScreen' as const,
};

type ValueOf<T> = T[keyof T];
Expand Down
26 changes: 18 additions & 8 deletions fixture/src/examples/ExamplesScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ import React from 'react';
import {StatusBar, StyleSheet, FlatList, Text, TouchableOpacity, Image} from 'react-native';
import {ReactNavigationPerformanceView, useProfiledNavigation} from '@shopify/react-native-performance-navigation';
import {StackNavigationProp} from '@react-navigation/stack';
import {useNavigation} from '@react-navigation/native';

import {NavigationKeys, RootStackParamList} from '../constants';

export const ExamplesScreen = () => {
const {navigate} = useProfiledNavigation<StackNavigationProp<RootStackParamList, 'Examples'>>();
const navigation = useNavigation<StackNavigationProp<RootStackParamList, 'Examples'>>();

const renderTimeoutMillisOverride = (screenName: string) => {
return screenName === NavigationKeys.PERFORMANCE ? 6 * 1000 : undefined;
Expand Down Expand Up @@ -40,19 +42,27 @@ export const ExamplesScreen = () => {
title: 'FlatList Screen',
destination: NavigationKeys.FLAT_LIST_SCREEN,
},
{
title: 'Nested Context Screen',
destination: NavigationKeys.NESTED_PROFILER_CONTEXT,
},
]}
renderItem={({item}) => (
<TouchableOpacity
style={styles.row}
onPress={uiEvent => {
navigate(
{
source: NavigationKeys.EXAMPLES,
uiEvent,
renderTimeoutMillisOverride: renderTimeoutMillisOverride(item.destination),
},
item.destination,
);
if (item.destination === NavigationKeys.NESTED_PROFILER_CONTEXT) {
navigation.navigate(item.destination);
} else {
navigate(
{
source: NavigationKeys.EXAMPLES,
uiEvent,
renderTimeoutMillisOverride: renderTimeoutMillisOverride(item.destination),
},
item.destination,
);
}
}}
>
<Text style={styles.rowTitle}>{item.title}</Text>
Expand Down
47 changes: 47 additions & 0 deletions fixture/src/examples/NestedContextScreen.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React from 'react';
import {ReactNavigationPerformanceView, useProfiledNavigation} from '@shopify/react-native-performance-navigation';
import {Button, Text, View, StyleSheet} from 'react-native';
import {StackNavigationProp} from '@react-navigation/stack';
import {PerformanceProfiler, LogLevel} from '@shopify/react-native-performance';

import {NavigationKeys, RootStackParamList} from '../constants';

const NestedContextScreen = () => {
const {navigate} = useProfiledNavigation<StackNavigationProp<RootStackParamList, 'Examples'>>();

return (
<PerformanceProfiler logLevel={LogLevel.Debug}>
<ReactNavigationPerformanceView screenName={NavigationKeys.NESTED_CONTEXT_SCREEN} interactive>
<Button
title="Present new screen inside nested Profiler Context"
onPress={() => navigate(NavigationKeys.INNER_NESTED_CONTEXT_SCREEN)}
/>
</ReactNavigationPerformanceView>
</PerformanceProfiler>
);
};

export const InnerNestedContextScreen = () => {
const text = 'This is a screen rendered in a nested Profiler Context\n\n You should see no errors in the logs';
return (
<ReactNavigationPerformanceView screenName={NavigationKeys.INNER_NESTED_CONTEXT_SCREEN} interactive>
<View style={styles.textContainer}>
<Text style={styles.text}>{text}</Text>
</View>
</ReactNavigationPerformanceView>
);
};

export default NestedContextScreen;

const styles = StyleSheet.create({
text: {
textAlignVertical: 'center',
textAlign: 'center',
fontSize: 18,
},
textContainer: {
flex: 1,
justifyContent: 'center',
},
});
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,17 @@ package com.shopify.reactnativeperformance

import android.content.Context
import android.view.View
import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.ReactContext
import com.facebook.react.uimanager.SimpleViewManager
import com.facebook.react.uimanager.ThemedReactContext
import com.facebook.react.uimanager.annotations.ReactProp
import com.facebook.react.uimanager.events.RCTEventEmitter
import kotlin.reflect.KProperty
import kotlin.properties.ReadWriteProperty
import com.facebook.react.common.MapBuilder

private const val RENDER_COMPLETION_EVENT_NAME = "@shopify/react-native-performance/onRenderComplete"

class PerformanceMarker(context: Context?) : View(context) {

Expand Down Expand Up @@ -35,7 +40,7 @@ class PerformanceMarker(context: Context?) : View(context) {
But that should be relatively negligible in the bigger scheme of things. Also note that we're using the
moment when `PerformanceMarker` is rendered as a proxy for when the rest of its siblings (the actual
screen content) is rendered. So we're already using these kinds of approximations at the native layer.
Adding this 1 additional approximation shouldn't affect the final render times signficantly.
Adding this 1 additional approximation shouldn't affect the final render times significantly.
*/
private fun sendRenderCompletionEventIfNeeded() {
val _destinationScreen = this.destinationScreen
Expand All @@ -52,13 +57,19 @@ class PerformanceMarker(context: Context?) : View(context) {
}

reportedOnce = true
RenderCompletionEventEmitter.onRenderComplete(
context as ReactContext,
destinationScreen = _destinationScreen,
renderPassName = _renderPassName,
interactive = _interactive,
componentInstanceId = _componentInstanceId,
)

val event = Arguments.createMap().apply {
putString("timestamp", System.currentTimeMillis().toString())
putString("renderPassName", _renderPassName)
putString("interactive", _interactive.toString())
putString("destinationScreen", _destinationScreen)
putString("componentInstanceId", _componentInstanceId)
}

val reactContext = context as ReactContext
reactContext
.getJSModule(RCTEventEmitter::class.java)
.receiveEvent(id, "onRenderComplete", event)
}

private class PerformanceMarkerProp<T : Any> : ReadWriteProperty<PerformanceMarker, T?> {
Expand Down Expand Up @@ -110,5 +121,13 @@ class PerformanceMarkerManager : SimpleViewManager<PerformanceMarker>() {
view.componentInstanceId = componentInstanceId
}

override fun getExportedCustomDirectEventTypeConstants(): MutableMap<String, Any> {
return MapBuilder.builder<String, Any>().put(
"onRenderComplete",
MapBuilder.of(
"registrationName", "onRenderComplete")
).build();
}

override fun getName() = "PerformanceMarker"
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ import Foundation
private var interactive: Interactive? = nil
private var destinationScreen: String? = nil
private var componentInstanceId: String? = nil
private var onRenderComplete: RCTDirectEventBlock? = nil

@objc func setOnRenderComplete(_ onRenderComplete: @escaping RCTDirectEventBlock) {
assertSetOnlyOnce(currentVal: self.onRenderComplete, newVal: onRenderComplete, propertyName: "onRenderComplete")
self.onRenderComplete = onRenderComplete
self.sendRenderCompletionEventIfNeeded()
}

@objc func setComponentInstanceId(_ componentInstanceId: String) {
assertSetOnlyOnce(currentVal: self.componentInstanceId, newVal: componentInstanceId, propertyName: "componentInstanceId")
Expand Down Expand Up @@ -48,21 +55,24 @@ import Foundation
let renderPassName = renderPassName,
let interactive = interactive,
let destinationScreen = destinationScreen,
let componentInstanceId = componentInstanceId
let componentInstanceId = componentInstanceId,
let onRenderComplete = onRenderComplete
else {
return
}

reportedOnce = true

RenderCompletionEventEmitter.INSTANCE?.onRenderComplete(
destinationScreen: destinationScreen,
renderPassName: renderPassName,
interactive: interactive,
componentInstanceId: componentInstanceId
) ?? assertionFailure(
"RenderCompletionEventEmitter.INSTANCE was not initialized by the time PerformanceMarker got rendered for screen " +
"'\(destinationScreen)', renderPassName '\(renderPassName)'.")
let timestamp = Timestamp.nowMillis()
let onRenderCompleteEvent = [
"timestamp": String(timestamp),
"renderPassName": renderPassName,
"interactive": interactive.description,
"destinationScreen": destinationScreen,
"componentInstanceId": componentInstanceId
]

onRenderComplete(onRenderCompleteEvent)

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ @interface RCT_EXTERN_MODULE(PerformanceMarkerManager, RCTViewManager)
RCT_EXPORT_VIEW_PROPERTY(interactive, NSString)
RCT_EXPORT_VIEW_PROPERTY(destinationScreen, NSString)
RCT_EXPORT_VIEW_PROPERTY(componentInstanceId, NSString)
RCT_EXPORT_VIEW_PROPERTY(onRenderComplete, RCTDirectEventBlock)

@end

This file was deleted.

This file was deleted.

Loading