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

[Async Updates] First MVP of Async Updates #2276

Merged
merged 5 commits into from May 9, 2023
Merged

Conversation

gpeal
Copy link
Collaborator

@gpeal gpeal commented Apr 9, 2023

This is the first MVP of async updates. This project is being funded by Airbnb Eng and likely wouldn't happen without their sponsorship.

The docs for the AsyncUpdates enum includes details on what is going on here but at a high level, Lottie has two hot paths:

  1. setProgress
  2. draw

This allows the former to happen off of them main thread _ immediately after_ draw completes so it is ready before the next one starts.

In many of my tests, the two paths were each accountable for ~50% of the total main thread work so this could reduce the main thread activity by ~50% or more.

Here is an example of systrace before:
CleanShot 2023-04-09 at 15 24 34@2x

You can see setProgress being called immediately before draw.

This is what it looks like with async updates enabled:
CleanShot 2023-04-09 at 15 23 35@2x

You can see that draw happens first and then setProgress for the next frame happens immediately after on a different thread.

This is experimental and defaults to AUTOMATIC but AUTOMATIC will default to false until this API is stabilized.

@gpeal gpeal force-pushed the gpeal/set-progress-thread2 branch from d29b0c4 to d71ff51 Compare April 9, 2023 22:51
@github-actions
Copy link

github-actions bot commented Apr 9, 2023

Snapshot Tests
API 23: Report Diff
API 31: Report Diff

@github-actions
Copy link

github-actions bot commented Apr 9, 2023

Snapshot Tests
API 23: Report Diff
API 31: Report Diff

@github-actions
Copy link

github-actions bot commented May 4, 2023

Snapshot Tests
API 23: Report Diff
API 31: Report Diff

@github-actions
Copy link

github-actions bot commented May 4, 2023

Snapshot Tests
API 23: Report Diff
API 31: Report Diff

@github-actions
Copy link

github-actions bot commented May 7, 2023

Snapshot Tests
API 23: Report Diff
API 31: Report Diff

Copy link

@Shawna1987 Shawna1987 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok

@gpeal gpeal force-pushed the gpeal/set-progress-thread2 branch from 9a40f19 to 34d07fc Compare May 9, 2023 01:01
@github-actions
Copy link

github-actions bot commented May 9, 2023

Snapshot Tests
API 23: Report Diff
API 31: Report Diff

@gpeal gpeal merged commit a0bf926 into master May 9, 2023
6 checks passed
@gpeal gpeal deleted the gpeal/set-progress-thread2 branch May 9, 2023 01:26
@AndroidDeveloperLB
Copy link

AndroidDeveloperLB commented Jul 16, 2023

Can anyone please make a tiny sample of how to use it?
Would it be possible to use it for this special scenario:
I want to create some sort of player of Lottie animation, that will send to a background thread a Bitmap of each frame that needs to be drawn, once it's time to show it.
This can be used, for example, for a live wallpaper.

Another possible scenario:
Go over each frame and send it to be converted to MP4.

@gpeal
Copy link
Collaborator Author

gpeal commented Jul 16, 2023

@AndroidDeveloperLB I'm not sure if this will necessarily help. I would:

  1. Create your bitmap
  2. Create a canvas instantiated with your bitmap so that when you draw to your canvas, it draws to the bitmap
  3. Call setProgress and draw(canvas) with your LottieDrawable. The frame will be drawn to your bitmap.

@AndroidDeveloperLB
Copy link

@gpeal Thank you, but is this what you suggest for the player, or for the conversion to MP4 ?
If it's for the conversion, this is what I did:

LottieCompositionFactory.fromRawRes(this, R.raw....)
    .addListener { composition ->
        // Create a LottieDrawable from the LottieComposition
        val drawable = LottieDrawable().apply {
            setComposition(composition)
        }
        thread {
            try {
                val totalFrames = composition.durationFrames.toInt()
                val frameDuration = composition.duration / totalFrames
                val frameDurationInt = frameDuration.roundToInt()
                Log.d("AppLog", "duration of each frame:$frameDurationInt ms . Frames count:$totalFrames")
                val width = drawable.intrinsicWidth
                val height = drawable.intrinsicHeight
                val bitmap =
                    Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
                val canvas = Canvas(bitmap)
                for (i in 0 until totalFrames) {
                    drawable.frame = i
                    canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR)
                    drawable.draw(canvas)
                    //bitmap ready here
                    Log.d("AppLog", "bitmap for frame $i  ")
                }
                val endTime = System.currentTimeMillis()
                Log.d(
                    "AppLog",
                    "it took ${endTime - startTime} ms to get all $totalFrames frames ($width x $height) as bitmaps"
                )
            } catch (e: Exception) {
                Log.d("AppLog", "error:$e")
                e.printStackTrace()
            }
        }
    }

If for the player, I'm not sure if such a thing would suffice.

@gpeal
Copy link
Collaborator Author

gpeal commented Jul 16, 2023

@AndroidDeveloperLB I'm not really sure what you're asking. In general, if you're trying to do real time playback, you shouldn't do the bitmap rendering because it's software rendering. The default hardware rendering path will be much much faster. If you need to do something with the bitmaps like render them to mp4 then code similar to what you have is probably your best bet.

@AndroidDeveloperLB
Copy link

@gpeal What I'm trying to have is a live wallpaper app, being able to show various kinds of content (color, image, animation and even video) in the center (center-crop) with ability to horizontally move.

If you had wanted to show a Lottie animation (or animated GIF/WEBP), would you have chosen to directly show it by their libraries that can be drawn to a Bitmap, and then via the Canvas of the live wallpaper?
Or would you have converted them to MP4 and then play them? For Lottie, as opposed to animated GIF/WEBP, it would also mean taking more storage and losing the quality (because it's a vectorized graphics).

If I use the playing solution of Lottie, and have a Bitmap being prepared in the background, wouldn't it be enough and the animation would be fluid?

Sadly there aren't much tutorials for a live wallpaper for playing MP4 files . I had to ask this on StackOverflow after finding a bit broken app:
https://stackoverflow.com/q/76608433/878126

As for converting to MP4 (from animated GIF/WEBP and Lottie) , I've also prepared something based on other's work, but I think it might have a bug too , because when I test it on a very few frames, I don't see some on the output video:
https://stackoverflow.com/a/64744296/878126
https://stackoverflow.com/a/64839777/878126

@aatishmittal194
Copy link

aatishmittal194 commented Jul 18, 2023

@gpeal I can see api to enable async update for specific LottieAnimationView. Does lib support api to enable async updates for all the animations? Want to enable async update for Canary users.

@gpeal
Copy link
Collaborator Author

gpeal commented Jul 18, 2023

@aatishmittal194 I've gotten another request to add a global flag for it. I'll add it to Lottie.initialize in the next release
#2341

@AndroidDeveloperLB
Copy link

@gpeal Can you please demonstrate it for a single one, though?

@gpeal
Copy link
Collaborator Author

gpeal commented Jul 18, 2023

@AndroidDeveloperLB app:lottie_asyncUpdates="enabled" on a LottieAnimationview (in XML) or setAsyncUpdates on LottieDrawable or LottieAnimationView or asyncUpdates = in Compose

@AndroidDeveloperLB
Copy link

@gpeal And that's it? Thank you!

@gpeal
Copy link
Collaborator Author

gpeal commented Jul 18, 2023

@AndroidDeveloperLB Yes that's it 😄

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

4 participants