Hide JS FPS on performance overlay as not accurate (#52000)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/52000

Fixes https://github.com/facebook/react-native/issues/50338

The current JS FPS value is incorrect because the frame skipping logic hasn't been reimplemented in Fabric.
As we're looking into moving this into the performance panel, I've discussed with huntie
and agreed we'll just remove the value for now to don't show inaccurate informations.

Changelog:
[Android] [Changed] - Hide JS FPS on performance overlay as not accurate

Reviewed By: huntie

Differential Revision: D76590909

fbshipit-source-id: 90b0d9c84f9aefa9197243ebb57f4e86107d6c01
This commit is contained in:
Nicola Corti
2025-06-13 14:08:01 -07:00
committed by Facebook GitHub Bot
parent 05521adbc8
commit feec8d0148
2 changed files with 28 additions and 7 deletions
@@ -33,7 +33,7 @@ internal class FpsView(reactContext: ReactContext?) : FrameLayout(reactContext!!
textView = findViewById<View>(R.id.fps_text) as TextView
frameCallback = FpsDebugFrameCallback(reactContext!!)
fpsMonitorRunnable = FPSMonitorRunnable()
setCurrentFPS(0.0, 0.0, 0, 0)
setCurrentFPS(0.0, 0.0, 0, 0, frameCallback.isRunningOnFabric)
}
override fun onAttachedToWindow() {
@@ -53,16 +53,21 @@ internal class FpsView(reactContext: ReactContext?) : FrameLayout(reactContext!!
currentFPS: Double,
currentJSFPS: Double,
droppedUIFrames: Int,
total4PlusFrameStutters: Int
total4PlusFrameStutters: Int,
runningOnFabric: Boolean
) {
val fpsString =
var fpsString =
String.format(
Locale.US,
"UI: %.1f fps\n%d dropped so far\n%d stutters (4+) so far\nJS: %.1f fps",
"UI: %.1f fps\n%d dropped so far\n%d stutters (4+) so far",
currentFPS,
droppedUIFrames,
total4PlusFrameStutters,
currentJSFPS)
total4PlusFrameStutters)
if (!runningOnFabric) {
// The JS FPS is only relevant for the legacy architecture, as Fabric we don't use
// BridgeIdleDebugListener to track JS frame drops.
fpsString += String.format(Locale.US, "\nJS: %.1f fps", currentJSFPS)
}
textView.text = fpsString
FLog.d(ReactConstants.TAG, fpsString)
}
@@ -80,7 +85,11 @@ internal class FpsView(reactContext: ReactContext?) : FrameLayout(reactContext!!
totalFramesDropped += frameCallback.expectedNumFrames - frameCallback.numFrames
total4PlusFrameStutters += frameCallback.get4PlusFrameStutters()
setCurrentFPS(
frameCallback.fps, frameCallback.jsFPS, totalFramesDropped, total4PlusFrameStutters)
frameCallback.fps,
frameCallback.jsFPS,
totalFramesDropped,
total4PlusFrameStutters,
frameCallback.isRunningOnFabric)
frameCallback.reset()
postDelayed(this, UPDATE_INTERVAL_MS.toLong())
}
@@ -65,6 +65,11 @@ internal class FpsDebugFrameCallback(private val reactContext: ReactContext) :
val uiManagerModule = reactContext.getNativeModule(UIManagerModule::class.java)
if (!reactContext.isBridgeless) {
reactContext.catalystInstance.addBridgeIdleDebugListener(didJSUpdateUiDuringFrameDetector)
isRunningOnFabric = false
} else {
// T172641976 Consider either implementing a mechanism similar to addBridgeIdleDebugListener
// for Fabric or point users to use RNDT.
isRunningOnFabric = true
}
uiManagerModule?.setViewHierarchyUpdateDebugListener(didJSUpdateUiDuringFrameDetector)
}
@@ -97,6 +102,10 @@ internal class FpsDebugFrameCallback(private val reactContext: ReactContext) :
0.0
} else numFrames.toDouble() * 1e9 / (lastFrameTime - firstFrameTime)
/**
* Please note that this value is not relevant if running on Fabric. That's because we don't
* implement addBridgeIdleDebugListener on Fabric.
*/
val jsFPS: Double
get() =
if (lastFrameTime == firstFrameTime) {
@@ -115,6 +124,9 @@ internal class FpsDebugFrameCallback(private val reactContext: ReactContext) :
return (targetFps * totalTimeMS / 1000 + 1).toInt()
}
var isRunningOnFabric = true
private set
fun get4PlusFrameStutters(): Int = fourPlusFrameStutters
private val totalTimeMS: Int