Browse Source

Merge pull request #11930 from AvaloniaUI/cleanup-runtime-platform-interface

Cleanup runtime platform classes #2
release/11.0.0-rc2.1
Max Katz 3 years ago
committed by GitHub
parent
commit
54d18061ed
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      src/Android/Avalonia.Android/AndroidPlatform.cs
  2. 122
      src/Avalonia.Base/Compatibility/NativeLibrary.cs
  3. 16
      src/Avalonia.Base/Platform/IRuntimePlatform.cs
  4. 161
      src/Avalonia.Base/Platform/Internal/DynLoader.cs
  5. 2
      src/Avalonia.Base/Platform/Internal/UnmanagedBlob.cs
  6. 24
      src/Avalonia.Base/Platform/Interop/IDynamicLibraryLoader.cs
  7. 12
      src/Avalonia.Base/Platform/StandardRuntimePlatform.cs
  8. 28
      src/Avalonia.Base/Platform/StandardRuntimePlatformServices.cs
  9. 8
      src/Avalonia.Base/Rendering/DefaultRenderTimer.cs
  10. 54
      src/Avalonia.Controls/AppBuilder.cs
  11. 1
      src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs
  12. 4
      src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs
  13. 5
      src/Avalonia.OpenGL/Egl/EglInterface.cs
  14. 6
      src/Avalonia.X11/X11CursorFactory.cs
  15. 5
      src/Avalonia.X11/X11Framebuffer.cs
  16. 4
      src/Avalonia.X11/X11Platform.cs
  17. 1
      src/Browser/Avalonia.Browser/BrowserAppBuilder.cs
  18. 18
      src/Browser/Avalonia.Browser/BrowserRuntimePlatform.cs
  19. 32
      src/Browser/Avalonia.Browser/WindowingPlatform.cs
  20. 6
      src/Headless/Avalonia.Headless/AvaloniaHeadlessPlatform.cs
  21. 5
      src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs
  22. 41
      src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs
  23. 20
      src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs
  24. 8
      src/Windows/Avalonia.Win32/FramebufferManager.cs
  25. 4
      src/Windows/Avalonia.Win32/Win32Platform.cs
  26. 1
      src/iOS/Avalonia.iOS/Platform.cs
  27. 2
      tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs

1
src/Android/Avalonia.Android/AndroidPlatform.cs

@ -20,6 +20,7 @@ namespace Avalonia
public static AppBuilder UseAndroid(this AppBuilder builder)
{
return builder
.UseStandardRuntimePlatformSubsystem()
.UseWindowingSubsystem(() => AndroidPlatform.Initialize(), "Android")
.UseSkia();
}

122
src/Avalonia.Base/Compatibility/NativeLibrary.cs

@ -0,0 +1,122 @@
using System;
using System.ComponentModel;
using System.Reflection;
using System.Runtime.InteropServices;
using Avalonia.Compatibility;
using Avalonia.Platform.Interop;
namespace Avalonia.Compatibility
{
internal class NativeLibraryEx
{
#if NET6_0_OR_GREATER
public static IntPtr Load(string dll, Assembly assembly) => NativeLibrary.Load(dll, assembly, null);
public static IntPtr Load(string dll) => NativeLibrary.Load(dll);
public static bool TryGetExport(IntPtr handle, string name, out IntPtr address) =>
NativeLibrary.TryGetExport(handle, name, out address);
#else
public static IntPtr Load(string dll, Assembly assembly) => Load(dll);
public static IntPtr Load(string dll)
{
var handle = DlOpen!(dll);
if (handle != IntPtr.Zero)
return handle;
throw new InvalidOperationException("Unable to load " + dll, DlError!());
}
public static bool TryGetExport(IntPtr handle, string name, out IntPtr address)
{
try
{
address = DlSym!(handle, name);
return address != default;
}
catch (Exception)
{
address = default;
return false;
}
}
static NativeLibraryEx()
{
if (OperatingSystemEx.IsWindows())
{
Win32Imports.Init();
}
else if (OperatingSystemEx.IsLinux() || OperatingSystemEx.IsMacOS())
{
var buffer = Marshal.AllocHGlobal(0x1000);
uname(buffer);
var unixName = Marshal.PtrToStringAnsi(buffer);
Marshal.FreeHGlobal(buffer);
if (unixName == "Darwin")
OsXImports.Init();
else
LinuxImports.Init();
}
}
private static Func<string, IntPtr>? DlOpen;
private static Func<IntPtr, string, IntPtr>? DlSym;
private static Func<Exception?>? DlError;
[DllImport("libc")]
static extern int uname(IntPtr buf);
static class Win32Imports
{
[DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
private static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
[DllImport("kernel32", EntryPoint = "LoadLibraryW", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern IntPtr LoadLibrary(string lpszLib);
public static void Init()
{
DlOpen = LoadLibrary;
DlSym = GetProcAddress;
DlError = () => new Win32Exception(Marshal.GetLastWin32Error());
}
}
static class LinuxImports
{
[DllImport("libdl.so.2")]
private static extern IntPtr dlopen(string path, int flags);
[DllImport("libdl.so.2")]
private static extern IntPtr dlsym(IntPtr handle, string symbol);
[DllImport("libdl.so.2")]
private static extern IntPtr dlerror();
public static void Init()
{
DlOpen = s => dlopen(s, 1);
DlSym = dlsym;
DlError = () => new InvalidOperationException(Marshal.PtrToStringAnsi(dlerror()));
}
}
static class OsXImports
{
[DllImport("/usr/lib/libSystem.dylib")]
private static extern IntPtr dlopen(string path, int flags);
[DllImport("/usr/lib/libSystem.dylib")]
private static extern IntPtr dlsym(IntPtr handle, string symbol);
[DllImport("/usr/lib/libSystem.dylib")]
private static extern IntPtr dlerror();
public static void Init()
{
DlOpen = s => dlopen(s, 1);
DlSym = dlsym;
DlError = () => new InvalidOperationException(Marshal.PtrToStringAnsi(dlerror()));
}
}
#endif
}
}

16
src/Avalonia.Base/Platform/IRuntimePlatform.cs

@ -3,24 +3,13 @@ using Avalonia.Metadata;
namespace Avalonia.Platform
{
[Unstable]
[PrivateApi]
public interface IRuntimePlatform
{
IDisposable StartSystemTimer(TimeSpan interval, Action tick);
RuntimePlatformInfo GetRuntimeInfo();
IUnmanagedBlob AllocBlob(int size);
}
[Unstable]
public interface IUnmanagedBlob : IDisposable
{
IntPtr Address { get; }
int Size { get; }
bool IsDisposed { get; }
}
[Unstable]
[PrivateApi]
public record struct RuntimePlatformInfo
{
public FormFactorType FormFactor => IsDesktop ? FormFactorType.Desktop :
@ -29,7 +18,6 @@ namespace Avalonia.Platform
public bool IsMobile { get; set; }
}
[Unstable]
public enum FormFactorType
{
Unknown,

161
src/Avalonia.Base/Platform/Internal/DynLoader.cs

@ -1,161 +0,0 @@
using System;
using System.Runtime.InteropServices;
using Avalonia.Platform.Interop;
// ReSharper disable InconsistentNaming
namespace Avalonia.Platform.Internal
{
class UnixLoader : IDynamicLibraryLoader
{
static class LinuxImports
{
[DllImport("libdl.so.2")]
private static extern IntPtr dlopen(string path, int flags);
[DllImport("libdl.so.2")]
private static extern IntPtr dlsym(IntPtr handle, string symbol);
[DllImport("libdl.so.2")]
private static extern IntPtr dlerror();
public static void Init()
{
DlOpen = dlopen;
DlSym = dlsym;
DlError = dlerror;
}
}
static class OsXImports
{
[DllImport("/usr/lib/libSystem.dylib")]
private static extern IntPtr dlopen(string path, int flags);
[DllImport("/usr/lib/libSystem.dylib")]
private static extern IntPtr dlsym(IntPtr handle, string symbol);
[DllImport("/usr/lib/libSystem.dylib")]
private static extern IntPtr dlerror();
public static void Init()
{
DlOpen = dlopen;
DlSym = dlsym;
DlError = dlerror;
}
}
[DllImport("libc")]
static extern int uname(IntPtr buf);
static UnixLoader()
{
var buffer = Marshal.AllocHGlobal(0x1000);
uname(buffer);
var unixName = Marshal.PtrToStringAnsi(buffer);
Marshal.FreeHGlobal(buffer);
if (unixName == "Darwin")
OsXImports.Init();
else
LinuxImports.Init();
}
private static Func<string, int, IntPtr>? DlOpen;
private static Func<IntPtr, string, IntPtr>? DlSym;
private static Func<IntPtr>? DlError;
// ReSharper restore InconsistentNaming
static string? DlErrorString() => Marshal.PtrToStringAnsi(DlError!.Invoke());
public IntPtr LoadLibrary(string dll)
{
var handle = DlOpen!.Invoke(dll, 1);
if (handle == IntPtr.Zero)
throw new DynamicLibraryLoaderException(DlErrorString()!);
return handle;
}
public IntPtr GetProcAddress(IntPtr dll, string proc, bool optional)
{
var ptr = DlSym!.Invoke(dll, proc);
if (ptr == IntPtr.Zero && !optional)
throw new DynamicLibraryLoaderException(DlErrorString()!);
return ptr;
}
}
internal class Win32Loader : IDynamicLibraryLoader
{
[DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
private static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
[DllImport("kernel32", EntryPoint = "LoadLibraryW", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern IntPtr LoadLibrary(string lpszLib);
IntPtr IDynamicLibraryLoader.LoadLibrary(string dll)
{
var handle = LoadLibrary(dll);
if (handle != IntPtr.Zero)
return handle;
var err = Marshal.GetLastWin32Error();
throw new DynamicLibraryLoaderException("Error loading " + dll + " error " + err);
}
IntPtr IDynamicLibraryLoader.GetProcAddress(IntPtr dll, string proc, bool optional)
{
var ptr = GetProcAddress(dll, proc);
if (ptr == IntPtr.Zero && !optional)
throw new DynamicLibraryLoaderException("Error " + Marshal.GetLastWin32Error());
return ptr;
}
}
#if NET6_0_OR_GREATER
internal class Net6Loader : IDynamicLibraryLoader
{
public IntPtr LoadLibrary(string dll)
{
try
{
return NativeLibrary.Load(dll);
}
catch (Exception ex)
{
throw new DynamicLibraryLoaderException("Error loading " + dll, ex);
}
}
public IntPtr GetProcAddress(IntPtr dll, string proc, bool optional)
{
try
{
if (optional)
{
return NativeLibrary.TryGetExport(dll, proc, out var address) ? address : default;
}
return NativeLibrary.GetExport(dll, proc);
}
catch (Exception ex)
{
throw new DynamicLibraryLoaderException("Error " + dll, ex);
}
}
}
#endif
internal class NotSupportedLoader : IDynamicLibraryLoader
{
IntPtr IDynamicLibraryLoader.LoadLibrary(string dll)
{
throw new PlatformNotSupportedException();
}
IntPtr IDynamicLibraryLoader.GetProcAddress(IntPtr dll, string proc, bool optional)
{
throw new PlatformNotSupportedException();
}
}
}

2
src/Avalonia.Base/Platform/Internal/UnmanagedBlob.cs

@ -6,7 +6,7 @@ using System.Threading;
namespace Avalonia.Platform.Internal;
internal class UnmanagedBlob : IUnmanagedBlob
internal class UnmanagedBlob
{
private IntPtr _address;
private readonly object _lock = new object();

24
src/Avalonia.Base/Platform/Interop/IDynamicLibraryLoader.cs

@ -1,24 +0,0 @@
using System;
using Avalonia.Metadata;
namespace Avalonia.Platform.Interop
{
public interface IDynamicLibraryLoader
{
IntPtr LoadLibrary(string dll);
IntPtr GetProcAddress(IntPtr dll, string proc, bool optional);
}
public class DynamicLibraryLoaderException : Exception
{
public DynamicLibraryLoaderException(string message) : base(message)
{
}
public DynamicLibraryLoaderException(string message, Exception innerException) : base(message, innerException)
{
}
}
}

12
src/Avalonia.Base/Platform/StandardRuntimePlatform.cs

@ -1,26 +1,20 @@
using System;
using System.Threading;
using Avalonia.Compatibility;
using Avalonia.Metadata;
using Avalonia.Platform.Internal;
namespace Avalonia.Platform
{
[PrivateApi]
public class StandardRuntimePlatform : IRuntimePlatform
{
public IDisposable StartSystemTimer(TimeSpan interval, Action tick)
{
return new Timer(_ => tick(), null, interval, interval);
}
public IUnmanagedBlob AllocBlob(int size) => new UnmanagedBlob(size);
private static readonly RuntimePlatformInfo s_info = new()
{
IsDesktop = OperatingSystemEx.IsWindows() || OperatingSystemEx.IsMacOS() || OperatingSystemEx.IsLinux(),
IsMobile = OperatingSystemEx.IsAndroid() || OperatingSystemEx.IsIOS()
};
public virtual RuntimePlatformInfo GetRuntimeInfo() => s_info;
}
}

28
src/Avalonia.Base/Platform/StandardRuntimePlatformServices.cs

@ -3,27 +3,15 @@ using Avalonia.Compatibility;
using Avalonia.Platform.Internal;
using Avalonia.Platform.Interop;
namespace Avalonia.Platform
namespace Avalonia.Platform;
internal static class StandardRuntimePlatformServices
{
public static class StandardRuntimePlatformServices
public static void Register(Assembly? assembly = null)
{
public static void Register(Assembly? assembly = null)
{
var standardPlatform = new StandardRuntimePlatform();
AssetLoader.RegisterResUriParsers();
AvaloniaLocator.CurrentMutable
.Bind<IRuntimePlatform>().ToConstant(standardPlatform)
.Bind<IAssetLoader>().ToConstant(new StandardAssetLoader(assembly))
.Bind<IDynamicLibraryLoader>().ToConstant(
#if NET6_0_OR_GREATER
new Net6Loader()
#else
OperatingSystemEx.IsWindows() ? (IDynamicLibraryLoader)new Win32Loader()
: OperatingSystemEx.IsMacOS() || OperatingSystemEx.IsLinux() || OperatingSystemEx.IsAndroid() ? new UnixLoader()
: new NotSupportedLoader()
#endif
);
}
AssetLoader.RegisterResUriParsers();
AvaloniaLocator.CurrentMutable
.Bind<IRuntimePlatform>().ToSingleton<StandardRuntimePlatform>()
.Bind<IAssetLoader>().ToConstant(new StandardAssetLoader(assembly));
}
}

8
src/Avalonia.Base/Rendering/DefaultRenderTimer.cs

@ -1,4 +1,5 @@
using System;
using System.Threading;
using Avalonia.Metadata;
using Avalonia.Platform;
@ -14,7 +15,6 @@ namespace Avalonia.Rendering
[PrivateApi]
public class DefaultRenderTimer : IRenderTimer
{
private IRuntimePlatform? _runtime;
private int _subscriberCount;
private Action<TimeSpan>? _tick;
private IDisposable? _subscription;
@ -80,11 +80,9 @@ namespace Avalonia.Rendering
/// </remarks>
protected virtual IDisposable StartCore(Action<TimeSpan> tick)
{
_runtime ??= AvaloniaLocator.Current.GetRequiredService<IRuntimePlatform>();
var interval = TimeSpan.FromSeconds(1.0 / FramesPerSecond);
return _runtime.StartSystemTimer(
TimeSpan.FromSeconds(1.0 / FramesPerSecond),
() => tick(TimeSpan.FromMilliseconds(Environment.TickCount)));
return new Timer(_ => tick(TimeSpan.FromMilliseconds(Environment.TickCount)), null, interval, interval);
}
/// <summary>

54
src/Avalonia.Controls/AppBuilder.cs

@ -12,23 +12,23 @@ namespace Avalonia
/// <summary>
/// Initializes platform-specific services for an <see cref="Application"/>.
/// </summary>
public class AppBuilder
public sealed class AppBuilder
{
private static bool s_setupWasAlreadyCalled;
private Action? _optionsInitializers;
private Func<Application>? _appFactory;
private IApplicationLifetime? _lifetime;
/// <summary>
/// Gets or sets the <see cref="IRuntimePlatform"/> instance.
/// </summary>
public IRuntimePlatform RuntimePlatform { get; set; }
/// <summary>
/// Gets or sets a method to call the initialize the runtime platform services (e. g. AssetLoader)
/// </summary>
public Action RuntimePlatformServicesInitializer { get; private set; }
public Action? RuntimePlatformServicesInitializer { get; private set; }
/// <summary>
/// Gets the name of the currently selected windowing subsystem.
/// </summary>
public string? RuntimePlatformServicesName { get; private set; }
/// <summary>
/// Gets the <see cref="Application"/> instance being initialized.
/// </summary>
@ -70,21 +70,10 @@ namespace Avalonia
/// <summary>
/// Initializes a new instance of the <see cref="AppBuilder"/> class.
/// </summary>
public AppBuilder()
: this(new StandardRuntimePlatform(),
builder => StandardRuntimePlatformServices.Register(builder.ApplicationType?.Assembly))
private AppBuilder()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="AppBuilder"/> class.
/// </summary>
protected AppBuilder(IRuntimePlatform platform, Action<AppBuilder> platformServices)
{
RuntimePlatform = platform;
RuntimePlatformServicesInitializer = () => platformServices(this);
}
/// <summary>
/// Begin configuring an <see cref="Application"/>.
/// </summary>
@ -96,7 +85,6 @@ namespace Avalonia
return new AppBuilder()
{
ApplicationType = typeof(TApp),
// Needed for CoreRT compatibility
_appFactory = () => new TApp()
};
}
@ -155,7 +143,7 @@ namespace Avalonia
$"Input type either needs to have BuildAvaloniaApp -> AppBuilder method or inherit Application type.");
}
protected AppBuilder Self => this;
private AppBuilder Self => this;
public AppBuilder AfterSetup(Action<AppBuilder> callback)
{
@ -225,6 +213,30 @@ namespace Avalonia
RenderingSubsystemName = name;
return Self;
}
/// <summary>
/// Specifies a runtime platform subsystem to use.
/// </summary>
/// <param name="initializer">The method to call to initialize the runtime platform subsystem.</param>
/// <param name="name">The name of the runtime platform subsystem.</param>
/// <returns>An <see cref="AppBuilder"/> instance.</returns>
public AppBuilder UseRuntimePlatformSubsystem(Action initializer, string name = "")
{
RuntimePlatformServicesInitializer = initializer;
RuntimePlatformServicesName = name;
return Self;
}
/// <summary>
/// Specifies a standard runtime platform subsystem to use.
/// </summary>
/// <returns>An <see cref="AppBuilder"/> instance.</returns>
public AppBuilder UseStandardRuntimePlatformSubsystem()
{
RuntimePlatformServicesInitializer = () => StandardRuntimePlatformServices.Register(ApplicationType?.Assembly);
RuntimePlatformServicesName = nameof(StandardRuntimePlatform);
return Self;
}
/// <summary>
/// Configures platform-specific options

1
src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs

@ -138,6 +138,7 @@ namespace Avalonia.DesignerSupport.Remote
CommandLineArgs args, object obj)
{
var builder = (AppBuilder)obj;
builder = builder.UseStandardRuntimePlatformSubsystem();
if (args.Method == Methods.AvaloniaRemote)
builder.UseWindowingSubsystem(() => PreviewerWindowingPlatform.Initialize(transport));
if (args.Method == Methods.Html)

4
src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs

@ -9,7 +9,9 @@ namespace Avalonia
{
public static AppBuilder UseAvaloniaNative(this AppBuilder builder)
{
builder.UseWindowingSubsystem(() =>
builder
.UseStandardRuntimePlatformSubsystem()
.UseWindowingSubsystem(() =>
{
var platform = AvaloniaNativePlatform.Initialize(
AvaloniaLocator.Current.GetService<AvaloniaNativePlatformOptions>() ??

5
src/Avalonia.OpenGL/Egl/EglInterface.cs

@ -35,9 +35,8 @@ namespace Avalonia.OpenGL.Egl
static Func<string, IntPtr> Load(string library)
{
var dyn = AvaloniaLocator.Current.GetRequiredService<IDynamicLibraryLoader>();
var lib = dyn.LoadLibrary(library);
return (s) => dyn.GetProcAddress(lib, s, true);
var lib = NativeLibraryEx.Load(library);
return (s) => NativeLibraryEx.TryGetExport(lib, s, out var address) ? address : default;
}
// ReSharper disable UnassignedGetOnlyAutoProperty

6
src/Avalonia.X11/X11CursorFactory.cs

@ -5,6 +5,7 @@ using System.Runtime.InteropServices;
using Avalonia.Controls.Platform.Surfaces;
using Avalonia.Input;
using Avalonia.Platform;
using Avalonia.Platform.Internal;
using Avalonia.SourceGenerator;
using Avalonia.Utilities;
@ -92,17 +93,16 @@ namespace Avalonia.X11
private unsafe class XImageCursor : CursorImpl, IFramebufferPlatformSurface, IPlatformHandle
{
private readonly PixelSize _pixelSize;
private readonly IUnmanagedBlob _blob;
private readonly UnmanagedBlob _blob;
public XImageCursor(IntPtr display, IBitmapImpl bitmap, PixelPoint hotSpot)
{
var size = Marshal.SizeOf<XcursorImage>() +
(bitmap.PixelSize.Width * bitmap.PixelSize.Height * 4);
var runtimePlatform = AvaloniaLocator.Current.GetRequiredService<IRuntimePlatform>();
var platformRenderInterface = AvaloniaLocator.Current.GetRequiredService<IPlatformRenderInterface>();
_pixelSize = bitmap.PixelSize;
_blob = runtimePlatform.AllocBlob(size);
_blob = new UnmanagedBlob(size);
var image = (XcursorImage*)_blob.Address;
image->version = 1;

5
src/Avalonia.X11/X11Framebuffer.cs

@ -1,6 +1,7 @@
using System;
using System.IO;
using Avalonia.Platform;
using Avalonia.Platform.Internal;
using SkiaSharp;
using static Avalonia.X11.XLib;
namespace Avalonia.X11
@ -10,7 +11,7 @@ namespace Avalonia.X11
private readonly IntPtr _display;
private readonly IntPtr _xid;
private readonly int _depth;
private IUnmanagedBlob _blob;
private UnmanagedBlob _blob;
public X11Framebuffer(IntPtr display, IntPtr xid, int depth, int width, int height, double factor)
{
@ -25,7 +26,7 @@ namespace Avalonia.X11
RowBytes = width * 4;
Dpi = new Vector(96, 96) * factor;
Format = PixelFormat.Bgra8888;
_blob = AvaloniaLocator.Current.GetRequiredService<IRuntimePlatform>().AllocBlob(RowBytes * height);
_blob = new UnmanagedBlob(RowBytes * height);
Address = _blob.Address;
}

4
src/Avalonia.X11/X11Platform.cs

@ -344,7 +344,9 @@ namespace Avalonia
{
public static AppBuilder UseX11(this AppBuilder builder)
{
builder.UseWindowingSubsystem(() =>
builder
.UseStandardRuntimePlatformSubsystem()
.UseWindowingSubsystem(() =>
new AvaloniaX11Platform().Initialize(AvaloniaLocator.Current.GetService<X11PlatformOptions>() ??
new X11PlatformOptions()));
return builder;

1
src/Browser/Avalonia.Browser/BrowserAppBuilder.cs

@ -98,6 +98,7 @@ public static class BrowserAppBuilder
this AppBuilder builder)
{
return builder
.UseBrowserRuntimePlatformSubsystem()
.UseWindowingSubsystem(BrowserWindowingPlatform.Register)
.UseSkia();
}

18
src/Browser/Avalonia.Browser/BrowserRuntimePlatform.cs

@ -1,4 +1,5 @@
using System;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.JavaScript;
using System.Text.RegularExpressions;
@ -7,6 +8,23 @@ using Avalonia.Platform;
namespace Avalonia.Browser;
internal static class BrowserRuntimePlatformServices
{
public static AppBuilder UseBrowserRuntimePlatformSubsystem(this AppBuilder builder)
{
builder.UseRuntimePlatformSubsystem(() => Register(builder.ApplicationType?.Assembly), nameof(BrowserRuntimePlatform));
return builder;
}
public static void Register(Assembly? assembly = null)
{
AssetLoader.RegisterResUriParsers();
AvaloniaLocator.CurrentMutable
.Bind<IRuntimePlatform>().ToSingleton<BrowserRuntimePlatform>()
.Bind<IAssetLoader>().ToConstant(new StandardAssetLoader(assembly));
}
}
internal class BrowserRuntimePlatform : StandardRuntimePlatform
{
private static readonly Lazy<RuntimePlatformInfo> Info = new(() =>

32
src/Browser/Avalonia.Browser/WindowingPlatform.cs

@ -57,12 +57,11 @@ namespace Avalonia.Browser
public IDisposable StartTimer(DispatcherPriority priority, TimeSpan interval, Action tick)
{
return GetRuntimePlatform()
.StartSystemTimer(interval, () =>
{
Dispatcher.UIThread.RunJobs(priority);
tick();
});
return new Timer(_ =>
{
Dispatcher.UIThread.RunJobs(priority);
tick();
}, null, interval, interval);
}
public void Signal(DispatcherPriority priority)
@ -71,18 +70,16 @@ namespace Avalonia.Browser
return;
_signaled = true;
var interval = TimeSpan.FromMilliseconds(1);
IDisposable? disp = null;
disp = new Timer(_ =>
{
_signaled = false;
disp?.Dispose();
disp = GetRuntimePlatform()
.StartSystemTimer(TimeSpan.FromMilliseconds(1),
() =>
{
_signaled = false;
disp?.Dispose();
Signaled?.Invoke(null);
});
Signaled?.Invoke(null);
}, null, interval, interval);
}
public bool CurrentThreadIsLoopThread
@ -94,10 +91,5 @@ namespace Avalonia.Browser
}
public event Action<DispatcherPriority?>? Signaled;
private static IRuntimePlatform GetRuntimePlatform()
{
return AvaloniaLocator.Current.GetRequiredService<IRuntimePlatform>();
}
}
}

6
src/Headless/Avalonia.Headless/AvaloniaHeadlessPlatform.cs

@ -105,8 +105,10 @@ namespace Avalonia.Headless
public static AppBuilder UseHeadless(this AppBuilder builder, AvaloniaHeadlessPlatformOptions opts)
{
if(opts.UseHeadlessDrawing)
builder.UseRenderingSubsystem(HeadlessPlatformRenderInterface.Initialize, "Headless");
return builder.UseWindowingSubsystem(() => AvaloniaHeadlessPlatform.Initialize(opts), "Headless");
builder = builder.UseRenderingSubsystem(HeadlessPlatformRenderInterface.Initialize, "Headless");
return builder
.UseStandardRuntimePlatformSubsystem()
.UseWindowingSubsystem(() => AvaloniaHeadlessPlatform.Initialize(opts), "Headless");
}
}
}

5
src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs

@ -76,7 +76,10 @@ namespace Avalonia.LinuxFramebuffer
internal static LinuxFramebufferLifetime Initialize(AppBuilder builder, IOutputBackend outputBackend, IInputBackend? inputBackend)
{
var platform = new LinuxFramebufferPlatform(outputBackend);
builder.UseSkia().UseWindowingSubsystem(platform.Initialize, "fbdev");
builder
.UseStandardRuntimePlatformSubsystem()
.UseSkia()
.UseWindowingSubsystem(platform.Initialize, "fbdev");
return new LinuxFramebufferLifetime(platform._fb, inputBackend);
}
}

41
src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs

@ -2,6 +2,7 @@ using System;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Runtime.InteropServices;
using Avalonia.Compatibility;
using Avalonia.Platform.Interop;
using SkiaSharp;
using BindingFlags = System.Reflection.BindingFlags;
@ -24,19 +25,33 @@ internal unsafe class SkiaMetalApi
{
// Make sure that skia is loaded
GC.KeepAlive(new SKPaint());
var loader = AvaloniaLocator.Current.GetRequiredService<IDynamicLibraryLoader>();
#if NET6_0_OR_GREATER
var dll = NativeLibrary.Load("libSkiaSharp", typeof(SKPaint).Assembly, null);
#else
var dll = loader.LoadLibrary("libSkiaSharp");
#endif
_gr_direct_context_make_metal_with_options = (delegate* unmanaged[Stdcall] <IntPtr, IntPtr, IntPtr, IntPtr>)
loader.GetProcAddress(dll, "gr_direct_context_make_metal_with_options", false);
_gr_backendrendertarget_new_metal =
(delegate* unmanaged[Stdcall]<int, int, int, GRMtlTextureInfoNative*, IntPtr>)
loader.GetProcAddress(dll, "gr_backendrendertarget_new_metal", false);
var dll = NativeLibraryEx.Load("libSkiaSharp", typeof(SKPaint).Assembly);
IntPtr address;
if (NativeLibraryEx.TryGetExport(dll, "gr_direct_context_make_metal_with_options", out address))
{
_gr_direct_context_make_metal_with_options =
(delegate* unmanaged[Stdcall] <IntPtr, IntPtr, IntPtr, IntPtr>)address;
}
else
{
throw new InvalidOperationException(
"Unable to export gr_direct_context_make_metal_with_options. Make sure SkiaSharp is up to date.");
}
if(NativeLibraryEx.TryGetExport(dll, "gr_backendrendertarget_new_metal", out address))
{
_gr_backendrendertarget_new_metal =
(delegate* unmanaged[Stdcall]<int, int, int, GRMtlTextureInfoNative*, IntPtr>)address;
}
else
{
throw new InvalidOperationException(
"Unable to export gr_backendrendertarget_new_metal. Make sure SkiaSharp is up to date.");
}
_contextCtor = typeof(GRContext).GetConstructor(
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null,
new[] { typeof(IntPtr), typeof(bool) }, null) ?? throw new MissingMemberException("GRContext.ctor(IntPtr,bool)");

20
src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs

@ -3,6 +3,7 @@ using System.IO;
using System.Threading;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
using Avalonia.Platform.Internal;
using Avalonia.Skia.Helpers;
using SkiaSharp;
@ -97,21 +98,12 @@ namespace Avalonia.Skia
SKColorType colorType = format.ToSkColorType();
SKAlphaType alphaType = alphaFormat.ToSkAlphaType();
var runtimePlatform = AvaloniaLocator.Current.GetService<IRuntimePlatform>();
_bitmap = new SKBitmap();
if (runtimePlatform != null)
{
_bitmap = new SKBitmap();
var nfo = new SKImageInfo(size.Width, size.Height, colorType, alphaType);
var blob = runtimePlatform.AllocBlob(nfo.BytesSize);
var nfo = new SKImageInfo(size.Width, size.Height, colorType, alphaType);
var blob = new UnmanagedBlob(nfo.BytesSize);
_bitmap.InstallPixels(nfo, blob.Address, nfo.RowBytes, s_releaseDelegate, blob);
}
else
{
_bitmap = new SKBitmap(size.Width, size.Height, colorType, alphaType);
}
_bitmap.InstallPixels(nfo, blob.Address, nfo.RowBytes, s_releaseDelegate, blob);
_bitmap.Erase(SKColor.Empty);
}
@ -176,7 +168,7 @@ namespace Avalonia.Skia
/// <param name="ctx">Blob.</param>
private static void ReleaseProc(IntPtr address, object ctx)
{
((IUnmanagedBlob)ctx).Dispose();
((UnmanagedBlob)ctx).Dispose();
}
/// <summary>

8
src/Windows/Avalonia.Win32/FramebufferManager.cs

@ -2,6 +2,7 @@
using System.Threading;
using Avalonia.Controls.Platform.Surfaces;
using Avalonia.Platform;
using Avalonia.Platform.Internal;
using Avalonia.Win32.Interop;
namespace Avalonia.Win32
@ -107,8 +108,7 @@ namespace Avalonia.Win32
private static FramebufferData AllocateFramebufferData(int width, int height)
{
var service = AvaloniaLocator.Current.GetRequiredService<IRuntimePlatform>();
var bitmapBlob = service.AllocBlob(width * height * _bytesPerPixel);
var bitmapBlob = new UnmanagedBlob(width * height * _bytesPerPixel);
return new FramebufferData(bitmapBlob, width, height);
}
@ -157,7 +157,7 @@ namespace Avalonia.Win32
private readonly struct FramebufferData
{
public IUnmanagedBlob Data { get; }
public UnmanagedBlob Data { get; }
public PixelSize Size { get; }
@ -165,7 +165,7 @@ namespace Avalonia.Win32
public UnmanagedMethods.BITMAPINFOHEADER Header { get; }
public FramebufferData(IUnmanagedBlob data, int width, int height)
public FramebufferData(UnmanagedBlob data, int width, int height)
{
Data = data;
Size = new PixelSize(width, height);

4
src/Windows/Avalonia.Win32/Win32Platform.cs

@ -24,7 +24,9 @@ namespace Avalonia
{
public static AppBuilder UseWin32(this AppBuilder builder)
{
return builder.UseWindowingSubsystem(
return builder
.UseStandardRuntimePlatformSubsystem()
.UseWindowingSubsystem(
() => Win32.Win32Platform.Initialize(
AvaloniaLocator.Current.GetService<Win32PlatformOptions>() ?? new Win32PlatformOptions()),
"Win32");

1
src/iOS/Avalonia.iOS/Platform.cs

@ -16,6 +16,7 @@ namespace Avalonia
public static AppBuilder UseiOS(this AppBuilder builder)
{
return builder
.UseStandardRuntimePlatformSubsystem()
.UseWindowingSubsystem(iOS.Platform.Register, "iOS")
.UseSkia();
}

2
tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs

@ -20,7 +20,7 @@ namespace Avalonia.Benchmarks.Styling
var services = new TestServices(
assetLoader: new StandardAssetLoader(),
globalClock: new MockGlobalClock(),
platform: new AppBuilder().RuntimePlatform,
platform: new StandardRuntimePlatform(),
standardCursorFactory: Mock.Of<ICursorFactory>(),
theme: () => CreateTheme(),
windowingPlatform: new MockWindowingPlatform());

Loading…
Cancel
Save