-
-
Notifications
You must be signed in to change notification settings - Fork 879
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: performance improvements on SpriteBatch
APIs
#1637
fix: performance improvements on SpriteBatch
APIs
#1637
Conversation
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 don't think there is anything wrong with the current approach that uses PictureRecorder
. (If there is, we need to have a clear confirmation of that, then we need to file a bug to Flutter team, and use the suggested workaround until that bug is resolved, but also put it into a separate function so that it can be reused through multiple other places where we use PictureRecorder in Flame).
The more probable cause of #1614 is that SpriteBatch.load()
method attempts to generate atlas on every call - even if the sprite batch won't be using the flipped images later on. And that image generation is not cached. So, if the user has code that does SpriteBatch.load()
whenever a component is created, and if many such components are created per second, then there will be lots of unnecessary image generation happening. This explanation fits well with the problem described in #1614 as "IOS cannot even see the app launches because it shutting down immediately after launched due to memory issue."
So, I suggest to do the following:
- Restore the original image generation through PictureRecorder;
- Make invocation of
_generateAtlas
happen only if the user explicitly requests support for flipped sprites in their SpriteBatch; - Cache that generated image;
- (Optionally) Measure the speed difference between generating an image via PictureRecorder vs via the Bitmap library.
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.
@nathanaelneveux can we please start with the following:
- Add a boolean flag into
SpriteBatch.load()
which will tell whether to generate the mirrored image or not. We don't want to create extra images for those users who will not be using them. - For now, have two methods
_generateAtlas1
and_generateAtlas2
with the PictureRecorder and the Bitmaps approaches -- this way it would be easier for us to compare and debug the two methods.
I think we can do better than a flag for the generation of the mirrored images. For one thing How do you feel about doing it as a "lazy load" where once we get a call to |
Will that not potentially impact performance mid-game instead of in |
|
Alright, sounds like it makes sense to have it lazily initialized then! |
Co-authored-by: Pasha Stetsenko <stpasha@gmail.com>
@nathanaelneveux any progress on this one? Anything that you need help with? :) |
I'm honestly struggling with what pattern to use to do this with null safety - I'll post what I have so far in a bit with some notes - I could use some advice. |
So obviously you’ll see in the most recent commit that it’s not quite there yet. It’s still generating the I was hoping I need a reference to |
Even though this is passing tests this isn't working on my test map - the flips are wrong. I'll have a test that looks at a larger atlas to get the bottom of this and make sure it doesn't happen again. |
It seems like it is failing both of the tests that you added, even the one not using the atlas? |
There was an error introduced when I stacked the flipped image vertically instead of horizontally in 35e08d8. |
Tests are passing now - I know there must be a better way to do the conditional in the BatchItem construction but its too late here for me to think straight |
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.
Thanks for your very hard and long work on this PR @nathanaelneveux, very appreciated! 🎉
(Are you on our discord?)
..translate(source.width / 2, source.height / 2) | ||
..rotateY(flip ? pi : 0) | ||
..translate(-source.width / 2, -source.height / 2), |
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.
Could these be defined directly in the Matrix4
? 🤔
@st-pasha Do you know how to do that, or if it is possible?
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.
We could have some extension method to make a Matrix4
out of RSTransform
, looks like there isn't one right now.
That _defaultScale
looks mighty suspicious to me as well, I think we can just remove it without anyone noticing.
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.
Removed it, should this really be 0 and not 1? 🤔
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 is the difference between the matrix's with the rotateY code and without:
WITH FLIPS
[0] 1.0,-0.0,-1.2246467991473532e-16,32.0
[1] -0.0,-1.0,0.0,16.0
[2] -0.0,0.0,0.0,0.0
[3] -0.0,0.0,0.0,1.0
[0] -0.0,1.0,0.0,48.0
[1] 1.0,0.0,-1.2246467991473532e-16,0.0
[2] -0.0,0.0,0.0,0.0
[3] -0.0,0.0,0.0,1.0
[0] -0.0,-1.0,0.0,48.0
[1] -1.0,0.0,1.2246467991473532e-16,32.0
[2] -0.0,0.0,0.0,0.0
[3] -0.0,0.0,0.0,1.0
[0] -1.0,-0.0,1.2246467991473532e-16,64.0
[1] -0.0,1.0,0.0,16.0
[2] -0.0,0.0,0.0,0.0
[3] -0.0,0.0,0.0,1.0
NO FLIPS
[0] -1.0,-0.0,0.0,48.0
[1] 0.0,-1.0,0.0,16.0
[2] 0.0,0.0,0.0,0.0
[3] 0.0,0.0,0.0,1.0
[0] 0.0,1.0,0.0,48.0
[1] -1.0,0.0,0.0,16.0
[2] 0.0,0.0,0.0,0.0
[3] 0.0,0.0,0.0,1.0
[0] 0.0,-1.0,0.0,48.0
[1] 1.0,0.0,0.0,16.0
[2] 0.0,0.0,0.0,0.0
[3] 0.0,0.0,0.0,1.0
[0] 1.0,-0.0,0.0,48.0
[1] 0.0,1.0,0.0,16.0
[2] 0.0,0.0,0.0,0.0
[3] 0.0,0.0,0.0,1.0
(.toString
doesn't look to be in the same order as the Matrix4 initializer)
_generateAtlas
_generateAtlas
SpriteBatch
APIs
Co-authored-by: Jochum van der Ploeg <jochum@vdploeg.net>
Phew :) that definitely ending up being more than I expected when first tackling this as a hobby project lol. Thank you all for your patience and help - great community you’ve built here |
/// [name] in the cache. | ||
Future<Image> fetchOrGenerate( | ||
String name, | ||
Future<Image> Function() imageGenerator, |
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.
Why is it named "Generator" instead of "Builder"?
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 didn't name it, but it's not a builder (if the builder name is used according to the builder pattern).
/// | ||
/// If the [imageGenerator] is used, the resulting [Image] is stored with | ||
/// [name] in the cache. | ||
Future<Image> fetchOrGenerate( |
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.
Is this meant to fetch and if it fails to do so then, it builds an image and also caches it?
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.
Yes, is that not clear from the Dartdocs? 😅
Description
Fix for the performance decrease mentioned in #1614
Some items that need work:
Checklist
fix:
,feat:
,docs:
etc).docs
and added dartdoc comments with///
.examples
.Breaking Change
Related Issues
Fixes #1614