Skiasharp: [QUESTION] WPF SKElement and memory consumption

Created on 16 Jun 2020  路  4Comments  路  Source: mono/SkiaSharp

Hi
In a WPF app i use skiasharp to write spokes to a circle via the GPU, i create my grcontext all works allrigth
but if do not update the Canvas that i get in PaintSurface from SKElement, every operation on the surface created from grcontext consumes memory without releaseing it.
se code

  <wpf:SKElement x:Name="BitmapHost" PaintSurface="OnPaintCanvas" />
        private void OnPaintCanvas(object sender, SKPaintSurfaceEventArgs e)
        {
            SKCanvas canvas = e.Surface.Canvas;
            int width = e.Info.Width;
            int height = e.Info.Height;
            if (SendScreenImage)
            {
                var canvasSize = new SKSize(width, height);
                if (_screenCanvasSize != canvasSize)
                {
                    _surface?.Dispose();
                    _grContext?.Dispose();

                    _grContext = GRContext.Create(GRBackend.OpenGL);
                    _surface = SKSurface.Create(_grContext, true, new SKImageInfo(width, height));

                    _screenCanvasSize = canvasSize;
                }
                MY_Skia_PaintSurfaceCommonNew(TheTarget, _surface, false, ref FirstPaint, QueuDrawings, ref Range, ref LastSend, signalrConnection, sendSpan, broadcastImageArray, false, listOfAllW);
                _surface.Canvas.Flush();
// if this is not called memory does not get relased (can it have to with garbage collection??
                if (ShowScreenUpdate)
                {
                    canvas.DrawSurface(_surface, new SKPoint(0f, 0f));
                }
            }
        }

Thank You

Most helpful comment

Hi Matthew
Thank you for you answer

It is a .NET Core 3.1
SkiaSharp.Views.WPF v1.68.3
SkiaSharp.Views.WindowsForms v1.68.3
SkiaSharp.Views v1.68.3
SkiaSharp.Svg v1.60.0
SkiaSharp.Extended v1.60.0

It's a bit complex aplication, but i will include the code section that causes the trouble. The memory consumption is really fast!
it is really obvious where memory consumes. If I comment out the line in the code surface.Canvas.DrawCircle((int)x, (int)y, 1f, paint); memory is not consumed.
And if i include the above line and call canvas.DrawSurface(_surface, new SKPoint(0f, 0f)); after the call to Skia_PaintSurfaceCommonNeWMinimal it is not consumed.
If i replace the line surface.Canvas.DrawCircle((int)x, (int)y, 1f, paint); with surface.Canvas.Clear( SKColors.Red); memory is NOT consumed

Thank You Peder
I get the _grContext from SkiaOpengl (.NET Framework 4.7.2) like this: private readonly WglContext _glContext = new WglContext();
The SkiaOpengl is part of the soulution and also use the latest SkiaSharp nugets and latest OpenTK 3.2.0.

    public static void Skia_PaintSurfaceCommonNeWMinimal( SKSurface surface, bool highResolution, ref bool isFirstPaint, ConcurrentQueue<drawingDataNew> queuDrawings, ref float range, ref DateTime lastSend, HubConnection signalrConnection, TimeSpan sendSpan, bool brodcast = false, bool UseRotateXY = false, List<WeatherObject> weather = null)
    {
        lock (LockingPaintThread)
        {
            //    var canvas = surface.Canvas;
            int width, height;
            bool retValue;
            drawingDataNew drawdata;

            if (highResolution)
            {
                height = 2048;
                width = 2048;
            }
            else
            {
                height = 1024;
                width = 1024;
            }

            float canvasHeight = surface.Canvas.LocalClipBounds.Height;
            float canvasWidth = surface.Canvas.LocalClipBounds.Width;

            if (isFirstPaint)
            {

                isFirstPaint = false;
            }

            {


                int oldzoom = 0;
                int oldangle = 0;
                int imgw = 1024;
                int imgh = 1024;
                int midx, midy;
                float scale = 1;
                int[] pixel = new int[3];
                midx = width / 2 + xOffset;
                midy = height / 2 + yOffset;

                    while (queuDrawings.Count > 0)
                    {
                        retValue = queuDrawings.TryDequeue(out drawdata);     
                        if (retValue)
                        {
                            if (range != drawdata.range)
                            {
                                surface.Canvas.DrawCircle(width / 2f + xOffset, height / 2f + yOffset, width / 2, radarBackGroundPaint);
                                surface.Canvas.DrawCircle(width / 2f + xOffset, height / 2f + yOffset, 2f, blue);
                                Skia_WriteRange(surface.Canvas, (int)drawdata.range, "Aldrich-Regular.ttf", new SKPoint(100, 20));
                                range = drawdata.range;
                            }
                            byte[] spoke = new byte[512];
                            if (highResolution)
                                spoke = drawdata.dataHighRes;
                            else
                                spoke = drawdata.data;
                            var angle = drawdata.degree;
                            double rad = ToRadian(angle);
                            double radcos = Math.Cos(rad);
                            double radsin = Math.Sin(rad);
                            int dataLength = 512;
                            if (highResolution)
                                dataLength = 1024;
                            for (int k = 0; k < dataLength; k++)
                            {
                                int valpunkt = spoke[k];
                                {
                                    var paint = theColorWeak;
                                    if (valpunkt == 0)
                                        paint = radarBackGroundPaint;
                                    else if (valpunkt > ThresholdRed)
                                        paint = theColorStrong;
                                    else if (valpunkt > ThresholdGreen)
                                        paint = theColorIntermediate;
                                    float r = k;//*scale;
                                    float x = (float)(midx + radcos * r);
                                    float y = (float)(midy + radsin * r);
                                    paint.IsAntialias = true;
                                         surface.Canvas.DrawCircle((int)x, (int)y, 1f, paint); //// Comment out and memory stops beeing consumed
                                }

                            }

                            spoke = null;
                        }
                        drawdata = null;                         
                    }
            }
        }
    }

All 4 comments

What version of SkiaSharp are you using? Is this .NET Core or Full Framework?

Also, is it possible to attach a sample that I can have a look at?

You code n this issue seems to be correct, so I can't say what might be causing the issue. What I can say is try not disposing and recreating the GRContext - that is fine to reuse as long as you are still using the same OpenGL context. You just need to re-create the surface.

What happens if you also just draw a blank screen for the GPU - say clear with red. Does it still leak?

Hi Matthew
Thank you for you answer

It is a .NET Core 3.1
SkiaSharp.Views.WPF v1.68.3
SkiaSharp.Views.WindowsForms v1.68.3
SkiaSharp.Views v1.68.3
SkiaSharp.Svg v1.60.0
SkiaSharp.Extended v1.60.0

It's a bit complex aplication, but i will include the code section that causes the trouble. The memory consumption is really fast!
it is really obvious where memory consumes. If I comment out the line in the code surface.Canvas.DrawCircle((int)x, (int)y, 1f, paint); memory is not consumed.
And if i include the above line and call canvas.DrawSurface(_surface, new SKPoint(0f, 0f)); after the call to Skia_PaintSurfaceCommonNeWMinimal it is not consumed.
If i replace the line surface.Canvas.DrawCircle((int)x, (int)y, 1f, paint); with surface.Canvas.Clear( SKColors.Red); memory is NOT consumed

Thank You Peder
I get the _grContext from SkiaOpengl (.NET Framework 4.7.2) like this: private readonly WglContext _glContext = new WglContext();
The SkiaOpengl is part of the soulution and also use the latest SkiaSharp nugets and latest OpenTK 3.2.0.

    public static void Skia_PaintSurfaceCommonNeWMinimal( SKSurface surface, bool highResolution, ref bool isFirstPaint, ConcurrentQueue<drawingDataNew> queuDrawings, ref float range, ref DateTime lastSend, HubConnection signalrConnection, TimeSpan sendSpan, bool brodcast = false, bool UseRotateXY = false, List<WeatherObject> weather = null)
    {
        lock (LockingPaintThread)
        {
            //    var canvas = surface.Canvas;
            int width, height;
            bool retValue;
            drawingDataNew drawdata;

            if (highResolution)
            {
                height = 2048;
                width = 2048;
            }
            else
            {
                height = 1024;
                width = 1024;
            }

            float canvasHeight = surface.Canvas.LocalClipBounds.Height;
            float canvasWidth = surface.Canvas.LocalClipBounds.Width;

            if (isFirstPaint)
            {

                isFirstPaint = false;
            }

            {


                int oldzoom = 0;
                int oldangle = 0;
                int imgw = 1024;
                int imgh = 1024;
                int midx, midy;
                float scale = 1;
                int[] pixel = new int[3];
                midx = width / 2 + xOffset;
                midy = height / 2 + yOffset;

                    while (queuDrawings.Count > 0)
                    {
                        retValue = queuDrawings.TryDequeue(out drawdata);     
                        if (retValue)
                        {
                            if (range != drawdata.range)
                            {
                                surface.Canvas.DrawCircle(width / 2f + xOffset, height / 2f + yOffset, width / 2, radarBackGroundPaint);
                                surface.Canvas.DrawCircle(width / 2f + xOffset, height / 2f + yOffset, 2f, blue);
                                Skia_WriteRange(surface.Canvas, (int)drawdata.range, "Aldrich-Regular.ttf", new SKPoint(100, 20));
                                range = drawdata.range;
                            }
                            byte[] spoke = new byte[512];
                            if (highResolution)
                                spoke = drawdata.dataHighRes;
                            else
                                spoke = drawdata.data;
                            var angle = drawdata.degree;
                            double rad = ToRadian(angle);
                            double radcos = Math.Cos(rad);
                            double radsin = Math.Sin(rad);
                            int dataLength = 512;
                            if (highResolution)
                                dataLength = 1024;
                            for (int k = 0; k < dataLength; k++)
                            {
                                int valpunkt = spoke[k];
                                {
                                    var paint = theColorWeak;
                                    if (valpunkt == 0)
                                        paint = radarBackGroundPaint;
                                    else if (valpunkt > ThresholdRed)
                                        paint = theColorStrong;
                                    else if (valpunkt > ThresholdGreen)
                                        paint = theColorIntermediate;
                                    float r = k;//*scale;
                                    float x = (float)(midx + radcos * r);
                                    float y = (float)(midy + radsin * r);
                                    paint.IsAntialias = true;
                                         surface.Canvas.DrawCircle((int)x, (int)y, 1f, paint); //// Comment out and memory stops beeing consumed
                                }

                            }

                            spoke = null;
                        }
                        drawdata = null;                         
                    }
            }
        }
    }

I have the same issue

Same issue here: and found out that calling DrawText seems fine, calling DrawPath causes memory leak.

Was this page helpful?
0 / 5 - 0 ratings