opened 05:50PM - 28 Jan 23 UTC
**Description**
I'm currently experimenting with animated SKPaths, but I stum…bled upon a memory leak issue I couldn't resolve.
I'm not entirely sure but I suspect there is some unmanaged resource, which is not released correctly.
(Either in SKPath, or in SKSurfce, maybe related to both?)
I tried two different methods of creating an animated SKPath. Either by calling Reset() and rebuilding a new shape,
or by calling Dispose() on the old path and creating a new one.
Both options result in a memory leak.
Some further observations:
- The memory leak affects both RAM and VRAM.
- Forcing a garbage collection run does not free up the leaked memory.
- The SKPaint settings (Antialias / Stroke / Fill) don't affect the memory leak. (Apparently)
- SKCanvas matrix changes don't affect the memory leak. (Translation and Scale get applied each frame)
- Only rebuilding but not drawing the path doesn't cause a memory leak.
- Only drawing the same path, without rebuilding it, doesn't cause a memory leak.
- Using dispose on the SKPath, instead of resetting and reusing it, doesn't stop the memory leak.
- Disposing + recreating or resetting + rebuilding the SKPath but skipping the "animation" (essentially re-adding the same points each frame) stops the memory leak?! (How is that even possible?)
After digging though some of the Google Skias documentation about SKPath, I found the [isVolatile] flag.
The description of which sounds suspiciously like what is going wrong here:
_Returns true if the path is volatile; it will not be altered or discarded by the caller after it is drawn.
[SkPath](https://api.skia.org/classSkPath.html) by default have volatile set false, allowing [SkSurface](https://api.skia.org/classSkSurface.html) to attach a cache of data which speeds repeated drawing. If true, [SkSurface](https://api.skia.org/classSkSurface.html) may not speed repeated drawing._
I don't know if the volatile setting was added to Skia in a later Version, than what SkiaSharp is build on, but both the Flag and the related setIsVolatile() function are absent in SkiaSharp.
**Code**
Alternative methods of creating the animated path (Running once each frame):
```
//-----------------------------------------------------------------------------------------------------//
//Reset and reuse existing SKPath
//-----------------------------------------------------------------------------------------------------//
PathBuffer.Reset();
//Move to first point position
PathBuffer.MoveTo(CurrentPoints[0].Position);
//Connect bezier points
for (int x = 1; x < CurrentPoints.Length; x++)
{
PathBuffer.CubicTo(CurrentPoints[x - 1].HandleNext, CurrentPoints[x].HandleLast, CurrentPoints[x].Position);
}
//Close loop
SKPoint first = PathBuffer.GetPoint(0);
PathBuffer.CubicTo(CurrentPoints.Last().HandleNext, CurrentPoints[0].HandleLast, first);
PathBuffer.Close();
```
```
//-----------------------------------------------------------------------------------------------------//
//Dispose old SKPath and create new SKPath
//-----------------------------------------------------------------------------------------------------//
PathBuffer.Reset();
PathBuffer.Dispose();
PathBuffer = new SKPath();
//Move to first point position
PathBuffer.MoveTo(CurrentPoints[0].Position);
//Connect bezier points
for (int x = 1; x < CurrentPoints.Length; x++)
{
PathBuffer.CubicTo(CurrentPoints[x - 1].HandleNext, CurrentPoints[x].HandleLast, CurrentPoints[x].Position);
}
//Close loop
SKPoint first = PathBuffer.GetPoint(0);
PathBuffer.CubicTo(CurrentPoints.Last().HandleNext, CurrentPoints[0].HandleLast, first);
PathBuffer.Close();
```
Drawing the animated path to the screen:
```
//Clear rendertarget
gameTarget.Clear(new SKColor(0, 0, 0, 0));
//Center canvas in the middle of the screen
gameTarget.surface.Canvas.Translate(gameTarget.width / 2f, gameTarget.height / 2f);
//Scale canvas (Path point coords are within -1 to 1 space)
gameTarget.surface.Canvas.Scale(100f, 100f);
using (SKPaint paint = new SKPaint())
{
paint.Color = new SKColor(255, 255, 255);
paint.IsAntialias = true;
paint.IsStroke = true;
paint.StrokeWidth = 0.02f;
if (!DEBUG_SKIP_DRAW)
{
//Draw current path to canvas
gameTarget.surface.Canvas.DrawPath(PathBuffer, paint);
}
}
//Reset rendertarget matrix
gameTarget.surface.Canvas.ResetMatrix();
```
**Expected Behavior**
Correctly releasing all unmanaged data (and caches?) related to SKPath and SKSurface if SKPath gets disposed.
**Actual Behavior**
I have no idea, what is going on "behind the curtains"...
All I see is a 100mb of RAM leaking every 10 seconds. Even though I called Dispose() on everything I have access to.
**Basic Information**
- Version with issue: 2.88.3, GPU Renderer
- Last known good version: Unknown
- IDE: Visual Studio 2022
- Platform Target Frameworks: Windows10, .NET 7
**Screenshots**
dotMemory screenshot:

**Reproduction Link**
Very simplified example project to showcase the memory leak issue. (Everything importand is in the DEBUG_SKPath.cs file)
This project uses .NET 7 and requires the newest version of Visual Studio 2022 to compile:
[ExampleProjectFiles.zip](https://github.com/mono/SkiaSharp/files/10527777/ExampleProjectFiles.zip)
Memory profile produced by JetBrains dotMemory profiler (I have not enough experience to know, what I have to look for):
[MemoryProfiling.zip](https://github.com/mono/SkiaSharp/files/10527894/MemoryProfiling.zip)