From 7fa329606e151451612d0b6afeaa513d1526a993 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sun, 5 Feb 2017 14:59:27 +0300 Subject: [PATCH] Use SafeHandle for cairo surface and don't reuse ImageSurfaceFramebuffer to conserve RAM. Also fixes memory leak. --- src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj | 1 + src/Gtk/Avalonia.Gtk3/FramebufferManager.cs | 14 ++--------- .../Avalonia.Gtk3/ImageSurfaceFramebuffer.cs | 23 ++++++------------- src/Gtk/Avalonia.Gtk3/Interop/CairoSurface.cs | 20 ++++++++++++++++ src/Gtk/Avalonia.Gtk3/Interop/Native.cs | 12 +++++----- 5 files changed, 36 insertions(+), 34 deletions(-) create mode 100644 src/Gtk/Avalonia.Gtk3/Interop/CairoSurface.cs diff --git a/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj b/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj index fe0d10d184..785baeba8a 100644 --- a/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj +++ b/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj @@ -44,6 +44,7 @@ KeyTransform.cs + diff --git a/src/Gtk/Avalonia.Gtk3/FramebufferManager.cs b/src/Gtk/Avalonia.Gtk3/FramebufferManager.cs index c2685e8791..0a0a8fe0a6 100644 --- a/src/Gtk/Avalonia.Gtk3/FramebufferManager.cs +++ b/src/Gtk/Avalonia.Gtk3/FramebufferManager.cs @@ -10,7 +10,6 @@ namespace Avalonia.Gtk3 class FramebufferManager : IFramebufferPlatformSurface, IDisposable { private readonly TopLevelImpl _window; - private ImageSurfaceFramebuffer _fb; public FramebufferManager(TopLevelImpl window) { _window = window; @@ -18,25 +17,16 @@ namespace Avalonia.Gtk3 public void Dispose() { - _fb?.Deallocate(); + // } public ILockedFramebuffer Lock() { if(_window.CurrentCairoContext == IntPtr.Zero) throw new InvalidOperationException("Window is not in drawing state"); - - var ctx = _window.CurrentCairoContext; var width = (int) _window.ClientSize.Width; var height = (int) _window.ClientSize.Height; - if (_fb == null || _fb.Width != width || - _fb.Height != height) - { - _fb?.Dispose(); - _fb = new ImageSurfaceFramebuffer(width, height); - } - _fb.Prepare(ctx); - return _fb; + return new ImageSurfaceFramebuffer(_window.CurrentCairoContext, width, height); } } } diff --git a/src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs b/src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs index a2b06d0d5e..d1964bf05d 100644 --- a/src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs +++ b/src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs @@ -13,10 +13,11 @@ namespace Avalonia.Gtk3 class ImageSurfaceFramebuffer : ILockedFramebuffer { private IntPtr _context; - private IntPtr _surface; + private CairoSurface _surface; - public ImageSurfaceFramebuffer(int width, int height) + public ImageSurfaceFramebuffer(IntPtr context, int width, int height) { + _context = context; _surface = Native.CairoImageSurfaceCreate(1, width, height); Width = width; Height = height; @@ -24,27 +25,17 @@ namespace Avalonia.Gtk3 RowBytes = Native.CairoImageSurfaceGetStride(_surface); Native.CairoSurfaceFlush(_surface); } - - public void Prepare(IntPtr context) - { - _context = context; - } - - public void Deallocate() - { - Native.CairoSurfaceDestroy(_surface); - _surface = IntPtr.Zero; - } - + public void Dispose() { - if(_context == IntPtr.Zero || _surface == IntPtr.Zero) + if(_context == IntPtr.Zero || _surface == null) return; Native.CairoSurfaceMarkDirty(_surface); Native.CairoSetSourceSurface(_context, _surface, 0, 0); Native.CairoPaint(_context); _context = IntPtr.Zero; - + _surface.Dispose(); + _surface = null; } public IntPtr Address { get; } diff --git a/src/Gtk/Avalonia.Gtk3/Interop/CairoSurface.cs b/src/Gtk/Avalonia.Gtk3/Interop/CairoSurface.cs new file mode 100644 index 0000000000..7838be9305 --- /dev/null +++ b/src/Gtk/Avalonia.Gtk3/Interop/CairoSurface.cs @@ -0,0 +1,20 @@ +using System; +using System.Runtime.InteropServices; + +namespace Avalonia.Gtk3.Interop +{ + class CairoSurface : SafeHandle + { + public CairoSurface() : base(IntPtr.Zero, true) + { + } + + protected override bool ReleaseHandle() + { + Native.CairoSurfaceDestroy(handle); + return true; + } + + public override bool IsInvalid => handle == IntPtr.Zero; + } +} diff --git a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs index bf66ef0cbb..cffdebeeab 100644 --- a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs +++ b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs @@ -110,25 +110,25 @@ namespace Avalonia.Gtk3.Interop [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)] - public delegate IntPtr cairo_image_surface_create(int format, int width, int height); + public delegate CairoSurface cairo_image_surface_create(int format, int width, int height); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)] - public delegate IntPtr cairo_image_surface_get_data(IntPtr surface); + public delegate IntPtr cairo_image_surface_get_data(CairoSurface surface); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)] - public delegate int cairo_image_surface_get_stride(IntPtr surface); + public delegate int cairo_image_surface_get_stride(CairoSurface surface); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)] - public delegate void cairo_surface_mark_dirty(IntPtr surface); + public delegate void cairo_surface_mark_dirty(CairoSurface surface); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)] - public delegate void cairo_surface_flush(IntPtr surface); + public delegate void cairo_surface_flush(CairoSurface surface); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)] public delegate void cairo_surface_destroy(IntPtr surface); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)] - public delegate void cairo_set_source_surface(IntPtr cr, IntPtr surface, double x, double y); + public delegate void cairo_set_source_surface(IntPtr cr, CairoSurface surface, double x, double y); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)] public delegate void cairo_paint(IntPtr context);