-
Notifications
You must be signed in to change notification settings - Fork 24.4k
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
Fix native animated event lag on Android #11994
Fix native animated event lag on Android #11994
Conversation
The only downside of separating the update loop into an extra method is that it does add an extra fixed size array allocation but does simplify the code a bit. |
@sahrens has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator. |
I patched this in along with #11315 and everything works great, but I don't have any context on the code - @kmagiera @ide @ryangomba @AaaChiuuu @achen1, can one of you guys give the go-ahead on this? |
I tried to merge this pull request into the Facebook internal repo but some checks failed. To unblock yourself please check the following: Does this pull request pass all open source tests on GitHub? If not please fix those. Does the code still apply cleanly on top of GitHub master? If not can please rebase. In all other cases this means some internal test failed, for example a part of a fb app won't work with this pull request. I've added the Import Failed label to this pull request so it is easy for someone at fb to find the pull request and check what failed. If you don't see anyone comment in a few days feel free to comment mentioning one of the core contributors to the project so they get a notification. |
1f44559
to
5861677
Compare
Let's ship it since it works for you @sahrens. Test plan looks good. |
@facebook-github-bot shipit |
@mkonicek has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator. |
I tried to merge this pull request into the Facebook internal repo but some checks failed. To unblock yourself please check the following: Does this pull request pass all open source tests on GitHub? If not please fix those. Does the code still apply cleanly on top of GitHub master? If not can please rebase. In all other cases this means some internal test failed, for example a part of a fb app won't work with this pull request. I've added the Import Failed label to this pull request so it is easy for someone at fb to find the pull request and check what failed. If you don't see anyone comment in a few days feel free to comment mentioning one of the core contributors to the project so they get a notification. |
@facebook-github-bot shipit |
@mkonicek has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator. |
I tried to merge this pull request into the Facebook internal repo but some checks failed. To unblock yourself please check the following: Does this pull request pass all open source tests on GitHub? If not please fix those. Does the code still apply cleanly on top of GitHub master? If not can please rebase. In all other cases this means some internal test failed, for example a part of a fb app won't work with this pull request. I've added the Import Failed label to this pull request so it is easy for someone at fb to find the pull request and check what failed. If you don't see anyone comment in a few days feel free to comment mentioning one of the core contributors to the project so they get a notification. |
@sahrens has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator. |
@janicduplessis: this is having trouble importing - maybe there is a merge conflict? Can you rebase and update? |
@sahrens Weird, doesn't look like there was any conficts, updated to master. |
There were some internal flow errors...I think I fixed them all but this is still having trouble. I'll get it through soon, I promise! |
@sahrens Any updates on this? |
@astreet said he would take a look - any progress, Andy? |
There were some internal test failures :( |
Hmm? I haven't seen this PR before but I can take a look now |
@@ -353,9 +355,47 @@ public void onEventDispatch(Event event) { | |||
*/ | |||
public void runUpdates(long frameTimeNanos) { | |||
UiThreadUtil.assertOnUiThread(); | |||
boolean hasFinishedAnimations = false; | |||
|
|||
List<AnimatedNode> nodes = new ArrayList<AnimatedNode>(mUpdatedNodes.size() + mActiveAnimations.size()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Re-allocating a new list on every frame isn't great: you can just keep a static one around to re-use.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
|
||
for (int i = 0; i < mActiveAnimations.size(); i++) { | ||
AnimationDriver animation = mActiveAnimations.valueAt(i); | ||
animation.runAnimationStep(frameTimeNanos); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not familiar with this code, but this seems odd: why are we running the animation and then updating all the nodes for the frame below on line L376? I.e., shouldn't we be updating first?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code didn't change, I just moved it in it's own method. How it works is we run the animation drivers first, which update the value nodes then update all the other nodes that are connected to these updated value nodes, which will cause props nodes to update their connected view.
@@ -334,7 +335,8 @@ public void onEventDispatch(Event event) { | |||
EventAnimationDriver eventDriver = mEventDrivers.get(event.getViewTag() + eventName); | |||
if (eventDriver != null) { | |||
event.dispatch(eventDriver); | |||
mUpdatedNodes.put(eventDriver.mValueNode.mTag, eventDriver.mValueNode); | |||
|
|||
updateNodes(Collections.singletonList((AnimatedNode) eventDriver.mValueNode)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You have an updateNodes here when you receive an event, and then in the same frame you could runUpdates and updateNodes again: is that correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are cases where this won't happen in the same frame, like during momentum scrolling, this is why we need to update it immediatly instead of relying on runUpdates which is called on each frame by the Choreographer.
These functions only update nodes that are marked as needing an update so it won't update twice in a frame.
Thanks @astreet. This is part of the stack to get sticky list headers working on Android by changing the impl to a shared `Animated` one with `useNativeDriver`. Several folks internal and external have requested it, but those internal-only Android tests were failing so I couldn't land it.
On Feb 14, 2017, at 3:51 AM, Andy Street <[email protected]<mailto:[email protected]>> wrote:
@astreet requested changes on this pull request.
________________________________
In ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java<#11994 (review)>:
@@ -353,9 +355,47 @@ public void onEventDispatch(Event event) {
*/
public void runUpdates(long frameTimeNanos) {
UiThreadUtil.assertOnUiThread();
+ boolean hasFinishedAnimations = false;
+
+ List<AnimatedNode> nodes = new ArrayList<AnimatedNode>(mUpdatedNodes.size() + mActiveAnimations.size());
Re-allocating a new list on every frame isn't great: you can just keep a static one around to re-use.
________________________________
In ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java<#11994 (review)>:
@@ -353,9 +355,47 @@ public void onEventDispatch(Event event) {
*/
public void runUpdates(long frameTimeNanos) {
UiThreadUtil.assertOnUiThread();
+ boolean hasFinishedAnimations = false;
+
+ List<AnimatedNode> nodes = new ArrayList<AnimatedNode>(mUpdatedNodes.size() + mActiveAnimations.size());
+ for (int i = 0; i < mUpdatedNodes.size(); i++) {
+ AnimatedNode node = mUpdatedNodes.valueAt(i);
+ nodes.add(node);
+ }
+
+ for (int i = 0; i < mActiveAnimations.size(); i++) {
+ AnimationDriver animation = mActiveAnimations.valueAt(i);
+ animation.runAnimationStep(frameTimeNanos);
I'm not familiar with this code, but this seems odd: why are we running the animation and then updating all the nodes for the frame below on line L376? I.e., shouldn't we be updating first?
________________________________
In ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java<#11994 (review)>:
@@ -334,7 +335,8 @@ public void onEventDispatch(Event event) {
EventAnimationDriver eventDriver = mEventDrivers.get(event.getViewTag() + eventName);
if (eventDriver != null) {
event.dispatch(eventDriver);
- mUpdatedNodes.put(eventDriver.mValueNode.mTag, eventDriver.mValueNode);
+
+ updateNodes(Collections.singletonList((AnimatedNode) eventDriver.mValueNode));
You have an updateNodes here when you receive an event, and then in the same frame you could runUpdates and updateNodes again: is that correct?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub<#11994 (review)>, or mute the thread<https://github.com/notifications/unsubscribe-auth/ABcJxxGR2t1MWZw7KHHl7yDt-Eo6KQilks5rcZUkgaJpZM4LoueJ>.
|
@astreet Updated to use a static list. |
@@ -51,6 +52,9 @@ | |||
*/ | |||
/*package*/ class NativeAnimatedNodesManager implements EventDispatcherListener { | |||
|
|||
// Used to avoid allocating a new array on every frame in runUpdates. | |||
private final static List<AnimatedNode> sRunUpdateNodeList = new ArrayList<>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you make this an instance variable unless it truly needs to be static and shared across NodesManagers?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Both would work, having it as a static ivar avoids having multiple copies in case there are multiple instances of the animated module (like in exponent). The array gets cleaned up after each invocation of runUpdates
and this always runs from the same thread so it is safe.
If you feel it should be a normal ivar I don't really mind, just saves a few bytes in rare situations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I'd prefer an ivar in case someone somewhere decides to have multiple RN instances running at the same time :)
@@ -51,6 +52,9 @@ | |||
*/ | |||
/*package*/ class NativeAnimatedNodesManager implements EventDispatcherListener { | |||
|
|||
// Used to avoid allocating a new array on every frame in runUpdates. | |||
private final static List<AnimatedNode> sRunUpdateNodeList = new ArrayList<>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I'd prefer an ivar in case someone somewhere decides to have multiple RN instances running at the same time :)
@astreet Updated to an ivar |
@astreet has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator. |
Summary: Native animated events sometimes end up lagging a frame behind on android because we perform the update in the normal animation loop instead of doing it immediately when we receive the event. We had the same issue on iOS and was fixed in a similar way. Moved some code around to have a method that updates a list of node that we can use to update the node in the animated event handler and also use it in the animation update loop. **Test plan** Tested that it did fix sticky headers lagging a frame behind during momentum scrolling in my PR facebook#11315 and also tested the native animations examples still work properly. Closes facebook#11994 Reviewed By: mkonicek Differential Revision: D4488977 Pulled By: sahrens fbshipit-source-id: 831a1565bc7b8fa88cadd5a8c1be876fbdefbf66
Summary: Native animated events sometimes end up lagging a frame behind on android because we perform the update in the normal animation loop instead of doing it immediately when we receive the event. We had the same issue on iOS and was fixed in a similar way. Moved some code around to have a method that updates a list of node that we can use to update the node in the animated event handler and also use it in the animation update loop. **Test plan** Tested that it did fix sticky headers lagging a frame behind during momentum scrolling in my PR facebook#11315 and also tested the native animations examples still work properly. Closes facebook#11994 Reviewed By: mkonicek Differential Revision: D4488977 Pulled By: sahrens fbshipit-source-id: 831a1565bc7b8fa88cadd5a8c1be876fbdefbf66
Summary: Native animated events sometimes end up lagging a frame behind on android because we perform the update in the normal animation loop instead of doing it immediately when we receive the event. We had the same issue on iOS and was fixed in a similar way. Moved some code around to have a method that updates a list of node that we can use to update the node in the animated event handler and also use it in the animation update loop. **Test plan** Tested that it did fix sticky headers lagging a frame behind during momentum scrolling in my PR facebook#11315 and also tested the native animations examples still work properly. Closes facebook#11994 Reviewed By: mkonicek Differential Revision: D4488977 Pulled By: sahrens fbshipit-source-id: 831a1565bc7b8fa88cadd5a8c1be876fbdefbf66
Native animated events sometimes end up lagging a frame behind on android because we perform the update in the normal animation loop instead of doing it immediately when we receive the event. We had the same issue on iOS and was fixed in a similar way.
Moved some code around to have a method that updates a list of node that we can use to update the node in the animated event handler and also use it in the animation update loop.
Test plan
Tested that it did fix sticky headers lagging a frame behind during momentum scrolling in my PR #11315 and also tested the native animations examples still work properly.