From dfc389429217e8c2a34f969b510b0a9019f3f585 Mon Sep 17 00:00:00 2001 From: Jakub Gonet Date: Wed, 7 Feb 2024 13:10:20 +0100 Subject: [PATCH 01/19] WIP --- Example/ios/Podfile.lock | 4 +- FabricExample/ios/Podfile.lock | 4 +- app/src/examples/EmptyExample.tsx | 167 +++++++++++++++++++++++++++++- 3 files changed, 168 insertions(+), 7 deletions(-) diff --git a/Example/ios/Podfile.lock b/Example/ios/Podfile.lock index 4103f29378d0..de937fe3447b 100644 --- a/Example/ios/Podfile.lock +++ b/Example/ios/Podfile.lock @@ -1445,8 +1445,8 @@ SPEC CHECKSUMS: RNScreens: b582cb834dc4133307562e930e8fa914b8c04ef2 RNSVG: ba3e7232f45e34b7b47e74472386cf4e1a676d0a SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17 - Yoga: ff0382b894475dba0b4d2a5fda860bfee5a9afad + Yoga: 08cd7601462818c4985bda7b205b9e6a92a7e66a PODFILE CHECKSUM: fca071226b19ce84770ff3ba64fd881fcb93dead -COCOAPODS: 1.14.3 +COCOAPODS: 1.11.3 diff --git a/FabricExample/ios/Podfile.lock b/FabricExample/ios/Podfile.lock index 5ff1dcabb94b..6cd7e9fd01fd 100644 --- a/FabricExample/ios/Podfile.lock +++ b/FabricExample/ios/Podfile.lock @@ -1721,8 +1721,8 @@ SPEC CHECKSUMS: RNScreens: f7b8bb892b4957f6f91e5dfd9a191e7f13ce8baa RNSVG: db32cfcad0a221fd175e0882eff7bcba7690380a SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17 - Yoga: ff0382b894475dba0b4d2a5fda860bfee5a9afad + Yoga: 08cd7601462818c4985bda7b205b9e6a92a7e66a PODFILE CHECKSUM: 8be1b60017e74344d005092a2799dafc33f58ffa -COCOAPODS: 1.14.3 +COCOAPODS: 1.11.3 diff --git a/app/src/examples/EmptyExample.tsx b/app/src/examples/EmptyExample.tsx index 19a20bdce594..256852a67282 100644 --- a/app/src/examples/EmptyExample.tsx +++ b/app/src/examples/EmptyExample.tsx @@ -1,16 +1,177 @@ -import { Text, StyleSheet, View } from 'react-native'; +import { Text, TextInput, StyleSheet, View } from 'react-native'; -import React from 'react'; +import React, { useEffect, useState } from 'react'; + +import Animated, { + useAnimatedProps, + useAnimatedStyle, + useFrameCallback, + useSharedValue, +} from 'react-native-reanimated'; + +import { Gesture, GestureDetector } from 'react-native-gesture-handler'; + +function createCircularBuffer(size: number) { + 'worklet'; + const buffer = { + size: size, + getSize() { + return this.size; + }, + setSize(n) { + this.size = n; + }, + }; + + return buffer; +} +class CircularBuffer { + size: number; + last: number; + + constructor(size: number) { + this.size = size; + this.last = -1; + } +} export default function EmptyExample() { return ( - Hello world! + + + ); +} + +Animated.addWhitelistedNativeProps({ text: true }); +const AnimatedTextInput = Animated.createAnimatedComponent(TextInput); + +function loopAnimationFrame(fn: (lastTime: number, time: number) => void) { + let lastTime = 0; + + function loop() { + requestAnimationFrame((time) => { + if (lastTime > 0) { + fn(lastTime, time); + } + lastTime = time; + requestAnimationFrame(loop); + }); + } + + loop(); +} + +function fps(deltaInMs: number) { + 'worklet'; + return ((1 / deltaInMs) * 1000).toFixed(3); +} + +function JsPerformance() { + const [jsFps, setJsFps] = useState(null); + + useEffect(() => { + loopAnimationFrame((lastTime, time) => setJsFps(fps(time - lastTime))); + }, []); + + return ( + + JS FPS + {jsFps ?? 'N/A'} + + ); +} + +function UiPerformance() { + const uiFps = useSharedValue(null); + const lastTimestamp = useSharedValue(null); + const lastLastTimestamp = useSharedValue(null); + const circularBuffer = useSharedValue(createCircularBuffer(5)); + + useFrameCallback((frameInfo) => { + if (lastTimestamp.value === null) { + lastTimestamp.value = frameInfo.timestamp; + } else if (lastLastTimestamp.value === null) { + lastLastTimestamp.value = lastTimestamp.value; + } else { + if (frameInfo.timestamp - lastTimestamp.value < 0) { + console.log(circularBuffer.value); + + circularBuffer.value.setSize(lastTimestamp.value); + console.log(circularBuffer.value.getSize()); + + console.log({ + t: frameInfo.timestamp, + pt: lastTimestamp.value, + ppt: lastLastTimestamp.value, + d: frameInfo.timestamp - lastTimestamp.value, + }); + } + uiFps.value = fps(frameInfo.timestamp - lastTimestamp.value); + lastLastTimestamp.value = lastTimestamp.value; + lastTimestamp.value = frameInfo.timestamp; + } + }); + + const animatedProps = useAnimatedProps(() => { + const text = uiFps.value ?? 'N/A'; + return { text, defaultValue: text }; + }); + + return ( + + UI FPS + ); } +function PerformanceMonitor() { + const lastPosition = useSharedValue({ xOffset: 0, yOffset: 0 }); + const position = useSharedValue({ x: 0, y: 0 }); + const move = Gesture.Pan() + .onChange((e) => { + const { xOffset, yOffset } = lastPosition.value; + + position.value = { + x: xOffset + e.translationX, + y: yOffset + e.translationY, + }; + }) + .onEnd(() => { + const { x: lastX, y: lastY } = position.value; + + lastPosition.value = { xOffset: lastX, yOffset: lastY }; + }); + + const positionStyle = useAnimatedStyle(() => { + const { x, y } = position.value; + return { + top: y, + left: x, + }; + }); + + return ( + + + + + + + ); +} + const styles = StyleSheet.create({ + monitor: { + flexDirection: 'row', + gap: 16, + borderWidth: 1, + padding: 8, + position: 'absolute', + backgroundColor: '#fff', + zIndex: 1000, + }, container: { flex: 1, alignItems: 'center', From 2c21257cc13eec104e6b639bff79dbcf0d1dd9f5 Mon Sep 17 00:00:00 2001 From: Jakub Gonet Date: Thu, 15 Feb 2024 11:05:10 +0100 Subject: [PATCH 02/19] WIP --- app/src/examples/BokehExample.tsx | 2 + app/src/examples/EmptyExample.tsx | 167 +------------------- app/src/examples/PerfomanceMonitor.tsx | 201 +++++++++++++++++++++++++ 3 files changed, 207 insertions(+), 163 deletions(-) create mode 100644 app/src/examples/PerfomanceMonitor.tsx diff --git a/app/src/examples/BokehExample.tsx b/app/src/examples/BokehExample.tsx index 0a73cc2bc790..447eef330be2 100644 --- a/app/src/examples/BokehExample.tsx +++ b/app/src/examples/BokehExample.tsx @@ -7,6 +7,7 @@ import Animated, { } from 'react-native-reanimated'; import { Dimensions, StyleSheet, View } from 'react-native'; import React, { useState } from 'react'; +import { PerformanceMonitor } from './PerfomanceMonitor'; const { width, height } = Dimensions.get('window'); @@ -74,6 +75,7 @@ function Bokeh({ count }: BokehProps) { export default function BokehExample() { return ( + ); diff --git a/app/src/examples/EmptyExample.tsx b/app/src/examples/EmptyExample.tsx index 256852a67282..fd5c0f935aa3 100644 --- a/app/src/examples/EmptyExample.tsx +++ b/app/src/examples/EmptyExample.tsx @@ -1,177 +1,18 @@ -import { Text, TextInput, StyleSheet, View } from 'react-native'; +import { Text, StyleSheet, View } from 'react-native'; -import React, { useEffect, useState } from 'react'; - -import Animated, { - useAnimatedProps, - useAnimatedStyle, - useFrameCallback, - useSharedValue, -} from 'react-native-reanimated'; - -import { Gesture, GestureDetector } from 'react-native-gesture-handler'; - -function createCircularBuffer(size: number) { - 'worklet'; - const buffer = { - size: size, - getSize() { - return this.size; - }, - setSize(n) { - this.size = n; - }, - }; - - return buffer; -} -class CircularBuffer { - size: number; - last: number; - - constructor(size: number) { - this.size = size; - this.last = -1; - } -} +import React from 'react'; +import { PerformanceMonitor } from './PerfomanceMonitor'; export default function EmptyExample() { return ( + Hello world! ); } -Animated.addWhitelistedNativeProps({ text: true }); -const AnimatedTextInput = Animated.createAnimatedComponent(TextInput); - -function loopAnimationFrame(fn: (lastTime: number, time: number) => void) { - let lastTime = 0; - - function loop() { - requestAnimationFrame((time) => { - if (lastTime > 0) { - fn(lastTime, time); - } - lastTime = time; - requestAnimationFrame(loop); - }); - } - - loop(); -} - -function fps(deltaInMs: number) { - 'worklet'; - return ((1 / deltaInMs) * 1000).toFixed(3); -} - -function JsPerformance() { - const [jsFps, setJsFps] = useState(null); - - useEffect(() => { - loopAnimationFrame((lastTime, time) => setJsFps(fps(time - lastTime))); - }, []); - - return ( - - JS FPS - {jsFps ?? 'N/A'} - - ); -} - -function UiPerformance() { - const uiFps = useSharedValue(null); - const lastTimestamp = useSharedValue(null); - const lastLastTimestamp = useSharedValue(null); - const circularBuffer = useSharedValue(createCircularBuffer(5)); - - useFrameCallback((frameInfo) => { - if (lastTimestamp.value === null) { - lastTimestamp.value = frameInfo.timestamp; - } else if (lastLastTimestamp.value === null) { - lastLastTimestamp.value = lastTimestamp.value; - } else { - if (frameInfo.timestamp - lastTimestamp.value < 0) { - console.log(circularBuffer.value); - - circularBuffer.value.setSize(lastTimestamp.value); - console.log(circularBuffer.value.getSize()); - - console.log({ - t: frameInfo.timestamp, - pt: lastTimestamp.value, - ppt: lastLastTimestamp.value, - d: frameInfo.timestamp - lastTimestamp.value, - }); - } - uiFps.value = fps(frameInfo.timestamp - lastTimestamp.value); - lastLastTimestamp.value = lastTimestamp.value; - lastTimestamp.value = frameInfo.timestamp; - } - }); - - const animatedProps = useAnimatedProps(() => { - const text = uiFps.value ?? 'N/A'; - return { text, defaultValue: text }; - }); - - return ( - - UI FPS - - - ); -} - -function PerformanceMonitor() { - const lastPosition = useSharedValue({ xOffset: 0, yOffset: 0 }); - const position = useSharedValue({ x: 0, y: 0 }); - const move = Gesture.Pan() - .onChange((e) => { - const { xOffset, yOffset } = lastPosition.value; - - position.value = { - x: xOffset + e.translationX, - y: yOffset + e.translationY, - }; - }) - .onEnd(() => { - const { x: lastX, y: lastY } = position.value; - - lastPosition.value = { xOffset: lastX, yOffset: lastY }; - }); - - const positionStyle = useAnimatedStyle(() => { - const { x, y } = position.value; - return { - top: y, - left: x, - }; - }); - - return ( - - - - - - - ); -} - const styles = StyleSheet.create({ - monitor: { - flexDirection: 'row', - gap: 16, - borderWidth: 1, - padding: 8, - position: 'absolute', - backgroundColor: '#fff', - zIndex: 1000, - }, container: { flex: 1, alignItems: 'center', diff --git a/app/src/examples/PerfomanceMonitor.tsx b/app/src/examples/PerfomanceMonitor.tsx new file mode 100644 index 000000000000..ae34ceec317c --- /dev/null +++ b/app/src/examples/PerfomanceMonitor.tsx @@ -0,0 +1,201 @@ +import { Text, TextInput, StyleSheet, View } from 'react-native'; + +import React, { useEffect, useMemo, useRef, useState } from 'react'; + +import Animated, { + useAnimatedProps, + useAnimatedStyle, + useFrameCallback, + useSharedValue, +} from 'react-native-reanimated'; + +import { Gesture, GestureDetector } from 'react-native-gesture-handler'; + +type CircularBuffer = ReturnType; +function createCircularDoublesBuffer(size: number) { + 'worklet'; + + return { + next: 0 as number, + buffer: new Float32Array(size), + size: size, + count: 0 as number, + + push(value: number): number | null { + const oldValue = this.buffer[this.next]; + const oldCount = this.count; + this.buffer[this.next] = value; + + this.next = (this.next + 1) % this.size; + this.count = Math.min(this.size, this.count + 1); + return oldCount === this.size ? oldValue : null; + }, + + front(): number | null { + const atLeastOne = this.count >= 1; + if (atLeastOne) { + const current = this.next - 1; + const index = current < 0 ? this.size - 1 : current; + return this.buffer[index]; + } + return null; + }, + + back(): number | null { + const atLeastOne = this.count >= 1; + if (atLeastOne) { + return this.buffer[this.next]; + } + return null; + }, + + reduce(fn: (acc: T, value: number) => T, initial: T) { + let i = 0; + let acc = initial; + while (i < this.count) { + const offset = (this.next + i) % this.size; + acc = fn(acc, this.buffer[offset]); + ++i; + } + return acc; + }, + } as const; +} + +Animated.addWhitelistedNativeProps({ text: true }); +const AnimatedTextInput = Animated.createAnimatedComponent(TextInput); + +function loopAnimationFrame(fn: (lastTime: number, time: number) => void) { + let lastTime = 0; + + function loop() { + requestAnimationFrame((time) => { + if (lastTime > 0) { + fn(lastTime, time); + } + lastTime = time; + requestAnimationFrame(loop); + }); + } + + loop(); +} + +function fps(renderTimeInMs: number) { + 'worklet'; + return ((1 / renderTimeInMs) * 1000).toFixed(3); +} + +function JsPerformance() { + const [jsFps, setJsFps] = useState(null); + + useEffect(() => { + loopAnimationFrame((lastTime, time) => { + return setJsFps(fps(time - lastTime)); + }); + }, []); + + return ( + + JS FPS + {jsFps ?? 'N/A'} + + ); +} + +function UiPerformance() { + const uiFps = useSharedValue(null); + const totalRenderTime = useSharedValue(0); + const circularBuffer = useRef(null); + + useFrameCallback(({ timestamp }) => { + if (circularBuffer.current === null) { + circularBuffer.current = createCircularDoublesBuffer(100); + } + + const buffer = circularBuffer.current; + const previousTimestamp = buffer.front() ?? timestamp; + const droppedTimestamp = buffer.push(timestamp); + const nextToDrop = buffer.back()!; + + const delta = timestamp - previousTimestamp; + const droppedDelta = + droppedTimestamp !== null ? nextToDrop - droppedTimestamp : 0; + totalRenderTime.value += delta - droppedDelta; + uiFps.value = fps(totalRenderTime.value / buffer.count); + }); + + const animatedProps = useAnimatedProps(() => { + const text = uiFps.value ?? 'N/A'; + return { text, defaultValue: text }; + }); + + return ( + + UI FPS + + + ); +} + +export function PerformanceMonitor() { + const lastPosition = useSharedValue({ xOffset: 0, yOffset: 0 }); + const position = useSharedValue({ x: 0, y: 0 }); + const move = useMemo( + () => + Gesture.Pan() + .onChange((e) => { + const { xOffset, yOffset } = lastPosition.value; + + position.value = { + x: xOffset + e.translationX, + y: yOffset + e.translationY, + }; + }) + .onEnd(() => { + const { x: lastX, y: lastY } = position.value; + + lastPosition.value = { xOffset: lastX, yOffset: lastY }; + }), + [lastPosition, position] + ); + const positionStyle = useAnimatedStyle(() => { + const { x, y } = position.value; + return { + transform: [{ translateX: x }, { translateY: y }], + }; + }); + + return ( + + + + + + + ); +} + +const styles = StyleSheet.create({ + monitor: { + flexDirection: 'row', + gap: 16, + borderWidth: 1, + padding: 8, + position: 'absolute', + backgroundColor: '#fff', + zIndex: 1000, + }, + text: { + width: 50, + }, + container: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + }, +}); From 08f8554f65dd9665bb82decb33dc67f0fb24e579 Mon Sep 17 00:00:00 2001 From: Krzysztof Piaskowy Date: Fri, 8 Mar 2024 16:16:58 +0100 Subject: [PATCH 03/19] Restore Podfile.lock --- Example/ios/Podfile.lock | 2 +- FabricExample/ios/Podfile.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Example/ios/Podfile.lock b/Example/ios/Podfile.lock index 16a41cf8cb6e..7a29bde244d3 100644 --- a/Example/ios/Podfile.lock +++ b/Example/ios/Podfile.lock @@ -1449,4 +1449,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: fca071226b19ce84770ff3ba64fd881fcb93dead -COCOAPODS: 1.11.3 +COCOAPODS: 1.14.3 diff --git a/FabricExample/ios/Podfile.lock b/FabricExample/ios/Podfile.lock index f0451f71a119..dec553937b1d 100644 --- a/FabricExample/ios/Podfile.lock +++ b/FabricExample/ios/Podfile.lock @@ -1725,4 +1725,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 8be1b60017e74344d005092a2799dafc33f58ffa -COCOAPODS: 1.11.3 +COCOAPODS: 1.14.3 From 5e9ec7431d246a10c6261078b0f5d8499112d4b2 Mon Sep 17 00:00:00 2001 From: LatekVo Date: Fri, 8 Mar 2024 18:55:32 +0100 Subject: [PATCH 04/19] fix floating point error --- app/src/examples/PerfomanceMonitor.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/examples/PerfomanceMonitor.tsx b/app/src/examples/PerfomanceMonitor.tsx index ae34ceec317c..18e1b8b89962 100644 --- a/app/src/examples/PerfomanceMonitor.tsx +++ b/app/src/examples/PerfomanceMonitor.tsx @@ -113,6 +113,7 @@ function UiPerformance() { circularBuffer.current = createCircularDoublesBuffer(100); } + timestamp = Math.round(timestamp); const buffer = circularBuffer.current; const previousTimestamp = buffer.front() ?? timestamp; const droppedTimestamp = buffer.push(timestamp); From f69861fe50c7abc59e3101ecfb086f6f518ef33b Mon Sep 17 00:00:00 2001 From: LatekVo Date: Mon, 11 Mar 2024 14:42:04 +0100 Subject: [PATCH 05/19] Add circularBuffer to JS fps counter to avoid fps spikes. --- app/src/examples/PerfomanceMonitor.tsx | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/app/src/examples/PerfomanceMonitor.tsx b/app/src/examples/PerfomanceMonitor.tsx index 18e1b8b89962..7c84a5fb2e14 100644 --- a/app/src/examples/PerfomanceMonitor.tsx +++ b/app/src/examples/PerfomanceMonitor.tsx @@ -87,11 +87,24 @@ function fps(renderTimeInMs: number) { } function JsPerformance() { - const [jsFps, setJsFps] = useState(null); - + const totalRenderTime = useSharedValue(0); + const circularBuffer = createCircularDoublesBuffer(100); + const [jsFps, setJsFps] = useState(null) + useEffect(() => { loopAnimationFrame((lastTime, time) => { - return setJsFps(fps(time - lastTime)); + const timestamp = Math.round(time); + const previousTimestamp = Math.round(lastTime) ?? timestamp; + + const droppedTimestamp = circularBuffer.push(timestamp); + const nextToDrop = circularBuffer.back()!; + + const delta = timestamp - previousTimestamp; + const droppedDelta = droppedTimestamp !== null ? nextToDrop - droppedTimestamp : 0; + totalRenderTime.value += delta - droppedDelta; + const currentFps = fps(totalRenderTime.value / circularBuffer.count); + + return setJsFps(currentFps); }); }, []); From ce2e98192eae03f4249308eb6a481f5e2e62c594 Mon Sep 17 00:00:00 2001 From: LatekVo Date: Mon, 11 Mar 2024 18:10:22 +0100 Subject: [PATCH 06/19] Visual improvements, remove jittering. Ran prettier. --- app/src/examples/PerfomanceMonitor.tsx | 43 +++++++++++++++++--------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/app/src/examples/PerfomanceMonitor.tsx b/app/src/examples/PerfomanceMonitor.tsx index 7c84a5fb2e14..8d83230d4b41 100644 --- a/app/src/examples/PerfomanceMonitor.tsx +++ b/app/src/examples/PerfomanceMonitor.tsx @@ -83,14 +83,14 @@ function loopAnimationFrame(fn: (lastTime: number, time: number) => void) { function fps(renderTimeInMs: number) { 'worklet'; - return ((1 / renderTimeInMs) * 1000).toFixed(3); + return ((1 / renderTimeInMs) * 1000).toFixed(1); } function JsPerformance() { const totalRenderTime = useSharedValue(0); const circularBuffer = createCircularDoublesBuffer(100); - const [jsFps, setJsFps] = useState(null) - + const [jsFps, setJsFps] = useState(null); + useEffect(() => { loopAnimationFrame((lastTime, time) => { const timestamp = Math.round(time); @@ -98,20 +98,30 @@ function JsPerformance() { const droppedTimestamp = circularBuffer.push(timestamp); const nextToDrop = circularBuffer.back()!; - + const delta = timestamp - previousTimestamp; - const droppedDelta = droppedTimestamp !== null ? nextToDrop - droppedTimestamp : 0; + const droppedDelta = + droppedTimestamp !== null ? nextToDrop - droppedTimestamp : 0; totalRenderTime.value += delta - droppedDelta; const currentFps = fps(totalRenderTime.value / circularBuffer.count); - + return setJsFps(currentFps); }); }, []); + const animatedProps = useAnimatedProps(() => { + const text = jsFps ?? 'N/A'; + return { text, defaultValue: text }; + }); + return ( - - JS FPS - {jsFps ?? 'N/A'} + + JS FPS + ); } @@ -145,8 +155,8 @@ function UiPerformance() { }); return ( - - UI FPS + + UI FPS Date: Fri, 15 Mar 2024 19:37:35 +0100 Subject: [PATCH 07/19] Extracted ui and js logic into a separate function. --- app/src/examples/PerfomanceMonitor.tsx | 78 ++++++++++++++++---------- 1 file changed, 49 insertions(+), 29 deletions(-) diff --git a/app/src/examples/PerfomanceMonitor.tsx b/app/src/examples/PerfomanceMonitor.tsx index 8d83230d4b41..33d60bb0c114 100644 --- a/app/src/examples/PerfomanceMonitor.tsx +++ b/app/src/examples/PerfomanceMonitor.tsx @@ -3,6 +3,7 @@ import { Text, TextInput, StyleSheet, View } from 'react-native'; import React, { useEffect, useMemo, useRef, useState } from 'react'; import Animated, { + SharedValue, useAnimatedProps, useAnimatedStyle, useFrameCallback, @@ -81,36 +82,59 @@ function loopAnimationFrame(fn: (lastTime: number, time: number) => void) { loop(); } -function fps(renderTimeInMs: number) { +function getFps(renderTimeInMs: number): string { 'worklet'; - return ((1 / renderTimeInMs) * 1000).toFixed(1); + return ((1 / renderTimeInMs) * 1000).toFixed(1).toString(); +} + +function getTimeDelta( + timestamp: number, + previousTimestamp: number | null +): number { + 'worklet'; + return previousTimestamp !== null ? timestamp - previousTimestamp : 0; +} + +function completeBufferRoutine( + buffer: CircularBuffer, + timestamp: number, + previousTimestamp: number, + totalRenderTime: SharedValue +) { + 'worklet'; + timestamp = Math.round(timestamp); + previousTimestamp = Math.round(previousTimestamp) ?? timestamp; + + const droppedTimestamp = buffer.push(timestamp); + const nextToDrop = buffer.back()!; + + const delta = getTimeDelta(timestamp, previousTimestamp); + const droppedDelta = getTimeDelta(nextToDrop, droppedTimestamp); + + totalRenderTime.value += delta - droppedDelta; + return getFps(totalRenderTime.value / buffer.count); } function JsPerformance() { + const jsFps = useSharedValue(null); const totalRenderTime = useSharedValue(0); const circularBuffer = createCircularDoublesBuffer(100); - const [jsFps, setJsFps] = useState(null); useEffect(() => { loopAnimationFrame((lastTime, time) => { - const timestamp = Math.round(time); - const previousTimestamp = Math.round(lastTime) ?? timestamp; - - const droppedTimestamp = circularBuffer.push(timestamp); - const nextToDrop = circularBuffer.back()!; - - const delta = timestamp - previousTimestamp; - const droppedDelta = - droppedTimestamp !== null ? nextToDrop - droppedTimestamp : 0; - totalRenderTime.value += delta - droppedDelta; - const currentFps = fps(totalRenderTime.value / circularBuffer.count); - - return setJsFps(currentFps); + const currentFps = completeBufferRoutine( + circularBuffer, + time, + lastTime, + totalRenderTime + ); + + jsFps.value = currentFps; }); }, []); const animatedProps = useAnimatedProps(() => { - const text = jsFps ?? 'N/A'; + const text = jsFps.value ?? 'N/A'; return { text, defaultValue: text }; }); @@ -135,18 +159,14 @@ function UiPerformance() { if (circularBuffer.current === null) { circularBuffer.current = createCircularDoublesBuffer(100); } - - timestamp = Math.round(timestamp); - const buffer = circularBuffer.current; - const previousTimestamp = buffer.front() ?? timestamp; - const droppedTimestamp = buffer.push(timestamp); - const nextToDrop = buffer.back()!; - - const delta = timestamp - previousTimestamp; - const droppedDelta = - droppedTimestamp !== null ? nextToDrop - droppedTimestamp : 0; - totalRenderTime.value += delta - droppedDelta; - uiFps.value = fps(totalRenderTime.value / buffer.count); + const previousTimestamp = circularBuffer.current.front() ?? timestamp; + const currentFps = completeBufferRoutine( + circularBuffer.current, + timestamp, + previousTimestamp, + totalRenderTime + ); + uiFps.value = currentFps; }); const animatedProps = useAnimatedProps(() => { From b1611bf3fc03c8246c92d08fabdfa99b75fb7e2f Mon Sep 17 00:00:00 2001 From: LatekVo Date: Mon, 18 Mar 2024 15:25:38 +0100 Subject: [PATCH 08/19] save before refactor --- app/src/examples/PerfomanceMonitor.tsx | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/app/src/examples/PerfomanceMonitor.tsx b/app/src/examples/PerfomanceMonitor.tsx index 33d60bb0c114..0335ce4f7a4b 100644 --- a/app/src/examples/PerfomanceMonitor.tsx +++ b/app/src/examples/PerfomanceMonitor.tsx @@ -1,6 +1,6 @@ import { Text, TextInput, StyleSheet, View } from 'react-native'; -import React, { useEffect, useMemo, useRef, useState } from 'react'; +import React, { useEffect, useMemo, useRef } from 'react'; import Animated, { SharedValue, @@ -33,8 +33,8 @@ function createCircularDoublesBuffer(size: number) { }, front(): number | null { - const atLeastOne = this.count >= 1; - if (atLeastOne) { + const notEmpty = this.count > 0; + if (notEmpty) { const current = this.next - 1; const index = current < 0 ? this.size - 1 : current; return this.buffer[index]; @@ -43,11 +43,8 @@ function createCircularDoublesBuffer(size: number) { }, back(): number | null { - const atLeastOne = this.count >= 1; - if (atLeastOne) { - return this.buffer[this.next]; - } - return null; + const notEmpty = this.count > 0; + return notEmpty ? this.buffer[this.next] : null; }, reduce(fn: (acc: T, value: number) => T, initial: T) { From 06337420a38547d86fc2f2c89f961890753771d1 Mon Sep 17 00:00:00 2001 From: LatekVo Date: Mon, 18 Mar 2024 16:23:57 +0100 Subject: [PATCH 09/19] Moved performance monitor to a separate component. Added performance monitor example. Removed PR specific mess from EmptyExample and Bokeh. --- app/src/examples/BokehExample.tsx | 2 - app/src/examples/EmptyExample.tsx | 2 - app/src/examples/PerfomanceMonitorExample.tsx | 11 +++++ app/src/examples/index.ts | 6 +++ .../component/PerformanceMonitor.tsx | 46 +++---------------- src/reanimated2/index.ts | 1 + 6 files changed, 25 insertions(+), 43 deletions(-) create mode 100644 app/src/examples/PerfomanceMonitorExample.tsx rename app/src/examples/PerfomanceMonitor.tsx => src/reanimated2/component/PerformanceMonitor.tsx (81%) diff --git a/app/src/examples/BokehExample.tsx b/app/src/examples/BokehExample.tsx index 447eef330be2..0a73cc2bc790 100644 --- a/app/src/examples/BokehExample.tsx +++ b/app/src/examples/BokehExample.tsx @@ -7,7 +7,6 @@ import Animated, { } from 'react-native-reanimated'; import { Dimensions, StyleSheet, View } from 'react-native'; import React, { useState } from 'react'; -import { PerformanceMonitor } from './PerfomanceMonitor'; const { width, height } = Dimensions.get('window'); @@ -75,7 +74,6 @@ function Bokeh({ count }: BokehProps) { export default function BokehExample() { return ( - ); diff --git a/app/src/examples/EmptyExample.tsx b/app/src/examples/EmptyExample.tsx index fd5c0f935aa3..19a20bdce594 100644 --- a/app/src/examples/EmptyExample.tsx +++ b/app/src/examples/EmptyExample.tsx @@ -1,12 +1,10 @@ import { Text, StyleSheet, View } from 'react-native'; import React from 'react'; -import { PerformanceMonitor } from './PerfomanceMonitor'; export default function EmptyExample() { return ( - Hello world! ); diff --git a/app/src/examples/PerfomanceMonitorExample.tsx b/app/src/examples/PerfomanceMonitorExample.tsx new file mode 100644 index 000000000000..6a1dc8c9a293 --- /dev/null +++ b/app/src/examples/PerfomanceMonitorExample.tsx @@ -0,0 +1,11 @@ +import React from 'react'; +import { View } from 'react-native'; +import { PerformanceMonitor } from 'react-native-reanimated'; + +export default function PerformanceMonitorExample() { + return ( + + + + ); +} diff --git a/app/src/examples/index.ts b/app/src/examples/index.ts index 6248d5fd2aa3..7ddc2fc38c4d 100644 --- a/app/src/examples/index.ts +++ b/app/src/examples/index.ts @@ -118,6 +118,7 @@ import WithClampExample from './WithClampExample'; import WorkletFactoryCrash from './WorkletFactoryCrashExample'; import HabitsExample from './LayoutAnimations/HabitsExample'; import MemoExample from './MemoExample'; +import PerformanceMonitorExample from './PerfomanceMonitorExample'; interface Example { icon?: string; @@ -467,6 +468,11 @@ export const EXAMPLES: Record = { title: 'Habits', screen: HabitsExample, }, + PerformanceMonitorExample: { + icon: '⏱️', + title: 'Performance monitor', + screen: PerformanceMonitorExample, + }, // Old examples diff --git a/app/src/examples/PerfomanceMonitor.tsx b/src/reanimated2/component/PerformanceMonitor.tsx similarity index 81% rename from app/src/examples/PerfomanceMonitor.tsx rename to src/reanimated2/component/PerformanceMonitor.tsx index 0335ce4f7a4b..0e38c9392cbb 100644 --- a/app/src/examples/PerfomanceMonitor.tsx +++ b/src/reanimated2/component/PerformanceMonitor.tsx @@ -1,16 +1,13 @@ import { Text, TextInput, StyleSheet, View } from 'react-native'; -import React, { useEffect, useMemo, useRef } from 'react'; +import React, { useEffect, useRef } from 'react'; import Animated, { SharedValue, useAnimatedProps, - useAnimatedStyle, useFrameCallback, useSharedValue, -} from 'react-native-reanimated'; - -import { Gesture, GestureDetector } from 'react-native-gesture-handler'; +} from '../../..'; type CircularBuffer = ReturnType; function createCircularDoublesBuffer(size: number) { @@ -57,7 +54,7 @@ function createCircularDoublesBuffer(size: number) { } return acc; }, - } as const; + }; } Animated.addWhitelistedNativeProps({ text: true }); @@ -184,40 +181,11 @@ function UiPerformance() { } export function PerformanceMonitor() { - const lastPosition = useSharedValue({ xOffset: 0, yOffset: 0 }); - const position = useSharedValue({ x: 0, y: 0 }); - const move = useMemo( - () => - Gesture.Pan() - .onChange((e) => { - const { xOffset, yOffset } = lastPosition.value; - - position.value = { - x: xOffset + e.translationX, - y: yOffset + e.translationY, - }; - }) - .onEnd(() => { - const { x: lastX, y: lastY } = position.value; - - lastPosition.value = { xOffset: lastX, yOffset: lastY }; - }), - [lastPosition, position] - ); - const positionStyle = useAnimatedStyle(() => { - const { x, y } = position.value; - return { - transform: [{ translateX: x }, { translateY: y }], - }; - }); - return ( - - - - - - + + + + ); } diff --git a/src/reanimated2/index.ts b/src/reanimated2/index.ts index c8e0fc1d79ec..bca4b773a2df 100644 --- a/src/reanimated2/index.ts +++ b/src/reanimated2/index.ts @@ -257,6 +257,7 @@ export { getAnimatedStyle, } from './jestUtils'; export { LayoutAnimationConfig } from './component/LayoutAnimationConfig'; +export { PerformanceMonitor } from './component/PerformanceMonitor'; export type { Adaptable, AdaptTransforms, From d98a95b8001e28f0420d7239affb9732e3104eed Mon Sep 17 00:00:00 2001 From: LatekVo Date: Tue, 19 Mar 2024 15:05:18 +0100 Subject: [PATCH 10/19] cleaned up code --- .../component/PerformanceMonitor.tsx | 23 ++++++------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/src/reanimated2/component/PerformanceMonitor.tsx b/src/reanimated2/component/PerformanceMonitor.tsx index 0e38c9392cbb..ce1f2c78fbb1 100644 --- a/src/reanimated2/component/PerformanceMonitor.tsx +++ b/src/reanimated2/component/PerformanceMonitor.tsx @@ -3,11 +3,12 @@ import { Text, TextInput, StyleSheet, View } from 'react-native'; import React, { useEffect, useRef } from 'react'; import Animated, { + FrameInfo, SharedValue, useAnimatedProps, useFrameCallback, useSharedValue, -} from '../../..'; +} from '../../../src'; type CircularBuffer = ReturnType; function createCircularDoublesBuffer(size: number) { @@ -43,20 +44,10 @@ function createCircularDoublesBuffer(size: number) { const notEmpty = this.count > 0; return notEmpty ? this.buffer[this.next] : null; }, - - reduce(fn: (acc: T, value: number) => T, initial: T) { - let i = 0; - let acc = initial; - while (i < this.count) { - const offset = (this.next + i) % this.size; - acc = fn(acc, this.buffer[offset]); - ++i; - } - return acc; - }, }; } +const DEFAULT_BUFFER_SIZE = 60; Animated.addWhitelistedNativeProps({ text: true }); const AnimatedTextInput = Animated.createAnimatedComponent(TextInput); @@ -78,7 +69,7 @@ function loopAnimationFrame(fn: (lastTime: number, time: number) => void) { function getFps(renderTimeInMs: number): string { 'worklet'; - return ((1 / renderTimeInMs) * 1000).toFixed(1).toString(); + return (1000 / renderTimeInMs).toFixed(1); } function getTimeDelta( @@ -112,7 +103,7 @@ function completeBufferRoutine( function JsPerformance() { const jsFps = useSharedValue(null); const totalRenderTime = useSharedValue(0); - const circularBuffer = createCircularDoublesBuffer(100); + const circularBuffer = createCircularDoublesBuffer(DEFAULT_BUFFER_SIZE); useEffect(() => { loopAnimationFrame((lastTime, time) => { @@ -149,9 +140,9 @@ function UiPerformance() { const totalRenderTime = useSharedValue(0); const circularBuffer = useRef(null); - useFrameCallback(({ timestamp }) => { + useFrameCallback(({ timestamp }: FrameInfo) => { if (circularBuffer.current === null) { - circularBuffer.current = createCircularDoublesBuffer(100); + circularBuffer.current = createCircularDoublesBuffer(DEFAULT_BUFFER_SIZE); } const previousTimestamp = circularBuffer.current.front() ?? timestamp; const currentFps = completeBufferRoutine( From be0d2d8f7ffeefb3c1a6abf477b6c4b4a6e19058 Mon Sep 17 00:00:00 2001 From: LatekVo Date: Tue, 19 Mar 2024 15:31:58 +0100 Subject: [PATCH 11/19] split imports to fix compilation errors --- .../component/PerformanceMonitor.tsx | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/reanimated2/component/PerformanceMonitor.tsx b/src/reanimated2/component/PerformanceMonitor.tsx index ce1f2c78fbb1..a9ed19936579 100644 --- a/src/reanimated2/component/PerformanceMonitor.tsx +++ b/src/reanimated2/component/PerformanceMonitor.tsx @@ -1,14 +1,12 @@ -import { Text, TextInput, StyleSheet, View } from 'react-native'; - import React, { useEffect, useRef } from 'react'; +import { Text, TextInput, StyleSheet } from 'react-native'; -import Animated, { - FrameInfo, - SharedValue, - useAnimatedProps, - useFrameCallback, - useSharedValue, -} from '../../../src'; +import { SharedValue } from '../commonTypes'; +import { FrameInfo } from '../frameCallback'; +import { useSharedValue, useAnimatedProps, useFrameCallback } from '../hook'; +import { createAnimatedComponent } from '../../createAnimatedComponent'; +import { addWhitelistedNativeProps } from '../../ConfigHelper'; +import { View } from '../../Animated'; type CircularBuffer = ReturnType; function createCircularDoublesBuffer(size: number) { @@ -48,8 +46,8 @@ function createCircularDoublesBuffer(size: number) { } const DEFAULT_BUFFER_SIZE = 60; -Animated.addWhitelistedNativeProps({ text: true }); -const AnimatedTextInput = Animated.createAnimatedComponent(TextInput); +addWhitelistedNativeProps({ text: true }); +const AnimatedTextInput = createAnimatedComponent(TextInput); function loopAnimationFrame(fn: (lastTime: number, time: number) => void) { let lastTime = 0; @@ -173,10 +171,10 @@ function UiPerformance() { export function PerformanceMonitor() { return ( - + - + ); } From be654fe74ecad04ed223886be176975621ea7fee Mon Sep 17 00:00:00 2001 From: LatekVo Date: Tue, 19 Mar 2024 15:48:08 +0100 Subject: [PATCH 12/19] fixed import statements --- src/reanimated2/component/PerformanceMonitor.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/reanimated2/component/PerformanceMonitor.tsx b/src/reanimated2/component/PerformanceMonitor.tsx index a9ed19936579..d3b599bc35e9 100644 --- a/src/reanimated2/component/PerformanceMonitor.tsx +++ b/src/reanimated2/component/PerformanceMonitor.tsx @@ -1,8 +1,8 @@ import React, { useEffect, useRef } from 'react'; import { Text, TextInput, StyleSheet } from 'react-native'; -import { SharedValue } from '../commonTypes'; -import { FrameInfo } from '../frameCallback'; +import type { SharedValue } from '../commonTypes'; +import type { FrameInfo } from '../frameCallback'; import { useSharedValue, useAnimatedProps, useFrameCallback } from '../hook'; import { createAnimatedComponent } from '../../createAnimatedComponent'; import { addWhitelistedNativeProps } from '../../ConfigHelper'; @@ -15,7 +15,7 @@ function createCircularDoublesBuffer(size: number) { return { next: 0 as number, buffer: new Float32Array(size), - size: size, + size, count: 0 as number, push(value: number): number | null { @@ -114,7 +114,7 @@ function JsPerformance() { jsFps.value = currentFps; }); - }, []); + }); const animatedProps = useAnimatedProps(() => { const text = jsFps.value ?? 'N/A'; From f1a715b214716f26a47709229a7e934aa747eb9a Mon Sep 17 00:00:00 2001 From: LatekVo Date: Tue, 19 Mar 2024 16:38:37 +0100 Subject: [PATCH 13/19] resolve circular dependencies --- src/createAnimatedComponent/PropsFilter.tsx | 4 ++-- src/reanimated2/component/PerformanceMonitor.tsx | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/createAnimatedComponent/PropsFilter.tsx b/src/createAnimatedComponent/PropsFilter.tsx index 78932cfd508e..eb0ae1b399e7 100644 --- a/src/createAnimatedComponent/PropsFilter.tsx +++ b/src/createAnimatedComponent/PropsFilter.tsx @@ -1,8 +1,8 @@ 'use strict'; import { shallowEqual } from '../reanimated2/hook/utils'; -import type { StyleProps } from '../reanimated2'; -import { isSharedValue } from '../reanimated2'; +import type { StyleProps } from '../reanimated2/commonTypes'; +import { isSharedValue } from '../reanimated2/isSharedValue'; import { isChromeDebugger } from '../reanimated2/PlatformChecker'; import WorkletEventHandler from '../reanimated2/WorkletEventHandler'; import { initialUpdaterRun } from '../reanimated2/animation'; diff --git a/src/reanimated2/component/PerformanceMonitor.tsx b/src/reanimated2/component/PerformanceMonitor.tsx index d3b599bc35e9..e4d8d25eaf22 100644 --- a/src/reanimated2/component/PerformanceMonitor.tsx +++ b/src/reanimated2/component/PerformanceMonitor.tsx @@ -1,12 +1,11 @@ import React, { useEffect, useRef } from 'react'; -import { Text, TextInput, StyleSheet } from 'react-native'; +import { Text, TextInput, StyleSheet, View } from 'react-native'; -import type { SharedValue } from '../commonTypes'; import type { FrameInfo } from '../frameCallback'; +import type { SharedValue } from '../commonTypes'; import { useSharedValue, useAnimatedProps, useFrameCallback } from '../hook'; import { createAnimatedComponent } from '../../createAnimatedComponent'; import { addWhitelistedNativeProps } from '../../ConfigHelper'; -import { View } from '../../Animated'; type CircularBuffer = ReturnType; function createCircularDoublesBuffer(size: number) { From 4b2e2eea30bf0e180fb5a9e78bc62b524ef0c267 Mon Sep 17 00:00:00 2001 From: LatekVo Date: Tue, 19 Mar 2024 16:49:32 +0100 Subject: [PATCH 14/19] add 'use strict' statement --- src/reanimated2/component/PerformanceMonitor.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/reanimated2/component/PerformanceMonitor.tsx b/src/reanimated2/component/PerformanceMonitor.tsx index e4d8d25eaf22..79d378ed0399 100644 --- a/src/reanimated2/component/PerformanceMonitor.tsx +++ b/src/reanimated2/component/PerformanceMonitor.tsx @@ -1,3 +1,5 @@ +'use strict'; + import React, { useEffect, useRef } from 'react'; import { Text, TextInput, StyleSheet, View } from 'react-native'; From e40ba7f1d28bdc3d8136b71372c2bd7f6fe975b8 Mon Sep 17 00:00:00 2001 From: LatekVo Date: Thu, 21 Mar 2024 11:03:24 +0100 Subject: [PATCH 15/19] added heavy apps to performance monitor example applied other suggestions --- app/src/examples/PerfomanceMonitorExample.tsx | 69 +++++++++++++++++-- .../component/PerformanceMonitor.tsx | 2 +- 2 files changed, 66 insertions(+), 5 deletions(-) diff --git a/app/src/examples/PerfomanceMonitorExample.tsx b/app/src/examples/PerfomanceMonitorExample.tsx index 6a1dc8c9a293..7fd5ac8dd9a0 100644 --- a/app/src/examples/PerfomanceMonitorExample.tsx +++ b/app/src/examples/PerfomanceMonitorExample.tsx @@ -1,11 +1,72 @@ -import React from 'react'; -import { View } from 'react-native'; +import React, { useRef, useState } from 'react'; +import { Text, StyleSheet, Pressable, View } from 'react-native'; import { PerformanceMonitor } from 'react-native-reanimated'; +import EmptyExample from './EmptyExample'; +import BokehExample from './BokehExample'; +import PlanetsExample from './PlanetsExample'; +import EmojiWaterfallExample from './EmojiWaterfallExample'; + +enum Examples { + Empty = 'Empty Example', + Bokeh = 'Bokeh Example', + Planets = 'Planets Example', + Emojis = 'Emoji Waterfall Example', +} + export default function PerformanceMonitorExample() { + const exampleElements = useRef( + new Map([ + [Examples.Empty, ], + [Examples.Bokeh, ], + [Examples.Planets, ], + [Examples.Emojis, ], + ]) + ); + + const [currentExample, setCurrentExample] = useState(); + + const setElementByExample = (example: Examples) => { + setCurrentExample(exampleElements.current.get(example)!); + }; + return ( - + <> - + {currentExample} + + {[ + Examples.Empty, + Examples.Bokeh, + Examples.Planets, + Examples.Emojis, + ].map((element) => ( + setElementByExample(element)}> + {element} + + ))} + + ); } + +const styles = StyleSheet.create({ + buttonContainer: { + position: 'absolute', + flex: 1, + flexDirection: 'column', + gap: 4, + margin: 8, + marginLeft: 200, + }, + button: { + backgroundColor: 'lightblue', + padding: 8, + borderRadius: 8, + flex: 1, + textAlign: 'center', + }, +}); diff --git a/src/reanimated2/component/PerformanceMonitor.tsx b/src/reanimated2/component/PerformanceMonitor.tsx index 79d378ed0399..26368d60f1aa 100644 --- a/src/reanimated2/component/PerformanceMonitor.tsx +++ b/src/reanimated2/component/PerformanceMonitor.tsx @@ -172,7 +172,7 @@ function UiPerformance() { export function PerformanceMonitor() { return ( - + From 2ecbc481d7acc6f97531c0a803bc3ddf90574d48 Mon Sep 17 00:00:00 2001 From: LatekVo Date: Mon, 25 Mar 2024 12:04:23 +0100 Subject: [PATCH 16/19] fix ui measurement errors --- .../component/PerformanceMonitor.tsx | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/reanimated2/component/PerformanceMonitor.tsx b/src/reanimated2/component/PerformanceMonitor.tsx index 26368d60f1aa..280840a3f10f 100644 --- a/src/reanimated2/component/PerformanceMonitor.tsx +++ b/src/reanimated2/component/PerformanceMonitor.tsx @@ -1,6 +1,6 @@ 'use strict'; -import React, { useEffect, useRef } from 'react'; +import React, { useEffect, useMemo, useRef } from 'react'; import { Text, TextInput, StyleSheet, View } from 'react-native'; import type { FrameInfo } from '../frameCallback'; @@ -108,8 +108,8 @@ function JsPerformance() { loopAnimationFrame((lastTime, time) => { const currentFps = completeBufferRoutine( circularBuffer, - time, - lastTime, + Math.round(time), + Math.round(lastTime), totalRenderTime ); @@ -137,19 +137,23 @@ function JsPerformance() { function UiPerformance() { const uiFps = useSharedValue(null); const totalRenderTime = useSharedValue(0); - const circularBuffer = useRef(null); + const circularBuffer = useSharedValue(null); useFrameCallback(({ timestamp }: FrameInfo) => { - if (circularBuffer.current === null) { - circularBuffer.current = createCircularDoublesBuffer(DEFAULT_BUFFER_SIZE); + if (circularBuffer.value === null) { + circularBuffer.value = createCircularDoublesBuffer(DEFAULT_BUFFER_SIZE); } - const previousTimestamp = circularBuffer.current.front() ?? timestamp; + + timestamp = Math.round(timestamp); + const previousTimestamp = circularBuffer.value.front() ?? timestamp; + const currentFps = completeBufferRoutine( - circularBuffer.current, + circularBuffer.value, timestamp, previousTimestamp, totalRenderTime ); + uiFps.value = currentFps; }); From 3d3fea5d3f6eecc4af6a11c4afb949344b5fea31 Mon Sep 17 00:00:00 2001 From: LatekVo Date: Mon, 25 Mar 2024 14:30:23 +0100 Subject: [PATCH 17/19] fixed JS loops rerunning after each rerender --- .../component/PerformanceMonitor.tsx | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/reanimated2/component/PerformanceMonitor.tsx b/src/reanimated2/component/PerformanceMonitor.tsx index 280840a3f10f..f0d207286344 100644 --- a/src/reanimated2/component/PerformanceMonitor.tsx +++ b/src/reanimated2/component/PerformanceMonitor.tsx @@ -96,26 +96,32 @@ function completeBufferRoutine( const droppedDelta = getTimeDelta(nextToDrop, droppedTimestamp); totalRenderTime.value += delta - droppedDelta; + return getFps(totalRenderTime.value / buffer.count); } function JsPerformance() { const jsFps = useSharedValue(null); const totalRenderTime = useSharedValue(0); - const circularBuffer = createCircularDoublesBuffer(DEFAULT_BUFFER_SIZE); + const circularBuffer = useRef( + createCircularDoublesBuffer(DEFAULT_BUFFER_SIZE) + ); useEffect(() => { - loopAnimationFrame((lastTime, time) => { + loopAnimationFrame((_, timestamp) => { + timestamp = Math.round(timestamp); + const previousTimestamp = circularBuffer.current.front() ?? timestamp; + const currentFps = completeBufferRoutine( - circularBuffer, - Math.round(time), - Math.round(lastTime), + circularBuffer.current, + timestamp, + previousTimestamp, totalRenderTime ); jsFps.value = currentFps; }); - }); + }, []); const animatedProps = useAnimatedProps(() => { const text = jsFps.value ?? 'N/A'; From 95f8f642207fe0861a9ac4ed08e035f3226d9596 Mon Sep 17 00:00:00 2001 From: LatekVo Date: Mon, 25 Mar 2024 15:07:12 +0100 Subject: [PATCH 18/19] fix styling and eslint errors --- .../component/PerformanceMonitor.tsx | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/src/reanimated2/component/PerformanceMonitor.tsx b/src/reanimated2/component/PerformanceMonitor.tsx index f0d207286344..84edfde63d84 100644 --- a/src/reanimated2/component/PerformanceMonitor.tsx +++ b/src/reanimated2/component/PerformanceMonitor.tsx @@ -1,7 +1,7 @@ 'use strict'; -import React, { useEffect, useMemo, useRef } from 'react'; -import { Text, TextInput, StyleSheet, View } from 'react-native'; +import React, { useEffect, useRef } from 'react'; +import { TextInput, StyleSheet, View } from 'react-native'; import type { FrameInfo } from '../frameCallback'; import type { SharedValue } from '../commonTypes'; @@ -124,13 +124,12 @@ function JsPerformance() { }, []); const animatedProps = useAnimatedProps(() => { - const text = jsFps.value ?? 'N/A'; + const text = 'JS: ' + jsFps.value ?? 'N/A'; return { text, defaultValue: text }; }); return ( - JS FPS { - const text = uiFps.value ?? 'N/A'; + const text = 'UI: ' + uiFps.value ?? 'N/A'; return { text, defaultValue: text }; }); return ( - UI FPS Date: Wed, 27 Mar 2024 15:55:50 +0100 Subject: [PATCH 19/19] apply review suggestions, style improvements --- app/src/examples/PerfomanceMonitorExample.tsx | 10 +++------- src/reanimated2/component/PerformanceMonitor.tsx | 12 +++++++----- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/app/src/examples/PerfomanceMonitorExample.tsx b/app/src/examples/PerfomanceMonitorExample.tsx index 7fd5ac8dd9a0..f714756a5236 100644 --- a/app/src/examples/PerfomanceMonitorExample.tsx +++ b/app/src/examples/PerfomanceMonitorExample.tsx @@ -24,16 +24,12 @@ export default function PerformanceMonitorExample() { ]) ); - const [currentExample, setCurrentExample] = useState(); - - const setElementByExample = (example: Examples) => { - setCurrentExample(exampleElements.current.get(example)!); - }; + const [currentExample, setCurrentExample] = useState(Examples.Empty); return ( <> - {currentExample} + {exampleElements.current.get(currentExample)!} {[ Examples.Empty, @@ -44,7 +40,7 @@ export default function PerformanceMonitorExample() { setElementByExample(element)}> + onPress={() => setCurrentExample(element)}> {element} ))} diff --git a/src/reanimated2/component/PerformanceMonitor.tsx b/src/reanimated2/component/PerformanceMonitor.tsx index 84edfde63d84..7a74de184fd0 100644 --- a/src/reanimated2/component/PerformanceMonitor.tsx +++ b/src/reanimated2/component/PerformanceMonitor.tsx @@ -66,9 +66,9 @@ function loopAnimationFrame(fn: (lastTime: number, time: number) => void) { loop(); } -function getFps(renderTimeInMs: number): string { +function getFps(renderTimeInMs: number): number { 'worklet'; - return (1000 / renderTimeInMs).toFixed(1); + return 1000 / renderTimeInMs; } function getTimeDelta( @@ -84,7 +84,7 @@ function completeBufferRoutine( timestamp: number, previousTimestamp: number, totalRenderTime: SharedValue -) { +): number { 'worklet'; timestamp = Math.round(timestamp); previousTimestamp = Math.round(previousTimestamp) ?? timestamp; @@ -119,7 +119,9 @@ function JsPerformance() { totalRenderTime ); - jsFps.value = currentFps; + // JS fps have to be measured every 2nd frame, + // thus 2x multiplication has to occur here + jsFps.value = (currentFps * 2).toFixed(0); }); }, []); @@ -159,7 +161,7 @@ function UiPerformance() { totalRenderTime ); - uiFps.value = currentFps; + uiFps.value = currentFps.toFixed(0); }); const animatedProps = useAnimatedProps(() => {