From 4d9b0c09dbc846fb620329ad2449c13e348ef535 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Tue, 21 Sep 2021 15:55:39 +0300 Subject: [PATCH] [FBDEV] Reuse the same memory region for the back buffer --- .../LockedFramebuffer.cs | 46 ------------ .../Output/FbDevBackBuffer.cs | 70 +++++++++++++++++++ .../Output/FbdevOutput.cs | 7 +- 3 files changed, 76 insertions(+), 47 deletions(-) delete mode 100644 src/Linux/Avalonia.LinuxFramebuffer/LockedFramebuffer.cs create mode 100644 src/Linux/Avalonia.LinuxFramebuffer/Output/FbDevBackBuffer.cs diff --git a/src/Linux/Avalonia.LinuxFramebuffer/LockedFramebuffer.cs b/src/Linux/Avalonia.LinuxFramebuffer/LockedFramebuffer.cs deleted file mode 100644 index 87c7b64c26..0000000000 --- a/src/Linux/Avalonia.LinuxFramebuffer/LockedFramebuffer.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using System.Runtime.InteropServices; -using Avalonia.Platform; - -namespace Avalonia.LinuxFramebuffer -{ - unsafe class LockedFramebuffer : ILockedFramebuffer - { - private readonly int _fb; - private readonly fb_fix_screeninfo _fixedInfo; - private fb_var_screeninfo _varInfo; - private readonly IntPtr _address; - - public LockedFramebuffer(int fb, fb_fix_screeninfo fixedInfo, fb_var_screeninfo varInfo, IntPtr address, Vector dpi) - { - _fb = fb; - _fixedInfo = fixedInfo; - _varInfo = varInfo; - _address = address; - Dpi = dpi; - //Use double buffering to avoid flicker - Address = Marshal.AllocHGlobal(RowBytes * Size.Height); - } - - - void VSync() - { - NativeUnsafeMethods.ioctl(_fb, FbIoCtl.FBIO_WAITFORVSYNC, null); - } - - public void Dispose() - { - VSync(); - NativeUnsafeMethods.memcpy(_address, Address, new IntPtr(RowBytes * Size.Height)); - - Marshal.FreeHGlobal(Address); - Address = IntPtr.Zero; - } - - public IntPtr Address { get; private set; } - public PixelSize Size => new PixelSize((int)_varInfo.xres, (int) _varInfo.yres); - public int RowBytes => (int) _fixedInfo.line_length; - public Vector Dpi { get; } - public PixelFormat Format => _varInfo.bits_per_pixel == 16 ? PixelFormat.Rgb565 : _varInfo.blue.offset == 16 ? PixelFormat.Rgba8888 : PixelFormat.Bgra8888; - } -} diff --git a/src/Linux/Avalonia.LinuxFramebuffer/Output/FbDevBackBuffer.cs b/src/Linux/Avalonia.LinuxFramebuffer/Output/FbDevBackBuffer.cs new file mode 100644 index 0000000000..7afad13bb6 --- /dev/null +++ b/src/Linux/Avalonia.LinuxFramebuffer/Output/FbDevBackBuffer.cs @@ -0,0 +1,70 @@ +using System; +using System.Runtime.InteropServices; +using System.Threading; +using Avalonia.Platform; + +namespace Avalonia.LinuxFramebuffer.Output +{ + internal unsafe class FbDevBackBuffer : IDisposable + { + private readonly int _fb; + private readonly fb_fix_screeninfo _fixedInfo; + private readonly fb_var_screeninfo _varInfo; + private readonly IntPtr _targetAddress; + private readonly object _lock = new object(); + + public FbDevBackBuffer(int fb, fb_fix_screeninfo fixedInfo, fb_var_screeninfo varInfo, IntPtr targetAddress) + { + _fb = fb; + _fixedInfo = fixedInfo; + _varInfo = varInfo; + _targetAddress = targetAddress; + Address = Marshal.AllocHGlobal(RowBytes * Size.Height); + } + + + public void Dispose() + { + if (Address != IntPtr.Zero) + { + Marshal.FreeHGlobal(Address); + Address = IntPtr.Zero; + } + } + + public ILockedFramebuffer Lock(Vector dpi) + { + Monitor.Enter(_lock); + try + { + return new LockedFramebuffer(Address, + new PixelSize((int)_varInfo.xres, (int)_varInfo.yres), + (int)_fixedInfo.line_length, dpi, + _varInfo.bits_per_pixel == 16 ? PixelFormat.Rgb565 + : _varInfo.blue.offset == 16 ? PixelFormat.Rgba8888 + : PixelFormat.Bgra8888, + () => + { + try + { + NativeUnsafeMethods.ioctl(_fb, FbIoCtl.FBIO_WAITFORVSYNC, null); + NativeUnsafeMethods.memcpy(_targetAddress, Address, new IntPtr(RowBytes * Size.Height)); + } + finally + { + Monitor.Exit(_lock); + } + }); + } + catch + { + Monitor.Exit(_lock); + throw; + } + } + + public IntPtr Address { get; private set; } + public PixelSize Size => new PixelSize((int)_varInfo.xres, (int) _varInfo.yres); + public int RowBytes => (int) _fixedInfo.line_length; + } +} diff --git a/src/Linux/Avalonia.LinuxFramebuffer/Output/FbdevOutput.cs b/src/Linux/Avalonia.LinuxFramebuffer/Output/FbdevOutput.cs index 61f00b2795..f3f9a12ac8 100644 --- a/src/Linux/Avalonia.LinuxFramebuffer/Output/FbdevOutput.cs +++ b/src/Linux/Avalonia.LinuxFramebuffer/Output/FbdevOutput.cs @@ -14,6 +14,7 @@ namespace Avalonia.LinuxFramebuffer private fb_var_screeninfo _varInfo; private IntPtr _mappedLength; private IntPtr _mappedAddress; + private FbDevBackBuffer _backBuffer; public double Scaling { get; set; } /// @@ -146,7 +147,9 @@ namespace Avalonia.LinuxFramebuffer { if (_fd <= 0) throw new ObjectDisposedException("LinuxFramebuffer"); - return new LockedFramebuffer(_fd, _fixedInfo, _varInfo, _mappedAddress, new Vector(96, 96) * Scaling); + return (_backBuffer ??= + new FbDevBackBuffer(_fd, _fixedInfo, _varInfo, _mappedAddress)) + .Lock(new Vector(96, 96) * Scaling); } @@ -165,6 +168,8 @@ namespace Avalonia.LinuxFramebuffer public void Dispose() { + _backBuffer?.Dispose(); + _backBuffer = null; ReleaseUnmanagedResources(); GC.SuppressFinalize(this); }