From 02d198a09eb19e55ceab66a125c1aacd88021429 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Tue, 27 Jun 2023 17:27:07 -0400 Subject: [PATCH 01/10] Replace public IDynamicLibraryLoader with internal NativeLibraryEx helper --- .../Compatibility/NativeLibrary.cs | 119 +++++++++++++ .../Platform/Internal/DynLoader.cs | 161 ------------------ .../Platform/Interop/IDynamicLibraryLoader.cs | 24 --- .../StandardRuntimePlatformServices.cs | 11 +- src/Avalonia.OpenGL/Egl/EglInterface.cs | 5 +- 5 files changed, 122 insertions(+), 198 deletions(-) create mode 100644 src/Avalonia.Base/Compatibility/NativeLibrary.cs delete mode 100644 src/Avalonia.Base/Platform/Internal/DynLoader.cs delete mode 100644 src/Avalonia.Base/Platform/Interop/IDynamicLibraryLoader.cs diff --git a/src/Avalonia.Base/Compatibility/NativeLibrary.cs b/src/Avalonia.Base/Compatibility/NativeLibrary.cs new file mode 100644 index 00000000000..8c77ee928ae --- /dev/null +++ b/src/Avalonia.Base/Compatibility/NativeLibrary.cs @@ -0,0 +1,119 @@ +using System; +using System.ComponentModel; +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) => System.Runtime.InteropServices.NativeLibrary.Load(dll); + public static bool TryGetExport(IntPtr handle, string name, out IntPtr address) + => System.Runtime.InteropServices.NativeLibrary.TryGetExport(handle, name, out address); +#else + public static IntPtr Load(string dll) + { + var handle = DlOpen(dll); + if (handle != IntPtr.Zero) + return handle; + throw new InvalidOperationException("Unable to load " + dll, DlErrorString()); + } + + 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? DlOpen; + private static Func? DlSym; + private static Func? DlErrorString; + + [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; + DlErrorString = () => 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; + DlErrorString = () => 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; + DlErrorString = () => new InvalidOperationException(Marshal.PtrToStringAnsi(dlerror())); + } + } +#endif + } +} diff --git a/src/Avalonia.Base/Platform/Internal/DynLoader.cs b/src/Avalonia.Base/Platform/Internal/DynLoader.cs deleted file mode 100644 index 07903669b19..00000000000 --- a/src/Avalonia.Base/Platform/Internal/DynLoader.cs +++ /dev/null @@ -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? DlOpen; - private static Func? DlSym; - private static Func? 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(); - } - } -} diff --git a/src/Avalonia.Base/Platform/Interop/IDynamicLibraryLoader.cs b/src/Avalonia.Base/Platform/Interop/IDynamicLibraryLoader.cs deleted file mode 100644 index 56c50e340dd..00000000000 --- a/src/Avalonia.Base/Platform/Interop/IDynamicLibraryLoader.cs +++ /dev/null @@ -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) - { - - } - } -} diff --git a/src/Avalonia.Base/Platform/StandardRuntimePlatformServices.cs b/src/Avalonia.Base/Platform/StandardRuntimePlatformServices.cs index 800d9b390f0..fe8ddb7a9ef 100644 --- a/src/Avalonia.Base/Platform/StandardRuntimePlatformServices.cs +++ b/src/Avalonia.Base/Platform/StandardRuntimePlatformServices.cs @@ -14,16 +14,7 @@ public static void Register(Assembly? assembly = null) AssetLoader.RegisterResUriParsers(); AvaloniaLocator.CurrentMutable .Bind().ToConstant(standardPlatform) - .Bind().ToConstant(new StandardAssetLoader(assembly)) - .Bind().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 - ); + .Bind().ToConstant(new StandardAssetLoader(assembly)); } } } diff --git a/src/Avalonia.OpenGL/Egl/EglInterface.cs b/src/Avalonia.OpenGL/Egl/EglInterface.cs index 2235d9087c3..4b766d0dda9 100644 --- a/src/Avalonia.OpenGL/Egl/EglInterface.cs +++ b/src/Avalonia.OpenGL/Egl/EglInterface.cs @@ -35,9 +35,8 @@ static Func Load() static Func Load(string library) { - var dyn = AvaloniaLocator.Current.GetRequiredService(); - 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 From cb33529af4eeeb4cbbbb0fab71a0d7aab19bd0d0 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Tue, 27 Jun 2023 17:28:01 -0400 Subject: [PATCH 02/10] Make IRuntimePlatform private API and remove some members that are always have single implementation --- .../Platform/IRuntimePlatform.cs | 15 ++------- .../Platform/Internal/UnmanagedBlob.cs | 2 +- .../Platform/StandardRuntimePlatform.cs | 12 ++----- .../Rendering/DefaultRenderTimer.cs | 8 ++--- src/Avalonia.X11/X11CursorFactory.cs | 6 ++-- src/Avalonia.X11/X11Framebuffer.cs | 5 +-- .../Avalonia.Browser/WindowingPlatform.cs | 32 +++++++------------ src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs | 20 ++++-------- .../Avalonia.Win32/FramebufferManager.cs | 8 ++--- 9 files changed, 37 insertions(+), 71 deletions(-) diff --git a/src/Avalonia.Base/Platform/IRuntimePlatform.cs b/src/Avalonia.Base/Platform/IRuntimePlatform.cs index 64b504c479d..a2b80794769 100644 --- a/src/Avalonia.Base/Platform/IRuntimePlatform.cs +++ b/src/Avalonia.Base/Platform/IRuntimePlatform.cs @@ -3,24 +3,13 @@ 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 : diff --git a/src/Avalonia.Base/Platform/Internal/UnmanagedBlob.cs b/src/Avalonia.Base/Platform/Internal/UnmanagedBlob.cs index a56d9ffd1c6..fc299dbcecd 100644 --- a/src/Avalonia.Base/Platform/Internal/UnmanagedBlob.cs +++ b/src/Avalonia.Base/Platform/Internal/UnmanagedBlob.cs @@ -6,7 +6,7 @@ namespace Avalonia.Platform.Internal; -internal class UnmanagedBlob : IUnmanagedBlob +internal class UnmanagedBlob { private IntPtr _address; private readonly object _lock = new object(); diff --git a/src/Avalonia.Base/Platform/StandardRuntimePlatform.cs b/src/Avalonia.Base/Platform/StandardRuntimePlatform.cs index 8352d794d08..7a5c92c7741 100644 --- a/src/Avalonia.Base/Platform/StandardRuntimePlatform.cs +++ b/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; } } diff --git a/src/Avalonia.Base/Rendering/DefaultRenderTimer.cs b/src/Avalonia.Base/Rendering/DefaultRenderTimer.cs index 74c8bd107a9..102cc30e879 100644 --- a/src/Avalonia.Base/Rendering/DefaultRenderTimer.cs +++ b/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? _tick; private IDisposable? _subscription; @@ -80,11 +80,9 @@ protected void Start() /// protected virtual IDisposable StartCore(Action tick) { - _runtime ??= AvaloniaLocator.Current.GetRequiredService(); + 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); } /// diff --git a/src/Avalonia.X11/X11CursorFactory.cs b/src/Avalonia.X11/X11CursorFactory.cs index 9818d3f86ae..6fc9e27c81a 100644 --- a/src/Avalonia.X11/X11CursorFactory.cs +++ b/src/Avalonia.X11/X11CursorFactory.cs @@ -5,6 +5,7 @@ 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 @@ private static IntPtr GetNullCursor(IntPtr display) 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() + (bitmap.PixelSize.Width * bitmap.PixelSize.Height * 4); - var runtimePlatform = AvaloniaLocator.Current.GetRequiredService(); var platformRenderInterface = AvaloniaLocator.Current.GetRequiredService(); _pixelSize = bitmap.PixelSize; - _blob = runtimePlatform.AllocBlob(size); + _blob = new UnmanagedBlob(size); var image = (XcursorImage*)_blob.Address; image->version = 1; diff --git a/src/Avalonia.X11/X11Framebuffer.cs b/src/Avalonia.X11/X11Framebuffer.cs index 90e4b185710..02cfe4a5174 100644 --- a/src/Avalonia.X11/X11Framebuffer.cs +++ b/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 @@ internal class X11Framebuffer : ILockedFramebuffer 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 @@ public X11Framebuffer(IntPtr display, IntPtr xid, int depth, int width, int heig RowBytes = width * 4; Dpi = new Vector(96, 96) * factor; Format = PixelFormat.Bgra8888; - _blob = AvaloniaLocator.Current.GetRequiredService().AllocBlob(RowBytes * height); + _blob = new UnmanagedBlob(RowBytes * height); Address = _blob.Address; } diff --git a/src/Browser/Avalonia.Browser/WindowingPlatform.cs b/src/Browser/Avalonia.Browser/WindowingPlatform.cs index 3ad26202063..a23cd019102 100644 --- a/src/Browser/Avalonia.Browser/WindowingPlatform.cs +++ b/src/Browser/Avalonia.Browser/WindowingPlatform.cs @@ -57,12 +57,11 @@ public static void Register() 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 @@ public void Signal(DispatcherPriority priority) 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 @@ public bool CurrentThreadIsLoopThread } public event Action? Signaled; - - private static IRuntimePlatform GetRuntimePlatform() - { - return AvaloniaLocator.Current.GetRequiredService(); - } } } diff --git a/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs b/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs index 38df7b2933a..581470fcfe3 100644 --- a/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs +++ b/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs @@ -3,6 +3,7 @@ using System.Threading; using Avalonia.Media.Imaging; using Avalonia.Platform; +using Avalonia.Platform.Internal; using Avalonia.Skia.Helpers; using SkiaSharp; @@ -97,21 +98,12 @@ public WriteableBitmapImpl(PixelSize size, Vector dpi, PixelFormat format, Alpha SKColorType colorType = format.ToSkColorType(); SKAlphaType alphaType = alphaFormat.ToSkAlphaType(); - var runtimePlatform = AvaloniaLocator.Current.GetService(); + _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 @@ public SKImage GetSnapshot() /// Blob. private static void ReleaseProc(IntPtr address, object ctx) { - ((IUnmanagedBlob)ctx).Dispose(); + ((UnmanagedBlob)ctx).Dispose(); } /// diff --git a/src/Windows/Avalonia.Win32/FramebufferManager.cs b/src/Windows/Avalonia.Win32/FramebufferManager.cs index cba4879f5bd..b1b203a92d0 100644 --- a/src/Windows/Avalonia.Win32/FramebufferManager.cs +++ b/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 @@ -105,8 +106,7 @@ private Vector GetCurrentDpi() private static FramebufferData AllocateFramebufferData(int width, int height) { - var service = AvaloniaLocator.Current.GetRequiredService(); - var bitmapBlob = service.AllocBlob(width * height * _bytesPerPixel); + var bitmapBlob = new UnmanagedBlob(width * height * _bytesPerPixel); return new FramebufferData(bitmapBlob, width, height); } @@ -155,7 +155,7 @@ private static bool DrawToWindow(IntPtr hWnd, FramebufferData framebufferData, i private readonly struct FramebufferData { - public IUnmanagedBlob Data { get; } + public UnmanagedBlob Data { get; } public PixelSize Size { get; } @@ -163,7 +163,7 @@ private readonly struct FramebufferData 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); From 605d10b02258b7aef2f181d6d15b9e569ac940c7 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Tue, 27 Jun 2023 18:42:53 -0400 Subject: [PATCH 03/10] Unify IRuntimePlatform registration across backends --- .../Avalonia.Android/AndroidPlatform.cs | 1 + .../Platform/IRuntimePlatform.cs | 1 - .../StandardRuntimePlatformServices.cs | 19 +++---- src/Avalonia.Controls/AppBuilder.cs | 54 +++++++++++-------- .../Remote/RemoteDesignerEntryPoint.cs | 1 + .../AvaloniaNativePlatformExtensions.cs | 4 +- src/Avalonia.X11/X11Platform.cs | 4 +- .../Avalonia.Browser/BrowserAppBuilder.cs | 1 + .../BrowserRuntimePlatform.cs | 18 +++++++ .../AvaloniaHeadlessPlatform.cs | 6 ++- .../LinuxFramebufferPlatform.cs | 5 +- src/Windows/Avalonia.Win32/Win32Platform.cs | 4 +- src/iOS/Avalonia.iOS/Platform.cs | 1 + 13 files changed, 80 insertions(+), 39 deletions(-) diff --git a/src/Android/Avalonia.Android/AndroidPlatform.cs b/src/Android/Avalonia.Android/AndroidPlatform.cs index d5d5f211e9e..28f6d5349b9 100644 --- a/src/Android/Avalonia.Android/AndroidPlatform.cs +++ b/src/Android/Avalonia.Android/AndroidPlatform.cs @@ -18,6 +18,7 @@ public static class AndroidApplicationExtensions public static AppBuilder UseAndroid(this AppBuilder builder) { return builder + .UseStandardRuntimePlatformSubsystem() .UseWindowingSubsystem(() => AndroidPlatform.Initialize(), "Android") .UseSkia(); } diff --git a/src/Avalonia.Base/Platform/IRuntimePlatform.cs b/src/Avalonia.Base/Platform/IRuntimePlatform.cs index a2b80794769..c6efadc99a7 100644 --- a/src/Avalonia.Base/Platform/IRuntimePlatform.cs +++ b/src/Avalonia.Base/Platform/IRuntimePlatform.cs @@ -18,7 +18,6 @@ public record struct RuntimePlatformInfo public bool IsMobile { get; set; } } - [Unstable] public enum FormFactorType { Unknown, diff --git a/src/Avalonia.Base/Platform/StandardRuntimePlatformServices.cs b/src/Avalonia.Base/Platform/StandardRuntimePlatformServices.cs index fe8ddb7a9ef..70919bc4772 100644 --- a/src/Avalonia.Base/Platform/StandardRuntimePlatformServices.cs +++ b/src/Avalonia.Base/Platform/StandardRuntimePlatformServices.cs @@ -3,18 +3,15 @@ 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().ToConstant(standardPlatform) - .Bind().ToConstant(new StandardAssetLoader(assembly)); - } + AssetLoader.RegisterResUriParsers(); + AvaloniaLocator.CurrentMutable + .Bind().ToSingleton() + .Bind().ToConstant(new StandardAssetLoader(assembly)); } } diff --git a/src/Avalonia.Controls/AppBuilder.cs b/src/Avalonia.Controls/AppBuilder.cs index 77cc9d4dcb9..3bc00bbc9c0 100644 --- a/src/Avalonia.Controls/AppBuilder.cs +++ b/src/Avalonia.Controls/AppBuilder.cs @@ -12,23 +12,23 @@ namespace Avalonia /// /// Initializes platform-specific services for an . /// - public class AppBuilder + public sealed class AppBuilder { private static bool s_setupWasAlreadyCalled; private Action? _optionsInitializers; private Func? _appFactory; private IApplicationLifetime? _lifetime; - - /// - /// Gets or sets the instance. - /// - public IRuntimePlatform RuntimePlatform { get; set; } /// /// Gets or sets a method to call the initialize the runtime platform services (e. g. AssetLoader) /// - public Action RuntimePlatformServicesInitializer { get; private set; } + public Action? RuntimePlatformServicesInitializer { get; private set; } + /// + /// Gets the name of the currently selected windowing subsystem. + /// + public string? RuntimePlatformServicesName { get; private set; } + /// /// Gets the instance being initialized. /// @@ -70,21 +70,10 @@ public class AppBuilder /// /// Initializes a new instance of the class. /// - public AppBuilder() - : this(new StandardRuntimePlatform(), - builder => StandardRuntimePlatformServices.Register(builder.ApplicationType?.Assembly)) + private AppBuilder() { } - /// - /// Initializes a new instance of the class. - /// - protected AppBuilder(IRuntimePlatform platform, Action platformServices) - { - RuntimePlatform = platform; - RuntimePlatformServicesInitializer = () => platformServices(this); - } - /// /// Begin configuring an . /// @@ -96,7 +85,6 @@ public static AppBuilder Configure() return new AppBuilder() { ApplicationType = typeof(TApp), - // Needed for CoreRT compatibility _appFactory = () => new TApp() }; } @@ -155,7 +143,7 @@ internal static AppBuilder Configure( $"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 callback) { @@ -225,6 +213,30 @@ public AppBuilder UseRenderingSubsystem(Action initializer, string name = "") RenderingSubsystemName = name; return Self; } + + /// + /// Specifies a runtime platform subsystem to use. + /// + /// The method to call to initialize the runtime platform subsystem. + /// The name of the runtime platform subsystem. + /// An instance. + public AppBuilder UseRuntimePlatformSubsystem(Action initializer, string name = "") + { + RuntimePlatformServicesInitializer = initializer; + RuntimePlatformServicesName = name; + return Self; + } + + /// + /// Specifies a standard runtime platform subsystem to use. + /// + /// An instance. + public AppBuilder UseStandardRuntimePlatformSubsystem() + { + RuntimePlatformServicesInitializer = () => StandardRuntimePlatformServices.Register(ApplicationType?.Assembly); + RuntimePlatformServicesName = nameof(StandardRuntimePlatform); + return Self; + } /// /// Configures platform-specific options diff --git a/src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs b/src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs index 6a6bc8c7463..06d63a65ba0 100644 --- a/src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs +++ b/src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs @@ -138,6 +138,7 @@ public IAvaloniaRemoteTransportConnection ConfigureApp(IAvaloniaRemoteTransportC 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) diff --git a/src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs b/src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs index 2b989ce7338..52f66d3b0c9 100644 --- a/src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs +++ b/src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs @@ -8,7 +8,9 @@ public static class AvaloniaNativePlatformExtensions { public static AppBuilder UseAvaloniaNative(this AppBuilder builder) { - builder.UseWindowingSubsystem(() => + builder + .UseStandardRuntimePlatformSubsystem() + .UseWindowingSubsystem(() => { var platform = AvaloniaNativePlatform.Initialize( AvaloniaLocator.Current.GetService() ?? diff --git a/src/Avalonia.X11/X11Platform.cs b/src/Avalonia.X11/X11Platform.cs index a880e4ba1a6..3a39a2a6b6a 100644 --- a/src/Avalonia.X11/X11Platform.cs +++ b/src/Avalonia.X11/X11Platform.cs @@ -289,7 +289,9 @@ public static class AvaloniaX11PlatformExtensions { public static AppBuilder UseX11(this AppBuilder builder) { - builder.UseWindowingSubsystem(() => + builder + .UseStandardRuntimePlatformSubsystem() + .UseWindowingSubsystem(() => new AvaloniaX11Platform().Initialize(AvaloniaLocator.Current.GetService() ?? new X11PlatformOptions())); return builder; diff --git a/src/Browser/Avalonia.Browser/BrowserAppBuilder.cs b/src/Browser/Avalonia.Browser/BrowserAppBuilder.cs index 38784871b17..dbc5f9f4a9b 100644 --- a/src/Browser/Avalonia.Browser/BrowserAppBuilder.cs +++ b/src/Browser/Avalonia.Browser/BrowserAppBuilder.cs @@ -98,6 +98,7 @@ public static AppBuilder UseBrowser( this AppBuilder builder) { return builder + .UseBrowserRuntimePlatformSubsystem() .UseWindowingSubsystem(BrowserWindowingPlatform.Register) .UseSkia(); } diff --git a/src/Browser/Avalonia.Browser/BrowserRuntimePlatform.cs b/src/Browser/Avalonia.Browser/BrowserRuntimePlatform.cs index 67bb0404102..eee2b70c9c4 100644 --- a/src/Browser/Avalonia.Browser/BrowserRuntimePlatform.cs +++ b/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 @@ 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().ToSingleton() + .Bind().ToConstant(new StandardAssetLoader(assembly)); + } +} + internal class BrowserRuntimePlatform : StandardRuntimePlatform { private static readonly Lazy Info = new(() => diff --git a/src/Headless/Avalonia.Headless/AvaloniaHeadlessPlatform.cs b/src/Headless/Avalonia.Headless/AvaloniaHeadlessPlatform.cs index 2d8506f92b6..9bb587c9b1a 100644 --- a/src/Headless/Avalonia.Headless/AvaloniaHeadlessPlatform.cs +++ b/src/Headless/Avalonia.Headless/AvaloniaHeadlessPlatform.cs @@ -105,8 +105,10 @@ public static class AvaloniaHeadlessPlatformExtensions 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"); } } } diff --git a/src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs b/src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs index 10ec4372c7d..e4a48c78fe5 100644 --- a/src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs +++ b/src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs @@ -76,7 +76,10 @@ void Initialize() 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); } } diff --git a/src/Windows/Avalonia.Win32/Win32Platform.cs b/src/Windows/Avalonia.Win32/Win32Platform.cs index e9019803be3..8f74fb49e85 100644 --- a/src/Windows/Avalonia.Win32/Win32Platform.cs +++ b/src/Windows/Avalonia.Win32/Win32Platform.cs @@ -25,7 +25,9 @@ public static class Win32ApplicationExtensions { public static AppBuilder UseWin32(this AppBuilder builder) { - return builder.UseWindowingSubsystem( + return builder + .UseStandardRuntimePlatformSubsystem() + .UseWindowingSubsystem( () => Win32.Win32Platform.Initialize( AvaloniaLocator.Current.GetService() ?? new Win32PlatformOptions()), "Win32"); diff --git a/src/iOS/Avalonia.iOS/Platform.cs b/src/iOS/Avalonia.iOS/Platform.cs index de664c93e0e..34018560e21 100644 --- a/src/iOS/Avalonia.iOS/Platform.cs +++ b/src/iOS/Avalonia.iOS/Platform.cs @@ -16,6 +16,7 @@ public static class IOSApplicationExtensions public static AppBuilder UseiOS(this AppBuilder builder) { return builder + .UseStandardRuntimePlatformSubsystem() .UseWindowingSubsystem(iOS.Platform.Register, "iOS") .UseSkia(); } From 8b0085d99ceaa11a9e7888712ba3919fadecb5ba Mon Sep 17 00:00:00 2001 From: Max Katz Date: Tue, 27 Jun 2023 19:04:10 -0400 Subject: [PATCH 04/10] Test NativeLibraryEx with non net6 --- .../Compatibility/NativeLibrary.cs | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/Avalonia.Base/Compatibility/NativeLibrary.cs b/src/Avalonia.Base/Compatibility/NativeLibrary.cs index 8c77ee928ae..7627c095bca 100644 --- a/src/Avalonia.Base/Compatibility/NativeLibrary.cs +++ b/src/Avalonia.Base/Compatibility/NativeLibrary.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel; +using System.Reflection; using System.Runtime.InteropServices; using Avalonia.Compatibility; using Avalonia.Platform.Interop; @@ -9,23 +10,25 @@ namespace Avalonia.Compatibility internal class NativeLibraryEx { #if NET6_0_OR_GREATER - public static IntPtr Load(string dll) => System.Runtime.InteropServices.NativeLibrary.Load(dll); - public static bool TryGetExport(IntPtr handle, string name, out IntPtr address) - => System.Runtime.InteropServices.NativeLibrary.TryGetExport(handle, name, out address); + 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); + var handle = DlOpen!(dll); if (handle != IntPtr.Zero) return handle; - throw new InvalidOperationException("Unable to load " + dll, DlErrorString()); + throw new InvalidOperationException("Unable to load " + dll, DlError!()); } public static bool TryGetExport(IntPtr handle, string name, out IntPtr address) { try { - address = DlSym(handle, name); + address = DlSym!(handle, name); return address != default; } catch (Exception) @@ -56,7 +59,7 @@ static NativeLibraryEx() private static Func? DlOpen; private static Func? DlSym; - private static Func? DlErrorString; + private static Func? DlError; [DllImport("libc")] static extern int uname(IntPtr buf); @@ -73,7 +76,7 @@ public static void Init() { DlOpen = LoadLibrary; DlSym = GetProcAddress; - DlErrorString = () => new Win32Exception(Marshal.GetLastWin32Error()); + DlError = () => new Win32Exception(Marshal.GetLastWin32Error()); } } @@ -92,7 +95,7 @@ public static void Init() { DlOpen = s => dlopen(s, 1); DlSym = dlsym; - DlErrorString = () => new InvalidOperationException(Marshal.PtrToStringAnsi(dlerror())); + DlError = () => new InvalidOperationException(Marshal.PtrToStringAnsi(dlerror())); } } @@ -111,7 +114,7 @@ public static void Init() { DlOpen = s => dlopen(s, 1); DlSym = dlsym; - DlErrorString = () => new InvalidOperationException(Marshal.PtrToStringAnsi(dlerror())); + DlError = () => new InvalidOperationException(Marshal.PtrToStringAnsi(dlerror())); } } #endif From 4c0873eb9a90e3fb12f8509cfa77b4a4f8ad07e6 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Wed, 28 Jun 2023 15:05:41 -0400 Subject: [PATCH 05/10] Fix build --- tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs b/tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs index ebd8a6474ae..c0ec3f0687b 100644 --- a/tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs +++ b/tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs @@ -20,7 +20,7 @@ private static IDisposable CreateApp() var services = new TestServices( assetLoader: new StandardAssetLoader(), globalClock: new MockGlobalClock(), - platform: new AppBuilder().RuntimePlatform, + platform: new StandardRuntimePlatform(), standardCursorFactory: Mock.Of(), theme: () => CreateTheme(), windowingPlatform: new MockWindowingPlatform()); From b580d3f986ea81907246d9b45665713d160f8439 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 30 Jun 2023 17:21:06 +0100 Subject: [PATCH 06/10] fix native api loading. --- .../Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs b/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs index 9475d96fc02..bfd486c60a2 100644 --- a/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs +++ b/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs @@ -2,6 +2,7 @@ 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,23 @@ public SkiaMetalApi() { // Make sure that skia is loaded GC.KeepAlive(new SKPaint()); - - var loader = AvaloniaLocator.Current.GetRequiredService(); -#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] ) - loader.GetProcAddress(dll, "gr_direct_context_make_metal_with_options", false); - _gr_backendrendertarget_new_metal = - (delegate* unmanaged[Stdcall]) - loader.GetProcAddress(dll, "gr_backendrendertarget_new_metal", false); - + + var dll = NativeLibraryEx.Load("libSkiaSharp"); + + 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] )address; + } + + if(NativeLibraryEx.TryGetExport(dll, "gr_backendrendertarget_new_metal", out address)) + { + _gr_backendrendertarget_new_metal = + (delegate* unmanaged[Stdcall])address; + } + _contextCtor = typeof(GRContext).GetConstructor( BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new[] { typeof(IntPtr), typeof(bool) }, null) ?? throw new MissingMemberException("GRContext.ctor(IntPtr,bool)"); From ae2aee402d30712ba10c115585ac35f34f897ec4 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 30 Jun 2023 21:42:54 +0100 Subject: [PATCH 07/10] Revert "fix native api loading." This reverts commit b580d3f986ea81907246d9b45665713d160f8439. --- .../Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs | 31 ++++++++----------- 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs b/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs index bfd486c60a2..9475d96fc02 100644 --- a/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs +++ b/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs @@ -2,7 +2,6 @@ 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; @@ -25,23 +24,19 @@ public SkiaMetalApi() { // Make sure that skia is loaded GC.KeepAlive(new SKPaint()); - - var dll = NativeLibraryEx.Load("libSkiaSharp"); - - 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] )address; - } - - if(NativeLibraryEx.TryGetExport(dll, "gr_backendrendertarget_new_metal", out address)) - { - _gr_backendrendertarget_new_metal = - (delegate* unmanaged[Stdcall])address; - } - + + var loader = AvaloniaLocator.Current.GetRequiredService(); +#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] ) + loader.GetProcAddress(dll, "gr_direct_context_make_metal_with_options", false); + _gr_backendrendertarget_new_metal = + (delegate* unmanaged[Stdcall]) + loader.GetProcAddress(dll, "gr_backendrendertarget_new_metal", false); + _contextCtor = typeof(GRContext).GetConstructor( BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new[] { typeof(IntPtr), typeof(bool) }, null) ?? throw new MissingMemberException("GRContext.ctor(IntPtr,bool)"); From ce4a8137ee60ec38ccc0aa220da4eec8d47dcf4c Mon Sep 17 00:00:00 2001 From: Max Katz Date: Fri, 30 Jun 2023 16:51:15 -0400 Subject: [PATCH 08/10] Revert "Revert "fix native api loading."" This reverts commit ae2aee402d30712ba10c115585ac35f34f897ec4. --- .../Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs b/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs index 9475d96fc02..bfd486c60a2 100644 --- a/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs +++ b/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs @@ -2,6 +2,7 @@ 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,23 @@ public SkiaMetalApi() { // Make sure that skia is loaded GC.KeepAlive(new SKPaint()); - - var loader = AvaloniaLocator.Current.GetRequiredService(); -#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] ) - loader.GetProcAddress(dll, "gr_direct_context_make_metal_with_options", false); - _gr_backendrendertarget_new_metal = - (delegate* unmanaged[Stdcall]) - loader.GetProcAddress(dll, "gr_backendrendertarget_new_metal", false); - + + var dll = NativeLibraryEx.Load("libSkiaSharp"); + + 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] )address; + } + + if(NativeLibraryEx.TryGetExport(dll, "gr_backendrendertarget_new_metal", out address)) + { + _gr_backendrendertarget_new_metal = + (delegate* unmanaged[Stdcall])address; + } + _contextCtor = typeof(GRContext).GetConstructor( BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new[] { typeof(IntPtr), typeof(bool) }, null) ?? throw new MissingMemberException("GRContext.ctor(IntPtr,bool)"); From 1489fcc62d743e4516e8b3ebac6aec6161e8cca3 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Fri, 30 Jun 2023 16:59:39 -0400 Subject: [PATCH 09/10] Pass typeof(SKPaint).Assembly as well --- src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs b/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs index bfd486c60a2..e133e3dcde8 100644 --- a/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs +++ b/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs @@ -26,7 +26,7 @@ public SkiaMetalApi() // Make sure that skia is loaded GC.KeepAlive(new SKPaint()); - var dll = NativeLibraryEx.Load("libSkiaSharp"); + var dll = NativeLibraryEx.Load("libSkiaSharp", typeof(SKPaint).Assembly); IntPtr address; From 6499bbd4dc11ef4046b861de3418b7a66ae5b87c Mon Sep 17 00:00:00 2001 From: Max Katz Date: Fri, 30 Jun 2023 17:06:07 -0400 Subject: [PATCH 10/10] Throw if methods were not found --- src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs b/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs index e133e3dcde8..3022f92ec5b 100644 --- a/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs +++ b/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs @@ -35,12 +35,22 @@ public SkiaMetalApi() _gr_direct_context_make_metal_with_options = (delegate* unmanaged[Stdcall] )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])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,