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
Dropped frames while scrolling in list with multiple spans #409
Comments
Here's the screenrecord https://imgur.com/a/oX3jmdn |
Thanks for the report! This seems like it might related to #378. Coil currently doesn't interrupt the thread during decode - it only supports coroutines cancellation. Just to be sure, can you enable logs and ensure that requests are being started/cancelled when you expect them to? If that's the case, I'll prioritize #378. |
Yup they seem to start & cancel when they should. It certainly looks like the case. I don't do Kotlin so I don't know how coroutines work so can't add much feedback in there, but it certainly looks like what you are describing. The threads should be interrupted if the view is detached/recycled otherwise this will happen. I tried with a big library (+10k pictures) and if I start scrolling really fast everything will be blocked indefinitely until everything is decoded, even if the views have been recycled. Let me know if there's anything else I can help you with. |
@franciscofranco I've merged interruption support into master. When you get the chance could you try using the latest snapshot and let me know if it fixes your issue? |
Awesome. I'll try in the next couple hours and get back to you ASAP. |
I'm afraid to report I don't see many, if any, improvements using the latest 0.12.0-SNAPSHOT :(. Maybe I'm being retarded and I need to enable this new feature? Didn't seem to find any api to do so if that's the case. How can I help? |
Also enabled logging to see wtf is going on, and just caught it taking more than 15 seconds between a particular cancelation:
And again, now with just 3 columns (so it's showing way less thumbnails):
|
Hmm are these local file uris, content uris, or http uris? Can you try increasing the |
These are local, there are no http uris on my app. Yes, let me try that. |
No change, same behaviour. No matter what kind of changes I make to the dispatcher or the ExecutorService it always seems to block every now and then. Basically the only thing I can do to mitigate this is lower the span count of my grid from 5 to 3 items. What else can I try? Can it be a bug on the coroutines themselves? Sorry I closed the issue by mistake |
@franciscofranco Ah darn, thanks for checking. If you have a sample project I could run locally that'd be best. Otherwise I'll create a list of a bunch of local images and debug likely this weekend. Do you know the average size of the images? Also are they mostly jpgs or another file type? |
There's nothing really fancy about the project... it's just a 5 span grid, each thumb is (displayWidth / spanCount), each picture is a normal 12px shot from my camera (Pixel 3) so they're JPG, all local URI. The code I'm using from Coil is the same I posted on this issue. I don't do any transformations or animations. You can check the app: https://play.google.com/store/apps/details?id=com.franco.graphice Let me know if you reproduced it, otherwise I'll give you access to my private repo with all the source code. Not ideal, but I'd rather help you out somehow figure this one up since I like Coil more than Glide at this point. |
I think I'm able to reproduce this (or at least another performance issue) by setting |
Okay so I think the dropped frames are a result of a combination of factors:
With enough active image requests it seems like main thread starts thrashing. I've started some work here to start optimizing for high concurrency scenarios like this, but it's going to take a while. In the meantime, I've found using |
I'm glad you managed to reproduce this locally! Do you really need to pull the drawables from cache synchronously? You can do it in a thread and then post it to the ImageView that was passed to the builder? I think it's reliatively trivial to check if the ImageView on the request has been recycled/different position so you can silently fail and move on to the new request. I'll try your suggestion. |
It's SIGNIFICANTLY better using the default Dispacher. It's like night & day. I can set it to 6 columns and while I can see some jank it doesn't seem to block the thread anymore to the point of waiting 20s for it to "unblock" itself and keep decoding. Good call and can't wait to see the improvements from your PR. |
@franciscofranco Coil checks the memory cache synchronously to avoid flashes where it shows the placeholder for one frame then shows the memory cache item. It also allows supporting cool things like image sampling automatically. The check is overall very quick and I'm not convinced it's 100% causing the lag we're running into. There's also some work like interacting with AndroidX Lifecycles and any View methods that need to be called from the main thread. I'm going to keep investigating the root cause of the lag and test if moving the memory cache check off the main thread solves the issue. If it does I think it makes sense to add a mode to move that work to the background dispatcher (at the cost of losing the benefits mentioned above). |
Just to add I'm having the same problem when using ViewPager2. Heavy main thead usage. Should I just downgrade Coil for the time being? 0.9.5 was fine |
@colinrtwhite can you try to see if setting trackWeakReferences(false) reduces your jank? |
@Wrakor Hmm I wouldn't expect 0.9.5 to perform better in this case, but I'll try 0.9.5 with my sample. If it fixes it it'll definitely help with debugging. EDIT: I see the same issue with 0.9.5. @franciscofranco I tried |
I reverted to v0.9.5 and the blockage is gone, as well as the "Davey!" logs, all back to being smooth. |
@Wrakor Hmm maybe it's a different issue you're running into. I did more profiling yesterday and even if I move the memory cache check to a background thread I still see the dropped frames. It seems like |
I tried 0.9.5 and the blockage is indeed gone. Very weird. |
@franciscofranco There were a number of performance enhancements that went into |
Fantastic. I'll definitely test in the next few days |
Just adding my two cents, I've tried the 0.13.0 update (both with and without |
@Wrakor If you're able to create a sample project that reproduces the issue (or modify the |
I'm trying to set a sample project, but I'm having trouble loading images when using Coil (shows a blank image view). Could you see if there's anything missing in my code? |
@Wrakor The application loads the images correctly for me with no performance issues - it's likely a local setup issue. |
I'm on Coil 1.0.0-rc2,facing exactly the same issue. The withInterruptibleSource seems not solving this problem. After some digging, I notice that Dispatchers.Default, which Coil relays on, is starting too many decode() thread at the same time, eating too many memory. Comparing to Glide v4.11.0, there's only 4 or 5. So, I feed the ImageRequest with a newFixedThreadPoolContext(nThreads = 4,...) as a workaround. |
@colinrtwhite I've managed to narrow down my problem to SVG loading. Some SVGs, when loaded with a custom Target, are causing heavy main thread usage. I've updated my example project. Do you want me to open a new issue? |
@Wrakor Thanks - I'll take a look. I'd keep discussion in this issue. |
So wanted to give coil a new try now with the interceptor and all but the lack of parallelism control triggers this issue too for me. While we can pass a fixed thread pool dispatcher, this triggers more context switch that needed and prevent thread sharing with the rest of the app, vastly reducing the gains of using coroutines. Is there any plans to add some concurrency limits ? |
@Tolriq You can use |
I still want the default to be available for the rest and not being blocked doing IO stuff that can take long when downloading large images. Using that would be even worse as all threads would be blocked doing IO for images and the rest of the coroutines would be waiting to run. |
Is there already a solution for this? Since with 5 spans and |
@leondeklerk Unfortunately, there isn't a great solution for this at the moment aside from setting |
I've just published Among other performance improvements, 2.x throttles the output of |
@colinrtwhite this solved the issue in my app. Now my image gallery (LazyColumn with 3 image columns) is very smooth. Thank you! |
@colinrtwhite I'm using PictureSelector. |
Is there a load limit? For example, when I load 1000 pictures, I swipe quickly and can only slide to the 500th position. After the 500 pictures are partially loaded, I can continue to slide down. |
Going to close this out as the original issue is fixed in |
I'm observing some pretty hefty main thread blockage using the latest Coil version (0.11.0) and older ones too. I just migrated from Glide on this app (I've used Coil before in other projects but never in a RV adapter) and everything was smooth until I tried scrolling on the RV. Anyway, tested with both ixel 3 and OnePlus 7T Pro so clearly not an hardware bottleneck.
My setup is a 5 span GridLayoutManager vertical scrolling. One device has +300 pictures, the other has ~50, so doesn't seem related to the amount of pictures. What I observe is when I start scrolling when Coil is decoding the URIs and starting to display the thumbnails something seems to block the main thread and the whole thing gets blocked and it seems to suspend the entire operation. Sometimes I have to wait 3-4 seconds for it to finish whatever it seems to be doing and it then resumes.
In the screenrecord I'm attatching you can see a bit of the jank after I open the app fresh. I made each thumb 8px/8px because it's my personal pictures library, but you get the picture. It obviously has worse jank with their actual decoded size (which should be device width / spancount) (5 in this case). The jank is halved if I decrease the span count to 3 or 4. Here's the very simple code I'm using:
Logcat is filled with these GC related operations when doing the scrolling:
Also it's a bit better when using BitmapConfig RGB_565, but just a tiny bit. With the default BitmapConfig I see tons of these logcat messages:
Any clues or question that I can answer to help you narrow it down somehow?
Thanks I hope this was clear enough.
The text was updated successfully, but these errors were encountered: