Skip to content
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

Skip flushing animation-frame callbacks if update was already run for a given frame #4099

Merged
merged 3 commits into from Feb 24, 2023

Conversation

kmagiera
Copy link
Member

Summary

This PR fixes a perf regression introduced in #3722. The issue was that we'd schedule a native request animation frame callback several times in a single frame. As a result we'd run mapper updates more than once per frame which resulted in UI thread skipping frames. The bug would surface in examples where we had both timing (active) and gesture driven animations. In such scenario we'd enqueue native requestAnimationFrame callback twice – first time for the running timing animation, and the second time as a result of handling the touch event. These two callbacks would then run as long as there are update happening and would incur additional cost to all updates happening on the UI thread. Moreover, for each new gesture started afterwards we'd add a new call to requestAnimationFrame and yet another time the updates would be executed withing a single frame. After several times of launching new stream of events, we'd end up with a huge number of repeated work being done in a single frame which was causing significant frame drops.

This PR addresses this problem by preventing requestAnimationFrame callbacks to be flushed more than once in a single frame. We do this by remembering if the previous flush was caused by the native rAF callback or was it forced by other activity (i.e. event handling). Then we only allow native rAF callback to perform a flush if it doesn't immediately follow a non-native flush. This way we don't perform additional work in a frame when the updates are already triggered by the event.

Test plan

Below is a link to a minimum repro of this issue. The app runs an infinite animation and renders a draggable circle. After running this app for a while and pulling the circle several times the performance starts to drop.
https://gist.github.com/kmagiera/b2df85f9512951f5e6ceee7bc569f5f1

@kmagiera kmagiera added this pull request to the merge queue Feb 24, 2023
Merged via the queue into main with commit d9a55c5 Feb 24, 2023
@kmagiera kmagiera deleted the fix-setimmediate-perf-regression branch February 24, 2023 13:11
fluiddot pushed a commit to wordpress-mobile/react-native-reanimated that referenced this pull request Jun 5, 2023
… a given frame (software-mansion#4099)

## Summary

This PR fixes a perf regression introduced in software-mansion#3722. The issue was that
we'd schedule a native request animation frame callback several times in
a single frame. As a result we'd run mapper updates more than once per
frame which resulted in UI thread skipping frames. The bug would surface
in examples where we had both timing (active) and gesture driven
animations. In such scenario we'd enqueue native requestAnimationFrame
callback twice – first time for the running timing animation, and the
second time as a result of handling the touch event. These two callbacks
would then run as long as there are update happening and would incur
additional cost to all updates happening on the UI thread. Moreover, for
each new gesture started afterwards we'd add a new call to
requestAnimationFrame and yet another time the updates would be executed
withing a single frame. After several times of launching new stream of
events, we'd end up with a huge number of repeated work being done in a
single frame which was causing significant frame drops.

This PR addresses this problem by preventing requestAnimationFrame
callbacks to be flushed more than once in a single frame. We do this by
remembering if the previous flush was caused by the native rAF callback
or was it forced by other activity (i.e. event handling). Then we only
allow native rAF callback to perform a flush if it doesn't immediately
follow a non-native flush. This way we don't perform additional work in
a frame when the updates are already triggered by the event.

## Test plan

Below is a link to a minimum repro of this issue. The app runs an
infinite animation and renders a draggable circle. After running this
app for a while and pulling the circle several times the performance
starts to drop.
https://gist.github.com/kmagiera/b2df85f9512951f5e6ceee7bc569f5f1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants