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

Animation Hitches / Main Thread Blocking. #355

Open
SleepiestAdam opened this issue Feb 14, 2025 · 5 comments
Open

Animation Hitches / Main Thread Blocking. #355

SleepiestAdam opened this issue Feb 14, 2025 · 5 comments

Comments

@SleepiestAdam
Copy link

SleepiestAdam commented Feb 14, 2025

Description

When using multiple rive views in a scroll view the main thread is often getting blocked causing an animation hitch while it waits on "next drawable"

Expected behavior

Ideally it shouldn't block the main thread / should ideally render each frame off the main thread and if the rive / metal view itself can't keep up it shouldn't slow down the rest of the UI while making the main thread wait. Less concerned about the rive view itself running at a lower frame rate than the rest of the UI should performance limitations require this - but scroll views etc shouldn't be getting blocked while waiting.

Image

Device & Versions (please complete the following information)

iOS 18.3, iPhone 16 Pro.
Rive 6.6.0

Stack Trace During Hang

semaphore_timedwait_trap
_dispatch_sema4_timedwait
_dispatch_semaphore_wait_slow
CAMetalLayerPrivateNextDrawableLocked(CAMetalLayer*, CAMetalDrawable**, unsigned long*)
-[CAMetalLayer nextDrawable]
-[MTKView currentDrawable]
-[RiveRendererView drawInRect:withCompletion:]
-[RiveRendererView drawRect:]
-[MTKView draw]
CA::Layer::layout_and_display_if_needed(CA::Transaction*)
CA::Context::commit_transaction(CA::Transaction*, double, double*)
CA::Transaction::commit()
CA::Transaction::flush_as_runloop_observer(bool)
_UIApplicationFlushCATransaction
__setupUpdateSequence_block_invoke_2
UIUpdateSequenceRun
schedulerStepScheduledMainSection
runloopSourceCallback
CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION
__CFRunLoopDoSource0
__CFRunLoopDoSources0
__CFRunLoopRun
CFRunLoopRunSpecific
GSEventRunModal
-[UIApplication run]
UIApplicationMain
closure #1 in KitRendererCommon(
:)
runApp(
:)
static App.main()
static SleepiestApp.$main()
__debug_main_executable_dylib_entry_point
start

@SleepiestAdam SleepiestAdam changed the title Animation Hitches / Main Thread Blocing. Animation Hitches / Main Thread Blocking. Feb 14, 2025
@dskuza
Copy link
Collaborator

dskuza commented Feb 18, 2025

Curious, how many Rive graphics do you have added to your scroll view? Are you able to (or are you already) pausing any animations that are off-screen due to scrolling? We're also talking internally about threading, both in our lower-level c++ runtime, and our higher level runtimes (e.g iOS). Is this the "Hangs" template in Instruments?

While I'm currently pretty deep into new feature development, I'll try and make some time to do my own profiling to see if I can reproduce similar results, and have some instrumentation to help out further development.

@SleepiestAdam
Copy link
Author

Hey - so the animations are hosted within a LazyVStack within a ScrollView. At peak there are ~18 rive views on screen, and more typically ~10 (screenshot attached for a ref of the worst case scenario).

Image

They are all being paused in the following instances:

  • When onDisappear is triggered on the SwiftUI view hosting the RiveView.

  • When the scenePhase changes to not active (aka the app is backgrounded).

We attended an Apple performance workshop and profiled it directly with some Apple engineers from their performance team last week who highlighted it was due to the Rive views / that metal views get sent to a “render server” or sorts with a maximum queue size of ~3 items and if too much was submitted to this then the out-of-the-box behaviour was that it would make the main thread wait until either render completion, or a timeout.

Their suggestion was to either:

  1. Reduce the reliance on Rive views (arguably a fair point - we do use rive for some relatively simple animations which could reasonably be done with basic SwiftUI animations if we accept less control over easing curves / only used Rive so as to allow motion designers a little more freedom during development).

  2. Tweak the rive library to handle the rendering off the main thread and only throw the final rendered image on the main thread.

In terms of the profiler settings - we used the hitches profiler setting, with the “Thread State Trace” instrument also added as advised by one of the Apple folks.

We have observed that reducing the preferredFrameRateRange of the rive views down to 25~35 FPS does reduce the animation hitches we experience (likely less being submitted to the render server), albeit they are still fairly noticeable. Overriding the view() of RiveViewModel an returning simply a Color.random results in a significant drop-off in animation hitches being present (basically only present during large-scale UI changes when switching to a new tab view etc / not during scroll).

@dskuza
Copy link
Collaborator

dskuza commented Feb 19, 2025

This is super valuable information, thank you so much! Can you open a support ticket here so that you can share some of your .rev files? (All users can export them now!) I'd like to try and reproduce some of this locally using your files, and if possible, see if I can suggest any performance optimizations while we continue some internal discussions.

We will be looking into improving threading across our runtimes, but that work can only start after other priorities the team is focusing on. I can't give an exact timeline today, but I can definitely start with the above.

@SleepiestAdam
Copy link
Author

A ticket has been submitted containing the above information / screenshots, alongside a selection of the rev files used, both in that screenshot and others. The reference is RIV-8544. If you need any further info then please just let me know.

@SleepiestAdam
Copy link
Author

SleepiestAdam commented Feb 20, 2025

Just a small note @dskuza - the changes to RiveDisplayLink you pushed in V6.7.0 appears to have rather significantly reduced the hitches we're experiencing in relation to Rive.

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

No branches or pull requests

2 participants