Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 2 commits into from
Sep 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 78 additions & 3 deletions src/Linux/Avalonia.LinuxFramebuffer/Output/FbDevBackBuffer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System;
using System.Runtime.InteropServices;
using System.Threading;
using Avalonia.Logging;
using Avalonia.Platform;

namespace Avalonia.LinuxFramebuffer.Output
Expand All @@ -14,14 +15,76 @@ internal unsafe class FbDevBackBuffer : IDisposable
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);
}


Expand All @@ -32,6 +95,9 @@ public void Dispose()
Marshal.FreeHGlobal(Address);
Address = IntPtr.Zero;
}

if (_asyncBlit != null)
_asyncBlit.Dispose();
}

public static LockedFramebuffer LockFb(IntPtr address, fb_var_screeninfo varInfo,
Expand All @@ -45,8 +111,15 @@ public static LockedFramebuffer LockFb(IntPtr address, fb_var_screeninfo varInfo
: 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
{
Expand All @@ -55,8 +128,10 @@ public ILockedFramebuffer Lock(Vector dpi)
{
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
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 1 addition & 1 deletion src/Linux/Avalonia.LinuxFramebuffer/Output/FbdevOutput.cs
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ private ILockedFramebuffer Lock(out FramebufferLockProperties properties)
_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);
}

Expand Down
Loading