Description
Using a timer for 30fps on a device to update a SKCanvasView to draw a progress bar will cause the the app to freeze after some amount of time (devices and times listed below in target devices). There appears to be a bunch of garbage collection message being output in a loop. UI Is non responsive. If left long enough the UI may update so the timer does appear to still be running.
Example of the GC spam:
[Mono] GC_TAR_BRIDGE bridges 23085 objects 23085 opaque 0 colors 23085 colors-bridged 23085 colors-visible 23085 xref 0 cache-hit 0 cache-semihit 0 cache-miss 0 setup 6.66ms tarjan 25.11ms scc-setup 12.90ms gather-xref 1.10ms xref-setup 0.27ms cleanup 7.48ms
[Mono] GC_BRIDGE: Complete, was running for 1644.92ms
[Mono] GC_MAJOR_SWEEP: major size: 2672K in use: 1856K
[Mono] GC_MAJOR: (user request) time 60.52ms, stw 60.88ms los size: 1024K in use: 74K
[monodroid-gc] 46303 outstanding GREFs. Performing a full GC!
[art] Starting a blocking GC Explicit
[art] Explicit concurrent mark sweep GC freed 11(480B) AllocSpace objects, 0(0B) LOS objects, 46% free, 18MB/34MB, paused 991us total 11.365ms
[Mono] GC_TAR_BRIDGE bridges 23087 objects 23087 opaque 0 colors 23087 colors-bridged 23087 colors-visible 23087 xref 0 cache-hit 0 cache-semihit 0 cache-miss 0 setup 5.65ms tarjan 20.96ms scc-setup 12.04ms gather-xref 0.81ms xref-setup 0.15ms cleanup 7.25ms
[Mono] GC_BRIDGE: Complete, was running for 1559.15ms
[Mono] GC_MAJOR_SWEEP: major size: 2672K in use: 1857K
[Mono] GC_MAJOR: (user request) time 50.82ms, stw 51.14ms los size: 1024K in use: 74K
[monodroid-gc] 46304 outstanding GREFs. Performing a full GC!
[art] Starting a blocking GC Explicit
[art] Explicit concurrent mark sweep GC freed 4(128B) AllocSpace objects, 0(0B) LOS objects, 46% free, 18MB/34MB, paused 945us total 10.922ms
[Mono] GC_TAR_BRIDGE bridges 23086 objects 23086 opaque 0 colors 23086 colors-bridged 23086 colors-visible 23086 xref 0 cache-hit 0 cache-semihit 0 cache-miss 0 setup 5.66ms tarjan 19.78ms scc-setup 11.79ms gather-xref 0.78ms xref-setup 0.14ms cleanup 7.25ms
[Mono] GC_BRIDGE: Complete, was running for 1537.47ms
[Mono] GC_MAJOR: (user request) time 48.67ms, stw 49.34ms los size: 1024K in use: 74K
[Mono] GC_MAJOR_SWEEP: major size: 2672K in use: 1856K
[monodroid-gc] 46305 outstanding GREFs. Performing a full GC!
[art] Starting a blocking GC Explicit
[art] Explicit concurrent mark sweep GC freed 3(96B) AllocSpace objects, 0(0B) LOS objects, 46% free, 18MB/34MB, paused 942us total 10.872ms
We have tried both with the PaintSurface event and overriding OnPaintSurface with the same results. Tried various timers, tried flushing and not flushing the canvas. Appears to happen on Android 6, 8-10. Possibly 7 as well but I don't have a device to test on.
Not calling InvalidateSurface will also prevent the issue, but then the display won't be update.
We believe we also have this issue in another app which isn't calling InvalidateSurface, but instead moving a small SKCanvasView up the screen. But this example is much more easier to reproduce.
Issue only appears to effect Android. I have tried the nuget package of 1.60.0, 1.68.0 and 1.68.1-rc.153 and they all exhibit the same behaviour although the time to freeze varies a little depending on what nuget version I use.
From memory we had profiled our production app which was giving us this problem. It wasn't using excessive CPU or memory when these freezes occurred.
Expected Behavior
Timer and display runs forever.
Actual Behavior
App will freeze after a certain amount of time which changes per device.
Basic Information
Reproduction Link
It looks like most of the garbage is coming from using Xamarin Essential's DeviceDisplay.MainDisplayInfo.Density every draw.
Oh my goodness... Moving DeviceDisplay.MainDisplayInfo.Density to be measured only once has allowed my emulator build to get up to 800 seconds. I feel this was most definitely the issue. Did you pick this up with profiling, or just by eyeing how regularly that was being called?
Everything else looked legit in the draw call, and I didn't know that Xamarin Essentials had DeviceDisplay.MainDisplayInfo.Density, so I was curious about it. Changed it to 10 and saw the number of GCs go down a lot, hah.
(sorry for the above close/open ticket, its been a long debugging filled day -_-)
My repro project is now up over 2,000 seconds. I am so sure that was the issue so I'll keep this closed and pass this on to one of the devs in our team to fix in our app and I'll check back to confirm it is indeed fixed. It makes perfect sense why we appeared to be the only people getting this issue in the last 2 years.
Thanks for the quick response!
I got exactly the same issue.
Moving the DeviceDisplay.MainDisplayInfo.Density out of the Draw, sorted the issue.
I this on Android?
I this on Android?
Yes, actually Android. IOS seems to not complain, at least in the emulator.
In short, Im working is a live chart library.
As the canvas needs to translate in order to offer the live moving effect, I had a need to redraw all visible the points, including text.
I'm using DeviceDisplay.MainDisplayInfo.Density to easier scale the text. By rush I was calling it within the OnDrawMethod, forcing the DeviceDisplay.MainDisplayInfo.Density to be called very often.
The quick fix was removing it form there.
The Density value is constant is only necessary to call it once. I'm using Xamarin.Essentials V1.3.1.
Personally I can not consider it a bug, as a simple change in the client architecture can avoid it. Still can be an interesting challenge for those who maintain the package.
@mattleibow , yeah this was on Android. Wasn't an issue with SkiaSharp but just how I was using Xamarin.Essentials.