Browse Source

Fix Metal renderer memory leakage on macOS (#21099)

* Fix Metal renderer memory leakage on macOS

* Remove no-op ResetContext call

* Disposable wrapper

* Success flag in ctor to dispose

* Rename variable name to _autoReleasePool

* Abstraction over AutoReleasePool

* Use Interlocked.Exchange in AutoReleasePool

---------

Co-authored-by: Julien Lebosquain <julien@lebosquain.net>
release/latest
Nikolay Borisenko 6 months ago
committed by Julien Lebosquain
parent
commit
12e0ef8632
  1. 30
      src/Skia/Avalonia.Skia/Gpu/Metal/AutoReleasePool.cs
  2. 36
      src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalGpu.cs

30
src/Skia/Avalonia.Skia/Gpu/Metal/AutoReleasePool.cs

@ -0,0 +1,30 @@
using System;
using System.Runtime.InteropServices;
using System.Threading;
namespace Avalonia.Skia.Metal;
internal sealed partial class AutoReleasePool : IDisposable
{
private IntPtr _pool;
public AutoReleasePool()
{
_pool = Push();
}
public void Dispose()
{
var pool = Interlocked.Exchange(ref _pool, IntPtr.Zero);
if (pool != IntPtr.Zero)
{
Pop(pool);
}
}
[LibraryImport("libobjc", EntryPoint = "objc_autoreleasePoolPush")]
private static partial IntPtr Push();
[LibraryImport("libobjc", EntryPoint = "objc_autoreleasePoolPop")]
private static partial void Pop(IntPtr pool);
}

36
src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalGpu.cs

@ -97,15 +97,33 @@ internal class SkiaMetalGpu : ISkiaGpu
public ISkiaGpuRenderSession BeginRenderingSession(IRenderTarget.RenderTargetSceneInfo sceneInfo)
{
// TODO: use expectedPixelSize
var session = (_target ?? throw new ObjectDisposedException(nameof(SkiaMetalRenderTarget))).BeginRendering();
var backendTarget = new GRBackendRenderTarget(session.Size.Width, session.Size.Height,
new GRMtlTextureInfo(session.Texture));
IMetalPlatformSurfaceRenderingSession? session = null;
GRBackendRenderTarget? backendTarget = null;
SKSurface? surface = null;
var success = false;
try
{
session = (_target ?? throw new ObjectDisposedException(nameof(SkiaMetalRenderTarget))).BeginRendering();
backendTarget = new GRBackendRenderTarget(session.Size.Width, session.Size.Height,
new GRMtlTextureInfo(session.Texture));
var surface = SKSurface.Create(_gpu._context!, backendTarget,
session.IsYFlipped ? GRSurfaceOrigin.BottomLeft : GRSurfaceOrigin.TopLeft,
SKColorType.Bgra8888);
surface = SKSurface.Create(_gpu._context!, backendTarget,
session.IsYFlipped ? GRSurfaceOrigin.BottomLeft : GRSurfaceOrigin.TopLeft,
SKColorType.Bgra8888);
return new SkiaMetalRenderSession(_gpu, surface, session, backendTarget);
var result = new SkiaMetalRenderSession(_gpu, surface, session, backendTarget);
success = true;
return result;
}
finally
{
if (!success)
{
surface?.Dispose();
backendTarget?.Dispose();
session?.Dispose();
}
}
}
public PlatformRenderTargetState State => _target?.State ?? PlatformRenderTargetState.Disposed;
@ -117,12 +135,14 @@ internal class SkiaMetalGpu : ISkiaGpu
private SKSurface? _surface;
private IMetalPlatformSurfaceRenderingSession? _session;
private GRBackendRenderTarget? _backendTarget;
private readonly AutoReleasePool _autoReleasePool;
public SkiaMetalRenderSession(SkiaMetalGpu gpu,
SKSurface surface,
IMetalPlatformSurfaceRenderingSession session,
GRBackendRenderTarget backendTarget)
{
_autoReleasePool = new AutoReleasePool();
_gpu = gpu;
_surface = surface;
_session = session;
@ -141,6 +161,8 @@ internal class SkiaMetalGpu : ISkiaGpu
_session = null;
_backendTarget?.Dispose();
_backendTarget = null;
_autoReleasePool.Dispose();
}
public GRContext GrContext => _gpu._context!;

Loading…
Cancel
Save