-
Notifications
You must be signed in to change notification settings - Fork 24.6k
Shadows are causing fps drops during animations on IOS #49128
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
Comments
I can reproduce on my side. Physical iPhone 12 |
FYI @joevilches for when back from PTO who worked on iOS impl of box shadow In the past we talked about using CALayer shadows when we had a happy path to do so. But there might also just be extra work we are doing on redraw that we shouldn't be. |
Acknowledging I saw this, thanks for the report! There is another iOS box shadow issue reported at #49134 which I will tackle before this one. |
Hi, @joevilches do you have any updates on this issue? Our iOS app have been stack on old shadows api for a month now, would really appreciate if you could fix this issue 🙏 |
Just ran into this too. Please let me know if there's anything I can do to help get this through the finish line. :) |
Hey thanks for the check ins! Yeah so update here is that I took a look at the issue and can confirm I am seeing it too (ty for providing repro). With the specific animation being used (changing height/width of shadow), there will have to be redrawing, which is unfortunate and slow. That being said I think there may be a path forward which would involve changing how RN does its layer drawing to hook into https://developer.apple.com/documentation/quartzcore/calayer/display()?language=objc on a specific layer (i.e. utilize UIKit's drawing lifecycle which might provide some optimizations). I think this would be quite challenging as some of RCTViewComponentView will need to change to get this to work which might lead to having to migrate other layers to use this as well (borders, background color, filter, etc). I am currently working on improving some accessibility APIs we have, so cannot prioritize this at the moment. I have some spare time in early April I can try to tackle this then! Also, we could implement a fast-case were we use iOS shadow APIs in the cases where we can use them (not inset, no spread), but it seems that might not suffice given you mentioned you are using the old shadows currently. |
@joevilches, thanks for the detailed explanation! In my situation, I notice a performance drop even when I'm not directly animating the element with the shadow. The shadowed element stays the same, I only animate its child elements. Despite this, it seems like the shadow is still being redrawn unnecessarily. Would it be as difficult to eliminate these unnecessary redraws as it is to optimize the animation of the element with the shadow? |
@joevilches A workaround I found for my situation (where the shadow-casting container is not animated, only its children), is to create a dedicated shadow caster inside the container as a sibling to the animated children, in which case performance is unaffected. I think as a low hanging fruit, it would be nice to have a builtin optimization at least for this case (which I imagine to be a common case). Before (bad): <View style={{ boxShadow: '...' }}>
<Animated.View>...</Animated.View>
</View> After (good): <View>
<View style={{ ...StyleSheet.absoluteFillObject, boxShadow: '...' }}>
<Animated.View>...</Animated.View>
</View> |
@AndrewPrifer thanks for the info this is interesting and will help a ton when I go to tackle this. Glad you were able to find a workaround but I agree this case should be able to work performantly with your before code |
Summary: A while ago someone on GitHub reported that box shadow on iOS was causing frame drops when animating a large, pretty blurry shadow: facebook#49128. This week I finally got around to fixing this! The slowness was happening since we were using CG to draw this shadow, which is very CPU intensive and, to my knowledge, does not take advantage of GPUs to do anything. Couple that with an animating, large, blurry shadow and we have frame issues. These shadows were taking very long to draw, to get the image of the shadow (which then needs to be copied into some texture 3 times as big, composited, put on the screen etc) took 12-14ms :o, thats very slow. To fix this I figured out how to get CA's shadow APIs working, which take advantage of the GPU. The enable inset shadows and spread you have to get creative with a mix of `shadowPath` and `mask` with a `CAShapeLayer`, but we got it done! Things are much faster, I am not sure how to time this but using a real device shows no frame drops :D Changelog: [iOS][Fixed] - Box shadows on iOS are faster Differential Revision: D72823334
It's early April and that means we are making shadows faster! I don't wanna be too hasty since my PR still needs review from a peer, but things are much faster with this new implementation! before: after: The implementation I went with was different than the one I guessed above. I tried that and while it did improve the speed quite a bit, there was still very noticeable frame drops. The implementation I landed on uses CA layer shadow APIs, which are the same ones used by these guys: https://reactnative.dev/docs/shadow-props#shadowcolor. TLDR, its probably the fastest we can get this since we are using Apple's shadow APIs now and not doing our own custom drawing :D Hopefully there is no glaring hole in this implementation but I tested quite a few cases and it all seems to be the same, with some small visual clarity wins compared to the old impl. Hope this helps! |
Summary: A while ago someone on GitHub reported that box shadow on iOS was causing frame drops when animating a large, pretty blurry shadow: facebook#49128. This week I finally got around to fixing this! The slowness was happening since we were using CG to draw this shadow, which is very CPU intensive and, to my knowledge, does not take advantage of GPUs to do anything. Couple that with an animating, large, blurry shadow and we have frame issues. These shadows were taking very long to draw, to get the image of the shadow (which then needs to be copied into some texture 3 times as big, composited, put on the screen etc) took 12-14ms :o, thats very slow. To fix this I figured out how to get CA's shadow APIs working, which take advantage of the GPU. The enable inset shadows and spread you have to get creative with a mix of `shadowPath` and `mask` with a `CAShapeLayer`, but we got it done! Things are much faster, I am not sure how to time this but using a real device shows no frame drops :D Changelog: [iOS][Fixed] - Box shadows on iOS are faster Reviewed By: lenaic Differential Revision: D72823334
Summary: Pull Request resolved: facebook#50636 A while ago someone on GitHub reported that box shadow on iOS was causing frame drops when animating a large, pretty blurry shadow: facebook#49128. This week I finally got around to fixing this! The slowness was happening since we were using CG to draw this shadow, which is very CPU intensive and, to my knowledge, does not take advantage of GPUs to do anything. Couple that with an animating, large, blurry shadow and we have frame issues. These shadows were taking very long to draw, to get the image of the shadow (which then needs to be copied into some texture 3 times as big, composited, put on the screen etc) took 12-14ms :o, thats very slow. To fix this I figured out how to get CA's shadow APIs working, which take advantage of the GPU. The enable inset shadows and spread you have to get creative with a mix of `shadowPath` and `mask` with a `CAShapeLayer`, but we got it done! Things are much faster, I am not sure how to time this but using a real device shows no frame drops :D Changelog: [iOS][Fixed] - Box shadows on iOS are faster Reviewed By: lenaic Differential Revision: D72823334
@joevilches awesome, thank you so much for your work!! 🚀🚀 Just to make sure, since you said you changed from custom drawing to CA layer shadows, does it still conform to the box shadow spec? E.g. does it handle transparency the same way (not showing through, and view transparency doesn't determine shadow strength)? |
@AndrewPrifer I tested it on a large range of values, and it seems to conform as much as the old implementation did
What do you mean by this?
If the |
Summary: Pull Request resolved: #50636 A while ago someone on GitHub reported that box shadow on iOS was causing frame drops when animating a large, pretty blurry shadow: #49128. This week I finally got around to fixing this! The slowness was happening since we were using CG to draw this shadow, which is very CPU intensive and, to my knowledge, does not take advantage of GPUs to do anything. Couple that with an animating, large, blurry shadow and we have frame issues. These shadows were taking very long to draw, to get the image of the shadow (which then needs to be copied into some texture 3 times as big, composited, put on the screen etc) took 12-14ms :o, thats very slow. To fix this I figured out how to get CA's shadow APIs working, which take advantage of the GPU. The enable inset shadows and spread you have to get creative with a mix of `shadowPath` and `mask` with a `CAShapeLayer`, but we got it done! Things are much faster, I am not sure how to time this but using a real device shows no frame drops :D Changelog: [iOS][Fixed] - Box shadows on iOS are faster Reviewed By: lenaic Differential Revision: D72823334 fbshipit-source-id: 460339c9d77e7423ce59a1a9178b6b3ad527e4b0
Summary: Pull Request resolved: facebook#50636 A while ago someone on GitHub reported that box shadow on iOS was causing frame drops when animating a large, pretty blurry shadow: facebook#49128. This week I finally got around to fixing this! The slowness was happening since we were using CG to draw this shadow, which is very CPU intensive and, to my knowledge, does not take advantage of GPUs to do anything. Couple that with an animating, large, blurry shadow and we have frame issues. These shadows were taking very long to draw, to get the image of the shadow (which then needs to be copied into some texture 3 times as big, composited, put on the screen etc) took 12-14ms :o, thats very slow. To fix this I figured out how to get CA's shadow APIs working, which take advantage of the GPU. The enable inset shadows and spread you have to get creative with a mix of `shadowPath` and `mask` with a `CAShapeLayer`, but we got it done! Things are much faster, I am not sure how to time this but using a real device shows no frame drops :D Changelog: [iOS][Fixed] - Box shadows on iOS are faster Reviewed By: lenaic Differential Revision: D72823334 fbshipit-source-id: 460339c9d77e7423ce59a1a9178b6b3ad527e4b0
Description
Shadows are causing fps drops during animations on IOS. Reproduction uses Expo for ease of use, this bug happens in bare projects without expo. The Reproduction uses react-native 0.76.6, but the problem exists with react-native 0.77 too
Steps to reproduce
React Native Version
0.76.6
Affected Platforms
Runtime - iOS
Output of
npx react-native info
Stacktrace or Logs
Reproducer
https://snack.expo.dev/@maksym4062/shadow-animation-freeze-mre
Screenshots and Videos
Full shadow without radius => 60fps
https://github.com/user-attachments/assets/750c7b08-5881-432d-b82a-bef62e2ac28f
Alpha shadow without radius => 40-50fps
https://github.com/user-attachments/assets/bb763084-70d9-4d4a-ad23-70a7b79a6571
Full shadow with radius => 30-40fps
https://github.com/user-attachments/assets/3ff472cd-adeb-41a3-8115-3640cb74ef7e
Alpha shadow with radius => 20-30fps
https://github.com/user-attachments/assets/386f55dd-92c2-4628-9fde-2d876ab0e2cf
The text was updated successfully, but these errors were encountered: