-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
FPSCounter #5770
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
Merged
Merged
FPSCounter #5770
Changes from 18 commits
Commits
Show all changes
25 commits
Select commit
Hold shift + click to select a range
dfc3894
WIP
jgonet 2c21257
WIP
jgonet 6f658ef
Merge branch 'main' into jgonet/fps
piaskowyk 08f8554
Restore Podfile.lock
piaskowyk 5e9ec74
fix floating point error
latekvo f69861f
Add circularBuffer to JS fps counter to avoid fps spikes.
latekvo 89a823f
Merge branch 'main' into jgonet/fps
latekvo ce2e981
Visual improvements, remove jittering. Ran prettier.
latekvo 74d7814
Extracted ui and js logic into a separate function.
latekvo b1611bf
save before refactor
latekvo 0633742
Moved performance monitor to a separate component.
latekvo d98a95b
cleaned up code
latekvo be0d2d8
split imports to fix compilation errors
latekvo 64a90a3
Merge branch 'main' into jgonet/fps
latekvo be654fe
fixed import statements
latekvo b44f1f8
Merge branch 'jgonet/fps' of https://github.com/software-mansion/reac…
latekvo f1a715b
resolve circular dependencies
latekvo 4b2e2ee
add 'use strict' statement
latekvo e40ba7f
added heavy apps to performance monitor example
latekvo 2ecbc48
fix ui measurement errors
latekvo 3d3fea5
fixed JS loops rerunning after each rerender
latekvo 8016d92
Merge branch 'main' into jgonet/fps
latekvo 95f8f64
fix styling and eslint errors
latekvo 17249c5
Merge branch 'jgonet/fps' of https://github.com/software-mansion/reac…
latekvo 18cd82b
apply review suggestions, style improvements
latekvo File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| import React from 'react'; | ||
| import { View } from 'react-native'; | ||
| import { PerformanceMonitor } from 'react-native-reanimated'; | ||
|
|
||
| export default function PerformanceMonitorExample() { | ||
| return ( | ||
| <View> | ||
| <PerformanceMonitor /> | ||
| </View> | ||
| ); | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,203 @@ | ||
| 'use strict'; | ||
|
|
||
| import React, { useEffect, useRef } from 'react'; | ||
| import { Text, TextInput, StyleSheet, View } from 'react-native'; | ||
|
|
||
| import type { FrameInfo } from '../frameCallback'; | ||
| import type { SharedValue } from '../commonTypes'; | ||
| import { useSharedValue, useAnimatedProps, useFrameCallback } from '../hook'; | ||
| import { createAnimatedComponent } from '../../createAnimatedComponent'; | ||
| import { addWhitelistedNativeProps } from '../../ConfigHelper'; | ||
|
|
||
| type CircularBuffer = ReturnType<typeof createCircularDoublesBuffer>; | ||
| function createCircularDoublesBuffer(size: number) { | ||
| 'worklet'; | ||
|
|
||
| return { | ||
| next: 0 as number, | ||
| buffer: new Float32Array(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 notEmpty = this.count > 0; | ||
| if (notEmpty) { | ||
| const current = this.next - 1; | ||
| const index = current < 0 ? this.size - 1 : current; | ||
| return this.buffer[index]; | ||
| } | ||
| return null; | ||
| }, | ||
|
|
||
| back(): number | null { | ||
| const notEmpty = this.count > 0; | ||
| return notEmpty ? this.buffer[this.next] : null; | ||
| }, | ||
| }; | ||
| } | ||
|
|
||
| const DEFAULT_BUFFER_SIZE = 60; | ||
| addWhitelistedNativeProps({ text: true }); | ||
| const AnimatedTextInput = 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 getFps(renderTimeInMs: number): string { | ||
| 'worklet'; | ||
| return (1000 / renderTimeInMs).toFixed(1); | ||
| } | ||
|
|
||
| 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<number> | ||
| ) { | ||
| '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<string | null>(null); | ||
| const totalRenderTime = useSharedValue(0); | ||
| const circularBuffer = createCircularDoublesBuffer(DEFAULT_BUFFER_SIZE); | ||
|
|
||
| useEffect(() => { | ||
| loopAnimationFrame((lastTime, time) => { | ||
| const currentFps = completeBufferRoutine( | ||
| circularBuffer, | ||
| time, | ||
| lastTime, | ||
| totalRenderTime | ||
| ); | ||
|
|
||
| jsFps.value = currentFps; | ||
| }); | ||
| }); | ||
|
|
||
| const animatedProps = useAnimatedProps(() => { | ||
| const text = jsFps.value ?? 'N/A'; | ||
| return { text, defaultValue: text }; | ||
| }); | ||
|
|
||
| return ( | ||
| <View style={styles.container}> | ||
| <Text style={styles.headers}>JS FPS</Text> | ||
| <AnimatedTextInput | ||
| style={styles.text} | ||
| animatedProps={animatedProps} | ||
| editable={false} | ||
| /> | ||
| </View> | ||
| ); | ||
| } | ||
|
|
||
| function UiPerformance() { | ||
| const uiFps = useSharedValue<string | null>(null); | ||
| const totalRenderTime = useSharedValue(0); | ||
| const circularBuffer = useRef<CircularBuffer | null>(null); | ||
|
|
||
| useFrameCallback(({ timestamp }: FrameInfo) => { | ||
| if (circularBuffer.current === null) { | ||
| circularBuffer.current = createCircularDoublesBuffer(DEFAULT_BUFFER_SIZE); | ||
| } | ||
| const previousTimestamp = circularBuffer.current.front() ?? timestamp; | ||
| const currentFps = completeBufferRoutine( | ||
| circularBuffer.current, | ||
| timestamp, | ||
| previousTimestamp, | ||
| totalRenderTime | ||
| ); | ||
| uiFps.value = currentFps; | ||
| }); | ||
|
|
||
| const animatedProps = useAnimatedProps(() => { | ||
| const text = uiFps.value ?? 'N/A'; | ||
| return { text, defaultValue: text }; | ||
| }); | ||
|
|
||
| return ( | ||
| <View style={styles.container}> | ||
| <Text style={styles.headers}>UI FPS</Text> | ||
| <AnimatedTextInput | ||
| style={styles.text} | ||
| animatedProps={animatedProps} | ||
| editable={false} | ||
| /> | ||
| </View> | ||
| ); | ||
| } | ||
|
|
||
| export function PerformanceMonitor() { | ||
| return ( | ||
| <View style={[styles.monitor]}> | ||
|
latekvo marked this conversation as resolved.
Outdated
|
||
| <JsPerformance /> | ||
| <UiPerformance /> | ||
| </View> | ||
| ); | ||
| } | ||
|
|
||
| const styles = StyleSheet.create({ | ||
| monitor: { | ||
| flexDirection: 'row', | ||
| gap: 8, | ||
| borderWidth: 1, | ||
| padding: 8, | ||
| position: 'absolute', | ||
| backgroundColor: '#fffa', | ||
| zIndex: 1000, | ||
| }, | ||
| headers: { | ||
| fontSize: 12, | ||
| }, | ||
| text: { | ||
| fontSize: 16, | ||
| }, | ||
| container: { | ||
| width: 40, | ||
| alignItems: 'center', | ||
| justifyContent: 'center', | ||
| }, | ||
| }); | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.