|
|
|
@ -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 |
|
|
|
{ |
|
|
|
|