@vyshnav18/react-native-fps-counter 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +101 -0
- package/android/.gradle/9.2.0/checksums/checksums.lock +0 -0
- package/android/.gradle/9.2.0/checksums/md5-checksums.bin +0 -0
- package/android/.gradle/9.2.0/checksums/sha1-checksums.bin +0 -0
- package/android/.gradle/9.2.0/fileChanges/last-build.bin +0 -0
- package/android/.gradle/9.2.0/fileHashes/fileHashes.bin +0 -0
- package/android/.gradle/9.2.0/fileHashes/fileHashes.lock +0 -0
- package/android/.gradle/9.2.0/gc.properties +0 -0
- package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
- package/android/.gradle/buildOutputCleanup/cache.properties +2 -0
- package/android/.gradle/vcs-1/gc.properties +0 -0
- package/android/build/reports/problems/problems-report.html +659 -0
- package/android/build.gradle +64 -0
- package/android/src/main/AndroidManifest.xml +3 -0
- package/android/src/main/java/com/rnfpscounter/FpsCounterModule.kt +120 -0
- package/android/src/main/java/com/rnfpscounter/FpsCounterPackage.kt +16 -0
- package/dist/index.d.mts +110 -0
- package/dist/index.d.ts +110 -0
- package/dist/index.js +192 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +188 -0
- package/dist/index.mjs.map +1 -0
- package/ios/FpsCounterModule.h +12 -0
- package/ios/FpsCounterModule.mm +123 -0
- package/ios/RNFpsCounter.podspec +21 -0
- package/package.json +73 -0
- package/react-native.config.js +14 -0
- package/src/FpsCounter.tsx +190 -0
- package/src/index.ts +10 -0
- package/src/specs/NativeFpsCounter.ts +26 -0
- package/src/useFPSMetric.ts +82 -0
- package/src/useUIFPSMetric.ts +81 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { TurboModule } from 'react-native';
|
|
2
|
+
import { TurboModuleRegistry } from 'react-native';
|
|
3
|
+
|
|
4
|
+
export interface Spec extends TurboModule {
|
|
5
|
+
/**
|
|
6
|
+
* Start monitoring UI thread FPS
|
|
7
|
+
*/
|
|
8
|
+
startMonitoring(): void;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Stop monitoring UI thread FPS
|
|
12
|
+
*/
|
|
13
|
+
stopMonitoring(): void;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Required for EventEmitter support
|
|
17
|
+
*/
|
|
18
|
+
addListener(eventName: string): void;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Required for EventEmitter support
|
|
22
|
+
*/
|
|
23
|
+
removeListeners(count: number): void;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export default TurboModuleRegistry.getEnforcing<Spec>('FpsCounter');
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
type FrameData = {
|
|
4
|
+
fps: number;
|
|
5
|
+
lastStamp: number;
|
|
6
|
+
framesCount: number;
|
|
7
|
+
average: number;
|
|
8
|
+
totalCount: number;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export type FPS = {
|
|
12
|
+
average: FrameData['average'];
|
|
13
|
+
fps: FrameData['fps'];
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* A custom hook that tracks frames per second (FPS) in React Native.
|
|
18
|
+
* Uses requestAnimationFrame to count frames and updates at most once per second.
|
|
19
|
+
*
|
|
20
|
+
* @returns An object containing the current FPS and rolling average FPS
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```tsx
|
|
24
|
+
* const { fps, average } = useFPSMetric();
|
|
25
|
+
* console.log(`Current: ${fps} FPS, Average: ${average.toFixed(2)} FPS`);
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export function useFPSMetric(): FPS {
|
|
29
|
+
const [frameState, setFrameState] = useState<FrameData>({
|
|
30
|
+
fps: 0,
|
|
31
|
+
lastStamp: Date.now(),
|
|
32
|
+
framesCount: 0,
|
|
33
|
+
average: 0,
|
|
34
|
+
totalCount: 0,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
// NOTE: timeout is here because requestAnimationFrame is deferred
|
|
39
|
+
// and to prevent setStates when unmounted
|
|
40
|
+
let timeout: ReturnType<typeof setTimeout> | null = null;
|
|
41
|
+
|
|
42
|
+
requestAnimationFrame((): void => {
|
|
43
|
+
timeout = setTimeout((): void => {
|
|
44
|
+
const currentStamp = Date.now();
|
|
45
|
+
const shouldSetState = currentStamp - frameState.lastStamp > 1000;
|
|
46
|
+
const newFramesCount = frameState.framesCount + 1;
|
|
47
|
+
|
|
48
|
+
// Updates FPS at most once per second
|
|
49
|
+
if (shouldSetState) {
|
|
50
|
+
const newValue = frameState.framesCount;
|
|
51
|
+
const totalCount = frameState.totalCount + 1;
|
|
52
|
+
|
|
53
|
+
// Math.min is used because values over 60 aren't really important
|
|
54
|
+
// Mean FPS is calculated incrementally instead of storing all values
|
|
55
|
+
const newMean = Math.min(
|
|
56
|
+
frameState.average + (newValue - frameState.average) / totalCount,
|
|
57
|
+
60
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
setFrameState({
|
|
61
|
+
fps: frameState.framesCount,
|
|
62
|
+
lastStamp: currentStamp,
|
|
63
|
+
framesCount: 0,
|
|
64
|
+
average: newMean,
|
|
65
|
+
totalCount,
|
|
66
|
+
});
|
|
67
|
+
} else {
|
|
68
|
+
setFrameState({
|
|
69
|
+
...frameState,
|
|
70
|
+
framesCount: newFramesCount,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}, 0);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
return () => {
|
|
77
|
+
if (timeout) clearTimeout(timeout);
|
|
78
|
+
};
|
|
79
|
+
}, [frameState]);
|
|
80
|
+
|
|
81
|
+
return { average: frameState.average, fps: frameState.fps };
|
|
82
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { useEffect, useState, useRef } from 'react';
|
|
2
|
+
import { NativeModules, NativeEventEmitter, Platform } from 'react-native';
|
|
3
|
+
|
|
4
|
+
const FpsCounterModule = NativeModules.FpsCounter;
|
|
5
|
+
|
|
6
|
+
type UIFrameData = {
|
|
7
|
+
uiFps: number;
|
|
8
|
+
uiAverage: number;
|
|
9
|
+
isAvailable: boolean;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type UIFPS = {
|
|
13
|
+
uiFps: number;
|
|
14
|
+
uiAverage: number;
|
|
15
|
+
/** Whether the native module is available */
|
|
16
|
+
isAvailable: boolean;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* A custom hook that tracks native UI thread frames per second.
|
|
21
|
+
* Uses CADisplayLink on iOS and Choreographer on Android.
|
|
22
|
+
*
|
|
23
|
+
* @returns An object containing the current UI FPS, rolling average, and availability status
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```tsx
|
|
27
|
+
* const { uiFps, uiAverage, isAvailable } = useUIFPSMetric();
|
|
28
|
+
* if (isAvailable) {
|
|
29
|
+
* console.log(`UI Thread: ${uiFps} FPS`);
|
|
30
|
+
* }
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export function useUIFPSMetric(): UIFPS {
|
|
34
|
+
const [frameData, setFrameData] = useState<UIFrameData>({
|
|
35
|
+
uiFps: 0,
|
|
36
|
+
uiAverage: 0,
|
|
37
|
+
isAvailable: !!FpsCounterModule,
|
|
38
|
+
});
|
|
39
|
+
const eventEmitterRef = useRef<NativeEventEmitter | null>(null);
|
|
40
|
+
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
// Handle case where native module is not available
|
|
43
|
+
if (!FpsCounterModule) {
|
|
44
|
+
if (__DEV__) {
|
|
45
|
+
console.warn(
|
|
46
|
+
'[react-native-fps-counter] Native module not found.\n' +
|
|
47
|
+
'Make sure you have run:\n' +
|
|
48
|
+
' - iOS: cd ios && pod install\n' +
|
|
49
|
+
' - Android: Rebuild the app (npx react-native run-android)\n' +
|
|
50
|
+
'If using Expo, you need to create a development build.'
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Create event emitter
|
|
57
|
+
eventEmitterRef.current = new NativeEventEmitter(FpsCounterModule);
|
|
58
|
+
|
|
59
|
+
// Subscribe to FPS updates
|
|
60
|
+
const subscription = eventEmitterRef.current.addListener(
|
|
61
|
+
'onUIFpsUpdate',
|
|
62
|
+
(data: { fps: number; average: number }) => {
|
|
63
|
+
setFrameData({
|
|
64
|
+
uiFps: Math.round(data.fps),
|
|
65
|
+
uiAverage: data.average,
|
|
66
|
+
isAvailable: true,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
// Start monitoring
|
|
72
|
+
FpsCounterModule.startMonitoring();
|
|
73
|
+
|
|
74
|
+
return () => {
|
|
75
|
+
subscription.remove();
|
|
76
|
+
FpsCounterModule.stopMonitoring();
|
|
77
|
+
};
|
|
78
|
+
}, []);
|
|
79
|
+
|
|
80
|
+
return frameData;
|
|
81
|
+
}
|