Repository navigation
Budget Android pre-mounting by the display's frame, not a 60 Hz one - #59057
Open
YevheniiKotyrlo wants to merge 3 commits into
Open
YevheniiKotyrlo wants to merge 3 commits into
YevheniiKotyrlo wants to merge 3 commits into
Conversation
MountItemDispatcher stopped pre-mounting views at half of a 60 Hz frame (8.33 ms) on every display: all of a 120 Hz frame and more than a 144 Hz one. It now pre-mounts for half of the shortest interval between the frames it has run on, which is the display's frame at its fastest refresh rate, and never for longer than before. The loop reads React Native's SystemClock so MountItemDispatcherTest can drive it.
YevheniiKotyrlo
marked this pull request as ready for review
October 14, 2026 11:36
Contributor
|
The refresh rate can change dynamically, so forever locking to the smallest time may be suboptimal - can we try to use the screen's current refresh rate? |
The shortest frame interval seen so far stays at the display's highest rate, so after a drop from 120 Hz back to 60 Hz pre-mounting keeps a 120 Hz budget, half of what the frame allows. DisplayFrameInterval reads the default display's refresh rate from DisplayManager when the display reports a change, never per frame, and MountItemDispatcher pre-mounts for half of that frame.
Contributor
Author
|
Updated in 35f8355: pre-mounting now takes half of the default display's current frame, read from |
Half of the display's frame grows below 60 Hz, to 500 ms at the 1 Hz an adaptive panel can idle at, so the budget is now the shorter of half the display's frame and half of a 60 Hz frame. A display that reports no refresh rate gets half of a 60 Hz frame rather than an assumed rate.
Contributor
Author
|
Follow-up in 3d780fb: below 60 Hz the budget stays at today's 8.33 ms instead of half of the longer frame, since an adaptive panel idling at 1 Hz would otherwise pre-mount for 500 ms, and a display that reports no refresh rate gets 8.33 ms as well. |
This branch has not been deployed
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Summary:
MountItemDispatcher.dispatchPreMountItemsstops pre-mounting atframeTimeNanos + FRAME_TIME_NS / 2, whereFRAME_TIME_NSis a 60 Hz frame. That budget is 8.33 ms on every display: all of a 120 Hz frame and more than a 144 Hz one, so a frame that pre-mounts a large backlog on a fast display spends its whole budget before it traverses.The dispatcher now pre-mounts for half of the default display's current frame, and never longer than half of a 60 Hz frame: a display running below 60 Hz, such as an adaptive panel idling at 1 Hz, keeps today's 8.33 ms instead of half of its long frame.
DisplayFrameIntervalreads the refresh rate fromDisplayManagerwhenFabricUIManagerinitializes and again whenever the display reports a change, so a frame reads a cached interval rather than the display; a display that reports no refresh rate gets 8.33 ms as well. The loop readscom.facebook.react.common.SystemClockso a test can drive it.Changelog:
[ANDROID] [FIXED] - Budget view pre-mounting by the display's current refresh rate instead of a 60 Hz frame
Test Plan:
MountItemDispatcherTest(new) covers the budget at 60, 90, 120, 144, 165, 185 and 240 Hz, at 1, 10, 24, 30 and 48 Hz, while the display reports no refresh rate, after the display drops from 120 Hz back to 60 Hz, and across a skipped frame.DisplayFrameIntervalTest(new) covers reading the default display's frame from 1 Hz to 240 Hz, following a refresh-rate change the display reports, keeping the last frame once stopped, and reporting no frame while the display reports no refresh rate.Every queued item takes 1 ms. With the 60 Hz budget, the cases above 60 Hz fail:
expected:<{60=9, 90=[6, 120=5, 144=4, 165=4, 185=3, 240=3]}> but was:<{60=9, 90=[9, 120=9, 144=9, 165=9, 185=9, 240=9]}>. Without the 8.33 ms ceiling, the cases below 60 Hz fail:expected:<{1=[9, 10=9, 24=9, 30=9, 48=9]}> but was:<{1=[20, 10=20, 24=20, 30=17, 48=11]}>../gradlew :packages:react-native:ReactAndroid:testDebugUnitTest -PenableWarningsAsErrors=true: 730 tests, 0 failures./gradlew :packages:react-native:ReactAndroid:compileReleaseKotlin :packages:react-native:ReactAndroid:compileReleaseJavaWithJavac: cleannode ./scripts/format-kotlin.js --checkandnode ./scripts/format-java.js --check: cleanI didn't run the full Android
build, the template and HelloWorld app builds, or the Android e2e jobs locally.