mono / SkiaSharp

SkiaSharp is a cross-platform 2D graphics API for .NET platforms based on Google's Skia Graphics Library. It provides a comprehensive 2D API that can be used across mobile, server and desktop models to render images.
MIT License
4.45k stars 539 forks source link

[BUG] [iOS] SKGLView Memory Leak compared to SKCanvasView #1683

Open IainS1986 opened 3 years ago

IainS1986 commented 3 years ago

Description

I've been investigating a memory issues i've been seeing when using SKGLView compared to SKCanvasView.

I'm using SkiaScene to handle touch manipulation but I'm fairly confident that isn't the issue.

To better test, I setup my project on iOS with a CADisplayLink to continuously trigger a SetNeedsDisplay on the Skia view so a new render will occur (I found the memory leak like this when I was doing a simple animation using CADisplayLink and left the animation running, its also possible to trigger this by continuously touching the screen triggering a redraw, but obviously thats much more effort to test!).

My skia drawing is fairly complex but at the root of it, the current example is basically drawing approximately 30 DrawLine calls and 30 DrawCircle calls every render.

I'm comparing the exact same code, with the only difference being one uses SKGLView and one uses SKCanvasView. I hook up to the PaintSurface events and the canvas from the surface is passed through the rendering, from what I can tell the reference to the canvas is never stored off anywhere within my logic (or SkiaScene).

With SKGLView, after about 1 minute the app completely freezes. No crash or application output is displayed or recieved. Looking at the console for the device the following lines print out...

IOReturn IOGPUDevice::new_resource(IOGPUNewResourceArgs *, struct IOGPUNewResourceReturnData *, IOByteCount, uint32_t *): PID 40079 likely leaking IOGPUResource (count=46000)
IOReturn IOGPUDevice::new_resource(IOGPUNewResourceArgs *, struct IOGPUNewResourceReturnData *, IOByteCount, uint32_t *): PID 40079 likely leaking IOGPUResource (count=47000)
IOReturn IOGPUDevice::new_resource(IOGPUNewResourceArgs *, struct IOGPUNewResourceReturnData *, IOByteCount, uint32_t *): PID 40079 likely leaking IOGPUResource (count=48000)
IOReturn IOGPUDevice::new_resource(IOGPUNewResourceArgs *, struct IOGPUNewResourceReturnData *, IOByteCount, uint32_t *): PID 40079 likely leaking IOGPUResource (count=49000)

This continues until count=60000 and then it freezes (always increasing in 1000 increments and freezing at 60000).

When I run the exact same code with SKCanvasView it can run forever (I've had the app running for 2hr+ on my desk and never had it freeze).

Below you can see the Xamarin profiler outputs. The first is the SKGLView and you can see it shoots up to a working set of around 500mb, then decreases and during this time the errors start printing out, and eventually it freezes (flatlines in the profiler).

The second is SKCanvasView, you can see my memory increases to a point, then when the continous rendering update occurs the memory basically hovers flat around 185mb (with a slight creep up but I suspect that isn't Skia related).

I've checked the Handle on the e.Surface.Canvas in the PaintSurface event when using the SKGLView and it always seems to be the same, so I'm getting the same canvas object through in the PaintSurface event (I think).

I'm calling Flush on the canvas after rendering.

Any thoughts?

Basic Information

Detailed IDE/OS information (click to expand) ``` === Visual Studio Enterprise 2019 for Mac === Version 8.9.4 (build 25) Installation UUID: 088507f4-3001-4ca4-9722-69b7ab1ca6e1 GTK+ 2.24.23 (Raleigh theme) Xamarin.Mac 6.18.0.23 (d16-6 / 088c73638) Package version: 612000125 === Mono Framework MDK === Runtime: Mono 6.12.0.125 (2020-02/8c552e98bd6) (64-bit) Package version: 612000125 === Roslyn (Language Service) === 3.9.0-6.21152.10+c10f884b30737542ddd84ca889a4aad9281ce210 === NuGet === Version: 5.8.0.6860 === .NET Core SDK === SDK: /usr/local/share/dotnet/sdk/5.0.201/Sdks SDK Versions: 5.0.201 5.0.103 5.0.101 5.0.100 3.1.407 3.1.406 3.1.404 3.1.403 3.1.402 3.1.401 3.1.302 3.1.301 3.1.300 3.1.202 3.1.200 3.1.102 MSBuild SDKs: /Applications/Visual Studio.app/Contents/Resources/lib/monodevelop/bin/MSBuild/Current/bin/Sdks === .NET Core Runtime === Runtime: /usr/local/share/dotnet/dotnet Runtime Versions: 5.0.4 5.0.3 5.0.1 5.0.0 3.1.13 3.1.12 3.1.10 3.1.9 3.1.8 3.1.7 3.1.6 3.1.5 3.1.4 3.1.2 2.1.23 2.1.22 2.1.21 2.1.20 2.1.19 2.1.18 2.1.17 2.1.16 === .NET Core 3.1 SDK === SDK: 3.1.407 === Xamarin.Profiler === Version: 1.6.13.11 Location: /Applications/Xamarin Profiler.app/Contents/MacOS/Xamarin Profiler === Updater === Version: 11 === Xamarin Designer === Version: 16.9.0.316 Hash: 2241b204a Branch: tags/vsm-rel/d16.9-4540908 Build date: 2021-03-10 21:18:10 UTC === Apple Developer Tools === Xcode 12.4 (17801) Build 12D4e === Xamarin.Mac === Version: 7.8.2.5 (Visual Studio Enterprise) Hash: 3836759d4 Branch: d16-9 Build date: 2021-02-10 17:56:43-0500 === Xamarin.iOS === Version: 14.14.2.5 (Visual Studio Enterprise) Hash: 3836759d4 Branch: d16-9 Build date: 2021-02-10 17:56:44-0500 === Xamarin.Android === Version: 11.2.2.1 (Visual Studio Enterprise) Commit: xamarin-android/d16-9/877f572 Android SDK: /Users/iainstanford/Library/Developer/Xamarin/android-sdk-macosx Supported Android versions: 5.1 (API level 22) 6.0 (API level 23) 7.1 (API level 25) 8.1 (API level 27) SDK Tools Version: 26.1.1 SDK Platform Tools Version: 31.0.0 SDK Build Tools Version: 30.0.3 Build Information: Mono: 5e9cb6d Java.Interop: xamarin/java.interop/d16-9@54f8c24 ProGuard: Guardsquare/proguard/v7.0.1@912d149 SQLite: xamarin/sqlite/3.34.1@daff8f4 Xamarin.Android Tools: xamarin/xamarin-android-tools/d16-9@d210f11 === Microsoft OpenJDK for Mobile === Java SDK: /Users/iainstanford/Library/Developer/Xamarin/jdk/microsoft_dist_openjdk_1.8.0.25 1.8.0-25 Android Designer EPL code available here: https://github.com/xamarin/AndroidDesigner.EPL === Android SDK Manager === Version: 16.9.0.22 Hash: a391de2 Branch: remotes/origin/d16-9~2 Build date: 2021-03-24 08:30:26 UTC === Android Device Manager === Version: 16.9.0.17 Hash: fc2b3db Branch: remotes/origin/dev/jmt/d16-9bump~1 Build date: 2021-03-24 08:30:44 UTC === Build Information === Release ID: 809040025 Git revision: 5d0f80a4343884101c6c7869bc61d5101cf30052 Build date: 2021-03-25 08:43:13-04 Build branch: release-8.9 Xamarin extensions: 5d0f80a4343884101c6c7869bc61d5101cf30052 === Operating System === Mac OS X 10.15.7 Darwin 19.6.0 Darwin Kernel Version 19.6.0 Thu Oct 29 22:56:45 PDT 2020 root:xnu-6153.141.2.2~1/RELEASE_X86_64 x86_64 ```

Screenshots

SKGLView Memory Usage: Screenshot 2021-04-07 at 08 50 51

SKCanvasView Memory Usage: Screenshot 2021-04-07 at 09 09 21

radev8 commented 3 years ago

This issue is possibly related to my issue as I also use the hardware acceleration. The memory usage over time is comparable. https://github.com/mono/SkiaSharp/issues/1680

IainS1986 commented 3 years ago

@radev8 I'll double check but I'm pretty certain I see the exact same rate of memory increase even with no SKPath usage

But certainly sounds in a similar area, and interesting yours is UWP while this is iOS. Both ARM based GPUs maybe?

charlenni commented 3 years ago

@IainS1986 As you could see, we now have the same problems as you: When there are many redraws in short time then the app crashes with the same GPU error.

Do you find a solution for this?

IainS1986 commented 3 years ago

@charlenni yes - don't use SKGLView on iOS.

charlenni commented 3 years ago

😂

Thank you for your fast answer.

Do you use SKPath for your drawings? Could it be, that there are resources created that aren't released before the next drawing?

charlenni commented 3 years ago

I assume here, that you use SKPath.

I assume, that there are problems with releasing the resources. Perhaps Skia uploads a path to the GPU for drawing, because it thinks, that the path is drawn again and again. But in some cases, if you create the SKPath every time new before drawing it, the release of the resources on GPU is not fast enough.

Did you try to release them by explicitly deleting SKPath after drawing the path?

IainS1986 commented 3 years ago

I use SKPath and I completely reuse the same paths in every redraw (Rewinding the same path to then reuse it). Before this I also released the SKPaths - both approaches still crashed.

However - its been a long time (April) - but I believe I could reproduce the memory issue without any SKPath calls too.

I don't think its SKPath specific. I think you could have a blank SKGLView and redraw every frame and get the same error.

najak3d commented 1 year ago

This defect would be a HUGE issue for us. Without the GPU-based SKGLView, we will be unable to make use of Custom Shaders, right? We NEED Custom Skia Shaders to create the needed effects, plus we need/want the performance boost. From what I can tell, this has never been fixed, correct?

BUT, I ran a test myself, where all I did was this:

  1. Clear(Green)
  2. DrawBitmap (full sized, 1000x2000 pixels approx)
  3. DrawPath Polygon 50 points. Each time I create a new Path (but using the same SKPoint[50] array), and do NOT dispose of the previous one (was trying to induce a crash that I could then see if Dispose() fixed it -- but for me, not calling Dispose() even works).

It Runs continuously at 50 FPS. No hiccups.

It's been running now for 15 minutes (900 seconds x 50 FPS = 45,000 frames rendered), and no crash yet. Performance hasn't slowed. I will let it run all night, and see if it's still running 8 hrs from now, and will update this comment.

EDIT: Yep, it ran for 8.5 hrs -- full speed, no slow downs, no crashes. 50 FPS, recreating a 50 pt Polygon Path every single frame.

I don't have a profiler here to show me the memory usage on my iPad.

I'm using Skia 2.88.6, on iOS 16.7, iPad Mini 4.

So for me, it appears it might be fixed? Or maybe my test case simply doesn't cause the defect for some reason.