Browse Source

[fbdev] Added a mode that should not waste time in FBIO_WAITFORVSYNC (#17124)

* [fbdev] Added a mode that should not waste time in FBIO_WAITFORVSYNC

* Log
pull/17180/head
Nikita Tsukanov 2 years ago
committed by GitHub
parent
commit
e296589b8f
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 81
      src/Linux/Avalonia.LinuxFramebuffer/Output/FbDevBackBuffer.cs
  2. 6
      src/Linux/Avalonia.LinuxFramebuffer/Output/FbDevOutputOptions.cs
  3. 2
      src/Linux/Avalonia.LinuxFramebuffer/Output/FbdevOutput.cs

81
src/Linux/Avalonia.LinuxFramebuffer/Output/FbDevBackBuffer.cs

@ -3,6 +3,7 @@
using System;
using System.Runtime.InteropServices;
using System.Threading;
using Avalonia.Logging;
using Avalonia.Platform;
namespace Avalonia.LinuxFramebuffer.Output
@ -14,14 +15,76 @@ namespace Avalonia.LinuxFramebuffer.Output
private readonly fb_var_screeninfo _varInfo;
private readonly IntPtr _targetAddress;
private readonly object _lock = new object();
private readonly AsyncFbBlitter? _asyncBlit;
public FbDevBackBuffer(int fb, fb_fix_screeninfo fixedInfo, fb_var_screeninfo varInfo, IntPtr targetAddress)
class AsyncFbBlitter : IDisposable
{
private readonly FbDevBackBuffer _fb;
private AutoResetEvent _signalToThread = new AutoResetEvent(false);
private ManualResetEvent _transferCompleted = new ManualResetEvent(true);
private Thread _thread;
private volatile bool _exit;
public AsyncFbBlitter(FbDevBackBuffer fb)
{
_fb = fb;
_thread = new Thread(Worker)
{
IsBackground = true,
Name = "FbDevBackBuffer::AsyncBlitter",
Priority = ThreadPriority.Highest
};
_thread.Start();
}
private void Worker()
{
while (true)
{
_signalToThread.WaitOne();
if (_exit)
return;
try
{
_fb.BlitToDevice();
}
catch(Exception e)
{
Logger.TryGet(LogEventLevel.Fatal, "FBDEV")?.Log(this, "Unable to update framebuffer: " + e);
}
_transferCompleted.Set();
}
}
public void Dispose()
{
_exit = true;
_signalToThread.Set();
_thread.Join();
_signalToThread.Dispose();
_transferCompleted.Dispose();
}
public void WaitForTransfer() => _transferCompleted.WaitOne();
public void BeginBlit()
{
_transferCompleted.Reset();
_signalToThread.Set();
}
}
public FbDevBackBuffer(int fb, fb_fix_screeninfo fixedInfo, fb_var_screeninfo varInfo, IntPtr targetAddress,
bool asyncBlit)
{
_fb = fb;
_fixedInfo = fixedInfo;
_varInfo = varInfo;
_targetAddress = targetAddress;
Address = Marshal.AllocHGlobal(RowBytes * Size.Height);
if (asyncBlit)
_asyncBlit = new AsyncFbBlitter(this);
}
@ -32,6 +95,9 @@ namespace Avalonia.LinuxFramebuffer.Output
Marshal.FreeHGlobal(Address);
Address = IntPtr.Zero;
}
if (_asyncBlit != null)
_asyncBlit.Dispose();
}
public static LockedFramebuffer LockFb(IntPtr address, fb_var_screeninfo varInfo,
@ -45,8 +111,15 @@ namespace Avalonia.LinuxFramebuffer.Output
: PixelFormat.Bgra8888, dispose);
}
private void BlitToDevice()
{
NativeUnsafeMethods.ioctl(_fb, FbIoCtl.FBIO_WAITFORVSYNC, null);
NativeUnsafeMethods.memcpy(_targetAddress, Address, new IntPtr(RowBytes * Size.Height));
}
public ILockedFramebuffer Lock(Vector dpi)
{
_asyncBlit?.WaitForTransfer();
Monitor.Enter(_lock);
try
{
@ -55,8 +128,10 @@ namespace Avalonia.LinuxFramebuffer.Output
{
try
{
NativeUnsafeMethods.ioctl(_fb, FbIoCtl.FBIO_WAITFORVSYNC, null);
NativeUnsafeMethods.memcpy(_targetAddress, Address, new IntPtr(RowBytes * Size.Height));
if (_asyncBlit != null)
_asyncBlit.BeginBlit();
else
BlitToDevice();
}
finally
{

6
src/Linux/Avalonia.LinuxFramebuffer/Output/FbDevOutputOptions.cs

@ -29,4 +29,10 @@ public class FbDevOutputOptions
/// The initial scale factor to use
/// </summary>
public double Scaling { get; set; } = 1;
/// <summary>
/// If set to true, FBIO_WAITFORVSYNC ioctl and following memcpy call will run on a dedicated thread
/// saving current one from doing nothing in a blocking call
/// </summary>
public bool? UseAsyncFrontBufferBlit { get; set; }
}

2
src/Linux/Avalonia.LinuxFramebuffer/Output/FbdevOutput.cs

@ -181,7 +181,7 @@ namespace Avalonia.LinuxFramebuffer
_lockedAtLeastOnce = true;
properties = new FramebufferLockProperties(retained);
return (_backBuffer ??=
new FbDevBackBuffer(_fd, _fixedInfo, _varInfo, _mappedAddress))
new FbDevBackBuffer(_fd, _fixedInfo, _varInfo, _mappedAddress, _options.UseAsyncFrontBufferBlit == true))
.Lock(new Vector(96, 96) * Scaling);
}

Loading…
Cancel
Save