浏览代码

Begin with DisableRuntimeMarshalling support + fix macOS Metal AOT (#17116)

* Add attribute

* Replace `SetLastError = true` with `Marshal.GetLastSystemError()`

* Make Avalonia.Base and Avalonia.Skia compatible with DisableRuntimeMarshalling

* First step in Android DisableRuntimeMarshalling support

* Make Avalonia.Browser compatible

* Set EnableRuntimeMarshalling=true on all projects we are not yet ready to support without runtime marshalling
Max Katz 1 年之前
父节点
当前提交
d7040e22d5

+ 6 - 0
build/TrimmingEnable.props

@@ -19,4 +19,10 @@
     <WarningsAsErrors Condition="'$(IsAotCompatible)' == 'true'">$(WarningsAsErrors);IL3050;IL3051;IL3052;IL3053;IL3054;IL3055;IL3056</WarningsAsErrors>
   </PropertyGroup>
 
+  <PropertyGroup Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net8.0')) AND '$(EnableRuntimeMarshalling)' != 'true'">
+    <WarningsAsErrors>$(WarningsAsErrors);CA1420;CA1421</WarningsAsErrors>
+  </PropertyGroup>
+  <ItemGroup Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net8.0')) AND '$(EnableRuntimeMarshalling)' != 'true'">
+    <AssemblyAttribute Include="System.Runtime.CompilerServices.DisableRuntimeMarshallingAttribute" />
+  </ItemGroup>
 </Project>

+ 1 - 0
src/Android/Avalonia.Android/Avalonia.Android.csproj

@@ -4,6 +4,7 @@
     <SupportedOSPlatformVersion>$(AvsMinSupportedAndroidVersion)</SupportedOSPlatformVersion>
     <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
     <AndroidResgenNamespace>Avalonia.Android.Internal</AndroidResgenNamespace>
+    <EnableRuntimeMarshalling>true</EnableRuntimeMarshalling>
   </PropertyGroup>
   <ItemGroup>
     <ProjectReference Include="..\..\..\packages\Avalonia\Avalonia.csproj" />

+ 3 - 3
src/Android/Avalonia.Android/Platform/SkiaPlatform/AndroidFramebuffer.cs

@@ -6,7 +6,7 @@ using Avalonia.Platform;
 
 namespace Avalonia.Android.Platform.SkiaPlatform
 {
-    class AndroidFramebuffer : ILockedFramebuffer
+    unsafe class AndroidFramebuffer : ILockedFramebuffer
     {
         private IntPtr _window;
 
@@ -24,7 +24,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
                 bottom = ANativeWindow_getHeight(_window)
             };
             Size = new PixelSize(rc.right, rc.bottom);
-            ANativeWindow_lock(_window, out buffer, ref rc);
+            ANativeWindow_lock(_window, &buffer, &rc);
 
             Format = buffer.format == AndroidPixelFormat.WINDOW_FORMAT_RGB_565
                 ? PixelFormat.Rgb565 : PixelFormat.Rgba8888;
@@ -61,7 +61,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
         internal static extern void ANativeWindow_unlockAndPost(IntPtr window);
 
         [DllImport("android")]
-        internal static extern int ANativeWindow_lock(IntPtr window, out ANativeWindow_Buffer outBuffer, ref ARect inOutDirtyBounds);
+        internal static extern int ANativeWindow_lock(IntPtr window, ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds);
         public enum AndroidPixelFormat
         {
             WINDOW_FORMAT_RGBA_8888 = 1,

+ 3 - 3
src/Android/Avalonia.Android/Platform/Vulkan/VulkanSupport.cs

@@ -6,10 +6,10 @@ using Avalonia.Vulkan;
 
 namespace Avalonia.Android.Platform.Vulkan
 {
-    internal class VulkanSupport
+    internal partial class VulkanSupport
     {
-        [DllImport("libvulkan.so")]
-        private static extern IntPtr vkGetInstanceProcAddr(IntPtr instance, string name);
+        [LibraryImport("libvulkan.so", StringMarshalling = StringMarshalling.Utf8)]
+        private static partial IntPtr vkGetInstanceProcAddr(IntPtr instance, string name);
 
         public static VulkanPlatformGraphics? TryInitialize(VulkanOptions options) =>
             VulkanPlatformGraphics.TryCreate(options ?? new(), new VulkanPlatformSpecificOptions

+ 0 - 1
src/Avalonia.Base/Avalonia.Base.csproj

@@ -5,7 +5,6 @@
     <AllowUnsafeBlocks>True</AllowUnsafeBlocks>
     <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
     <CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GeneratedFiles</CompilerGeneratedFilesOutputPath>
-    <IsTrimmable>true</IsTrimmable>
   </PropertyGroup>
   <ItemGroup>
     <EmbeddedResource Include="Assets\*.trie" />

+ 1 - 1
src/Avalonia.Base/Media/Imaging/PixelFormatTranscoder.cs

@@ -18,7 +18,7 @@ internal static unsafe class PixelFormatTranscoder
         AlphaFormat destAlphaFormat)
     {
         var pixelCount = srcSize.Width * srcSize.Height;
-        var bufferSize = pixelCount * Marshal.SizeOf<Rgba8888Pixel>();
+        var bufferSize = pixelCount * sizeof(Rgba8888Pixel);
         using var blob = new UnmanagedBlob(bufferSize);
       
         var pixels = new Span<Rgba8888Pixel>((void*)blob.Address, pixelCount);

+ 15 - 12
src/Avalonia.Base/Platform/Internal/UnmanagedBlob.cs

@@ -3,6 +3,7 @@ using System.Collections.Generic;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using System.Threading;
+using Avalonia.Compatibility;
 
 namespace Avalonia.Platform.Internal;
 
@@ -108,21 +109,15 @@ internal class UnmanagedBlob : IDisposable
     public int Size { get; private set; }
     public bool IsDisposed { get; private set; }
 
-    [DllImport("libc", SetLastError = true)]
+    [DllImport("libc")]
     private static extern IntPtr mmap(IntPtr addr, IntPtr length, int prot, int flags, int fd, IntPtr offset);
-    [DllImport("libc", SetLastError = true)]
+    [DllImport("libc")]
     private static extern int munmap(IntPtr addr, IntPtr length);
-    [DllImport("libc", SetLastError = true)]
-    private static extern long sysconf(int name);
-
-    private bool? _useMmap;
-    private bool UseMmap
-        => _useMmap ?? ((_useMmap = RuntimeInformation.IsOSPlatform(OSPlatform.Linux)).Value);
 
     // Could be replaced with https://github.com/dotnet/runtime/issues/40892 when it will be available.
     private IntPtr Alloc(int size)
     {
-        if (!UseMmap)
+        if (!OperatingSystemEx.IsLinux())
         {
             return Marshal.AllocHGlobal(size);
         }
@@ -131,8 +126,12 @@ internal class UnmanagedBlob : IDisposable
             var rv = mmap(IntPtr.Zero, new IntPtr(size), 3, 0x22, -1, IntPtr.Zero);
             if (rv.ToInt64() == -1 || (ulong)rv.ToInt64() == 0xffffffff)
             {
-                var errno = Marshal.GetLastWin32Error();
+#if NET6_0_OR_GREATER
+                var errno = Marshal.GetLastSystemError();
                 throw new Exception("Unable to allocate memory: " + errno);
+#else
+                throw new Exception("Unable to allocate memory");
+#endif
             }
             return rv;
         }
@@ -140,7 +139,7 @@ internal class UnmanagedBlob : IDisposable
 
     private void Free(IntPtr ptr, int len)
     {
-        if (!UseMmap)
+        if (!OperatingSystemEx.IsLinux())
         {
             Marshal.FreeHGlobal(ptr);
         }
@@ -148,8 +147,12 @@ internal class UnmanagedBlob : IDisposable
         {
             if (munmap(ptr, new IntPtr(len)) == -1)
             {
-                var errno = Marshal.GetLastWin32Error();
+#if NET6_0_OR_GREATER
+                var errno = Marshal.GetLastSystemError();
                 throw new Exception("Unable to free memory: " + errno);
+#else
+                throw new Exception("Unable to free memory");
+#endif
             }
         }
     }

+ 1 - 0
src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj

@@ -3,6 +3,7 @@
   <PropertyGroup>
     <TargetFrameworks>$(AvsCurrentTargetFramework);$(AvsLegacyTargetFrameworks);netstandard2.0</TargetFrameworks>
     <Nullable>enable</Nullable>
+    <EnableRuntimeMarshalling>true</EnableRuntimeMarshalling>
   </PropertyGroup>
 
   <Import Project="../../build/TrimmingEnable.props" />

+ 1 - 0
src/Avalonia.OpenGL/Avalonia.OpenGL.csproj

@@ -3,6 +3,7 @@
   <PropertyGroup>
     <TargetFrameworks>$(AvsCurrentTargetFramework);$(AvsLegacyTargetFrameworks);netstandard2.0</TargetFrameworks>
     <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+    <EnableRuntimeMarshalling>true</EnableRuntimeMarshalling>
   </PropertyGroup>
 
   <ItemGroup>

+ 1 - 0
src/Avalonia.Vulkan/Avalonia.Vulkan.csproj

@@ -3,6 +3,7 @@
     <PropertyGroup>
         <TargetFrameworks>$(AvsCurrentTargetFramework);$(AvsLegacyTargetFrameworks);netstandard2.0</TargetFrameworks>
         <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+        <EnableRuntimeMarshalling>true</EnableRuntimeMarshalling>
     </PropertyGroup>
 
     <ItemGroup>

+ 1 - 0
src/Avalonia.X11/Avalonia.X11.csproj

@@ -2,6 +2,7 @@
   <PropertyGroup>
     <TargetFrameworks>$(AvsCurrentTargetFramework);$(AvsLegacyTargetFrameworks);netstandard2.0</TargetFrameworks>
     <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+    <EnableRuntimeMarshalling>true</EnableRuntimeMarshalling>
   </PropertyGroup>
 
   <ItemGroup>

+ 3 - 3
src/Browser/Avalonia.Browser/Rendering/BrowserWebGlRenderTarget.cs

@@ -96,8 +96,8 @@ partial class WebGlContext : IGlContext, Avalonia.Skia.IGlSkiaSpecificOptionsFea
     [JSImport("WebGlRenderTarget.makeContextCurrent", AvaloniaModule.MainModuleName)]
     private static partial bool MakeContextCurrent(int context);
 
-    [DllImport("libSkiaSharp", EntryPoint = "eglGetProcAddress")]
-    private static extern IntPtr eglGetProcAddress(string name);
+    [LibraryImport("libSkiaSharp", EntryPoint = "eglGetProcAddress", StringMarshalling = StringMarshalling.Utf8)]
+    private static partial IntPtr eglGetProcAddress(string name);
 
     private int _contextId;
     private readonly Thread _thread;
@@ -177,4 +177,4 @@ partial class WebGlContext : IGlContext, Avalonia.Skia.IGlSkiaSpecificOptionsFea
         throw new NotSupportedException();
 
     public bool UseNativeSkiaGrGlInterface => true;
-}
+}

+ 1 - 0
src/Linux/Avalonia.LinuxFramebuffer/Avalonia.LinuxFramebuffer.csproj

@@ -2,6 +2,7 @@
   <PropertyGroup>
     <TargetFrameworks>$(AvsCurrentTargetFramework);$(AvsLegacyTargetFrameworks);netstandard2.0</TargetFrameworks>
     <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+    <EnableRuntimeMarshalling>true</EnableRuntimeMarshalling>
   </PropertyGroup>
   <ItemGroup>
     <ProjectReference Include="..\..\..\packages\Avalonia\Avalonia.csproj" />

+ 13 - 7
src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs

@@ -1,6 +1,7 @@
 using System;
 using System.Diagnostics.CodeAnalysis;
 using System.Reflection;
+using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using Avalonia.Compatibility;
 using Avalonia.Platform.Interop;
@@ -77,13 +78,18 @@ internal unsafe class SkiaMetalApi
     {
         options ??= new();
         var nativeOptions = _contextOptionsToNative.Invoke(options, null)!;
-        var pOptions = Marshal.AllocHGlobal(Marshal.SizeOf(nativeOptions));
-        Marshal.StructureToPtr(nativeOptions, pOptions, false);
-        var context = _gr_direct_context_make_metal_with_options(device, queue, pOptions);
-        Marshal.FreeHGlobal(pOptions);
-        if (context == IntPtr.Zero)
-            throw new InvalidOperationException("Unable to create GRContext from Metal device.");
-        return (GRContext)_contextCtor.Invoke(new object[] { context, true });
+        var gcHandle = GCHandle.Alloc(nativeOptions, GCHandleType.Pinned);
+        try
+        {
+            var context = _gr_direct_context_make_metal_with_options(device, queue, gcHandle.AddrOfPinnedObject());
+            if (context == IntPtr.Zero)
+                throw new InvalidOperationException("Unable to create GRContext from Metal device.");
+            return (GRContext)_contextCtor.Invoke(new object[] { context, true });
+        }
+        finally
+        {
+            gcHandle.Free();   
+        }
     }
 
     internal struct GRMtlTextureInfoNative

+ 1 - 0
src/Windows/Avalonia.Win32/Avalonia.Win32.csproj

@@ -2,6 +2,7 @@
   <PropertyGroup>
     <TargetFrameworks>$(AvsCurrentTargetFramework);$(AvsLegacyTargetFrameworks);netstandard2.0</TargetFrameworks>
     <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+    <EnableRuntimeMarshalling>true</EnableRuntimeMarshalling>
     <!-- We still keep BinaryFormatter for WinForms compatibility. -->
     <EnableUnsafeBinaryFormatterSerialization>true</EnableUnsafeBinaryFormatterSerialization>
   </PropertyGroup>