From ece070705369bd3bc8329813aef57d0d1d2edaf9 Mon Sep 17 00:00:00 2001 From: UltraNamahage <60680748+UltraNamahage@users.noreply.github.com> Date: Sun, 14 Mar 2021 04:10:01 +0900 Subject: [PATCH] Organize Seed and solve the problem of more Seed and code strips with the Preserve attribute. --- src/ImageSharp/Advanced/AotCompilerTools.cs | 320 +++++++++--------- src/ImageSharp/Advanced/PreserveAttribute.cs | 14 + .../PaletteDitherProcessor{TPixel}.cs | 3 +- 3 files changed, 168 insertions(+), 169 deletions(-) create mode 100644 src/ImageSharp/Advanced/PreserveAttribute.cs diff --git a/src/ImageSharp/Advanced/AotCompilerTools.cs b/src/ImageSharp/Advanced/AotCompilerTools.cs index 666b459e4..ea4cd1c8c 100644 --- a/src/ImageSharp/Advanced/AotCompilerTools.cs +++ b/src/ImageSharp/Advanced/AotCompilerTools.cs @@ -3,7 +3,6 @@ using System; using System.Diagnostics.CodeAnalysis; -using System.IO; using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Formats; @@ -13,6 +12,7 @@ using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Jpeg.Components; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Tga; +using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors; @@ -40,70 +40,82 @@ namespace SixLabors.ImageSharp.Advanced [ExcludeFromCodeCoverage] internal static class AotCompilerTools { - static AotCompilerTools() - { - Unsafe.SizeOf(); - Unsafe.SizeOf(); - Unsafe.SizeOf(); - Unsafe.SizeOf(); - Unsafe.SizeOf(); - Unsafe.SizeOf(); - Unsafe.SizeOf(); - Unsafe.SizeOf(); - } - /// /// This is the method that seeds the AoT compiler. /// None of these seed methods needs to actually be called to seed the compiler. /// The calls just need to be present when the code is compiled, and each implementation will be built. /// + /// + /// This method doesn't actually do anything but serves an important purpose... + /// If you are running ImageSharp on iOS and try to call SaveAsGif, it will throw an exception: + /// "Attempting to JIT compile method... OctreeFrameQuantizer.ConstructPalette... while running in aot-only mode." + /// The reason this happens is the SaveAsGif method makes heavy use of generics, which are too confusing for the AoT + /// compiler used on Xamarin.iOS. It spins up the JIT compiler to try and figure it out, but that is an illegal op on + /// iOS so it bombs out. + /// If you are getting the above error, you need to call this method, which will pre-seed the AoT compiler with the + /// necessary methods to complete the SaveAsGif call. That's it, otherwise you should NEVER need this method!!! + /// + [Preserve] private static void SeedEverything() { - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); + try + { + Unsafe.SizeOf(); + Unsafe.SizeOf(); + Unsafe.SizeOf(); + Unsafe.SizeOf(); + Unsafe.SizeOf(); + Unsafe.SizeOf(); + Unsafe.SizeOf(); + Unsafe.SizeOf(); + Unsafe.SizeOf(); + + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + } + catch + { + // nop + } + + throw new InvalidOperationException("This method is used for AOT code generation only. Do not call it at runtime."); } /// /// Seeds the compiler using the given pixel format. /// /// The pixel format. + [Preserve] private static void Seed() where TPixel : unmanaged, IPixel { // This is we actually call all the individual methods you need to seed. - AotPixelInterface(); - AotCompileOctreeQuantizer(); - AotCompileWuQuantizer(); - AotCompilePaletteQuantizer(); - AotCompileDithering(); - AotCompilePixelOperations(); - AotCompileImage(); AotCompileImageProcessingContextFactory(); AotCompileImageEncoderInternals(); @@ -115,136 +127,19 @@ namespace SixLabors.ImageSharp.Advanced AotCompileResamplers(); AotCompileQuantizers(); AotCompilePixelSamplingStrategys(); + AotCompileDithers(); + AotCompileMemoryManagers(); Unsafe.SizeOf(); - AotCodecs(); - // TODO: Do the discovery work to figure out what works and what doesn't. } - private static void AotPixelInterface() - where TPixel : unmanaged, IPixel - { - TPixel pixel = default; - Rgba32 rgba32 = default; - pixel.ToRgba32(ref rgba32); - pixel.FromRgba32(rgba32); - pixel.FromScaledVector4(pixel.ToScaledVector4()); - pixel.FromVector4(pixel.ToVector4()); - - pixel.FromArgb32(default); - pixel.FromBgr24(default); - pixel.FromBgra32(default); - pixel.FromBgra5551(default); - pixel.FromL16(default); - pixel.FromL8(default); - pixel.FromLa16(default); - pixel.FromLa32(default); - pixel.FromRgb24(default); - pixel.FromRgb48(default); - pixel.FromRgba64(default); - pixel.FromRgb24(default); - } - - /// - /// This method doesn't actually do anything but serves an important purpose... - /// If you are running ImageSharp on iOS and try to call SaveAsGif, it will throw an exception: - /// "Attempting to JIT compile method... OctreeFrameQuantizer.ConstructPalette... while running in aot-only mode." - /// The reason this happens is the SaveAsGif method makes heavy use of generics, which are too confusing for the AoT - /// compiler used on Xamarin.iOS. It spins up the JIT compiler to try and figure it out, but that is an illegal op on - /// iOS so it bombs out. - /// If you are getting the above error, you need to call this method, which will pre-seed the AoT compiler with the - /// necessary methods to complete the SaveAsGif call. That's it, otherwise you should NEVER need this method!!! - /// - /// The pixel format. - private static void AotCompileOctreeQuantizer() - where TPixel : unmanaged, IPixel - { - using var test = new OctreeQuantizer(Configuration.Default, new OctreeQuantizer().Options); - var frame = new ImageFrame(Configuration.Default, 1, 1); - test.QuantizeFrame(frame, frame.Bounds()); - } - - /// - /// This method pre-seeds the WuQuantizer in the AoT compiler for iOS. - /// - /// The pixel format. - private static void AotCompileWuQuantizer() - where TPixel : unmanaged, IPixel - { - using var test = new WuQuantizer(Configuration.Default, new WuQuantizer().Options); - var frame = new ImageFrame(Configuration.Default, 1, 1); - test.QuantizeFrame(frame, frame.Bounds()); - } - - /// - /// This method pre-seeds the PaletteQuantizer in the AoT compiler for iOS. - /// - /// The pixel format. - private static void AotCompilePaletteQuantizer() - where TPixel : unmanaged, IPixel - { - using var test = (PaletteQuantizer)new PaletteQuantizer(Array.Empty()).CreatePixelSpecificQuantizer(Configuration.Default); - var frame = new ImageFrame(Configuration.Default, 1, 1); - test.QuantizeFrame(frame, frame.Bounds()); - } - - /// - /// This method pre-seeds the default dithering engine (FloydSteinbergDiffuser) in the AoT compiler for iOS. - /// - /// The pixel format. - private static void AotCompileDithering() - where TPixel : unmanaged, IPixel - { - ErrorDither errorDither = ErrorDither.FloydSteinberg; - OrderedDither orderedDither = OrderedDither.Bayer2x2; - TPixel pixel = default; - using (var image = new ImageFrame(Configuration.Default, 1, 1)) - { - errorDither.Dither(image, image.Bounds(), pixel, pixel, 0, 0, 0); - orderedDither.Dither(pixel, 0, 0, 0, 0); - } - } - - /// - /// This method pre-seeds the decoder and encoder for a given pixel format in the AoT compiler for iOS. - /// - /// The pixel format. - private static void AotCodecs() - where TPixel : unmanaged, IPixel - { - Configuration configuration = Configuration.Default; - ImageFormatManager formatsManager = configuration.ImageFormatsManager; - foreach (IImageFormat imageFormat in configuration.ImageFormats) - { - using var ms = new MemoryStream(); - using (var encoded = new Image(1, 1)) - { - encoded.Save(ms, formatsManager.FindEncoder(imageFormat)); - ms.Position = 0; - } - - using var decoded = Image.Load(ms); - Span span = decoded.GetPixelRowSpan(0); - } - } - - /// - /// This method pre-seeds the PixelOperations engine for the AoT compiler on iOS. - /// - /// The pixel format. - private static void AotCompilePixelOperations() - where TPixel : unmanaged, IPixel - { - var pixelOp = new PixelOperations(); - pixelOp.GetPixelBlender(PixelColorBlendingMode.Normal, PixelAlphaCompositionMode.Clear); - } - /// /// This method pre-seeds the for a given pixel format in the AoT compiler. /// /// The pixel format. + [Preserve] private static unsafe void AotCompileImage() where TPixel : unmanaged, IPixel { @@ -282,6 +177,11 @@ namespace SixLabors.ImageSharp.Advanced ImageFrame.LoadPixelData(default, default(ReadOnlySpan), default, default); } + /// + /// This method pre-seeds the all in the AoT compiler. + /// + /// The pixel format. + [Preserve] private static void AotCompileImageProcessingContextFactory() where TPixel : unmanaged, IPixel => default(DefaultImageOperationsProviderFactory).CreateImageProcessingContext(default, default, default); @@ -290,6 +190,7 @@ namespace SixLabors.ImageSharp.Advanced /// This method pre-seeds the all in the AoT compiler. /// /// The pixel format. + [Preserve] private static void AotCompileImageEncoderInternals() where TPixel : unmanaged, IPixel { @@ -304,6 +205,7 @@ namespace SixLabors.ImageSharp.Advanced /// This method pre-seeds the all in the AoT compiler. /// /// The pixel format. + [Preserve] private static void AotCompileImageDecoderInternals() where TPixel : unmanaged, IPixel { @@ -318,6 +220,7 @@ namespace SixLabors.ImageSharp.Advanced /// This method pre-seeds the all in the AoT compiler. /// /// The pixel format. + [Preserve] private static void AotCompileImageEncoders() where TPixel : unmanaged, IPixel { @@ -332,6 +235,7 @@ namespace SixLabors.ImageSharp.Advanced /// This method pre-seeds the all in the AoT compiler. /// /// The pixel format. + [Preserve] private static void AotCompileImageDecoders() where TPixel : unmanaged, IPixel { @@ -347,6 +251,7 @@ namespace SixLabors.ImageSharp.Advanced /// /// The pixel format. /// The encoder. + [Preserve] private static void AotCompileImageEncoder() where TPixel : unmanaged, IPixel where TEncoder : class, IImageEncoder @@ -360,6 +265,7 @@ namespace SixLabors.ImageSharp.Advanced /// /// The pixel format. /// The decoder. + [Preserve] private static void AotCompileImageDecoder() where TPixel : unmanaged, IPixel where TDecoder : class, IImageDecoder @@ -375,6 +281,7 @@ namespace SixLabors.ImageSharp.Advanced /// There is no structure that implements ISwizzler. /// /// The pixel format. + [Preserve] private static void AotCompileImageProcessors() where TPixel : unmanaged, IPixel { @@ -448,6 +355,7 @@ namespace SixLabors.ImageSharp.Advanced /// /// The pixel format. /// The processor type + [Preserve] private static void AotCompileImageProcessor() where TPixel : unmanaged, IPixel where TProc : class, IImageProcessor @@ -458,6 +366,7 @@ namespace SixLabors.ImageSharp.Advanced /// /// The pixel format. /// The processor type + [Preserve] private static void AotCompilerCloningImageProcessor() where TPixel : unmanaged, IPixel where TProc : class, ICloningImageProcessor @@ -470,6 +379,7 @@ namespace SixLabors.ImageSharp.Advanced /// There is no structure that implements ISwizzler. /// /// The pixel format. + [Preserve] private static void AotCompileGenericImageProcessors() where TPixel : unmanaged, IPixel { @@ -485,6 +395,7 @@ namespace SixLabors.ImageSharp.Advanced /// /// The pixel format. /// The processor type + [Preserve] private static void AotCompileGenericCloningImageProcessor() where TPixel : unmanaged, IPixel where TProc : class, ICloningImageProcessor @@ -494,6 +405,7 @@ namespace SixLabors.ImageSharp.Advanced /// This method pre-seeds the all in the AoT compiler. /// /// The pixel format. + [Preserve] private static void AotCompileResamplers() where TPixel : unmanaged, IPixel { @@ -511,6 +423,7 @@ namespace SixLabors.ImageSharp.Advanced /// /// The pixel format. /// The processor type + [Preserve] private static void AotCompileResampler() where TPixel : unmanaged, IPixel where TResampler : struct, IResampler @@ -527,6 +440,7 @@ namespace SixLabors.ImageSharp.Advanced /// This method pre-seeds the all in the AoT compiler. /// /// The pixel format. + [Preserve] private static void AotCompileQuantizers() where TPixel : unmanaged, IPixel { @@ -542,6 +456,7 @@ namespace SixLabors.ImageSharp.Advanced /// /// The pixel format. /// The quantizer type + [Preserve] private static void AotCompileQuantizer() where TPixel : unmanaged, IPixel @@ -555,11 +470,80 @@ namespace SixLabors.ImageSharp.Advanced /// This method pre-seeds the in the AoT compiler. /// /// The pixel format. + [Preserve] private static void AotCompilePixelSamplingStrategys() where TPixel : unmanaged, IPixel { default(DefaultPixelSamplingStrategy).EnumeratePixelRegions(default); default(ExtensivePixelSamplingStrategy).EnumeratePixelRegions(default); } + + /// + /// This method pre-seeds the all in the AoT compiler. + /// + /// The pixel format. + [Preserve] + private static void AotCompileDithers() + where TPixel : unmanaged, IPixel + { + AotCompileDither(); + AotCompileDither(); + } + + /// + /// This method pre-seeds the in the AoT compiler. + /// + /// The pixel format. + /// The dither. + [Preserve] + private static void AotCompileDither() + where TPixel : unmanaged, IPixel + where TDither : struct, IDither + { + var octree = default(OctreeQuantizer); + default(TDither).ApplyQuantizationDither, TPixel>(ref octree, default, default, default); + + var palette = default(PaletteQuantizer); + default(TDither).ApplyQuantizationDither, TPixel>(ref palette, default, default, default); + + var wu = default(WuQuantizer); + default(TDither).ApplyQuantizationDither, TPixel>(ref wu, default, default, default); + default(TDither).ApplyPaletteDither.DitherProcessor, TPixel>(default, default, default); + } + + /// + /// This method pre-seeds the all in the AoT compiler. + /// + /// The pixel format. + [Preserve] + private static void AotCompileMemoryManagers() + where TPixel : unmanaged, IPixel + { + AotCompileMemoryManager(); + AotCompileMemoryManager(); + } + + /// + /// This method pre-seeds the in the AoT compiler. + /// + /// The pixel format. + /// The buffer. + [Preserve] + private static void AotCompileMemoryManager() + where TPixel : unmanaged, IPixel + where TBuffer : MemoryAllocator + { + default(TBuffer).Allocate(default, default); + default(TBuffer).Allocate(default, default); + default(TBuffer).Allocate(default, default); + default(TBuffer).Allocate(default, default); + default(TBuffer).Allocate(default, default); + default(TBuffer).Allocate(default, default); + default(TBuffer).Allocate(default, default); + default(TBuffer).Allocate(default, default); + default(TBuffer).Allocate(default, default); + default(TBuffer).Allocate(default, default); + default(TBuffer).Allocate(default, default); + } } } diff --git a/src/ImageSharp/Advanced/PreserveAttribute.cs b/src/ImageSharp/Advanced/PreserveAttribute.cs new file mode 100644 index 000000000..a16b30e23 --- /dev/null +++ b/src/ImageSharp/Advanced/PreserveAttribute.cs @@ -0,0 +1,14 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Advanced +{ + /// + /// This is necessary to avoid being excluded from compilation in environments that do AOT builds, such as Unity's IL2CPP and Xamarin. + /// The only thing that matters is the class name. + /// There is no need to use or inherit from the PreserveAttribute class in each environment. + /// + internal sealed class PreserveAttribute : System.Attribute + { + } +} diff --git a/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs index 789d02046..4631cd422 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs @@ -72,7 +72,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// Used to allow inlining of calls to /// . /// - private readonly struct DitherProcessor : IPaletteDitherImageProcessor + /// Internal for AOT + internal readonly struct DitherProcessor : IPaletteDitherImageProcessor { private readonly EuclideanPixelMap pixelMap;