From a4fe64abb7f0abf718ba5502b75c633121938999 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Wed, 22 Mar 2023 18:52:13 +0100 Subject: [PATCH 1/4] Port GrayscalConverter to Arm --- .../Common/Helpers/SimdUtils.HwIntrinsics.cs | 29 ++++++++ .../JpegColorConverter.GrayScaleArm.cs | 68 +++++++++++++++++++ .../GrayscaleColorConversion.cs | 8 +++ .../Formats/Jpg/JpegColorConverterTests.cs | 19 +++++- 4 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleArm.cs diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs index 3841b64b4d..819837be13 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs @@ -4,6 +4,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.Arm; using System.Runtime.Intrinsics.X86; using SixLabors.ImageSharp.PixelFormats; @@ -554,6 +555,34 @@ public static Vector256 MultiplyAdd( return Avx.Add(Avx.Multiply(vm0, vm1), va); } + /// + /// Performs a multiplication and an addition of the . + /// TODO: Fix. The arguments are in a different order to the FMA intrinsic. + /// + /// ret = (vm0 * vm1) + va + /// The vector to add to the intermediate result. + /// The first vector to multiply. + /// The second vector to multiply. + /// The . + [MethodImpl(InliningOptions.AlwaysInline)] + public static Vector128 MultiplyAdd( + in Vector128 va, + in Vector128 vm0, + in Vector128 vm1) + { + if (Fma.IsSupported) + { + return Fma.MultiplyAdd(vm1, vm0, va); + } + + if (AdvSimd.IsSupported) + { + return AdvSimd.Add(AdvSimd.Multiply(vm0, vm1), va); + } + + return Avx.Add(Avx.Multiply(vm0, vm1), va); + } + /// /// Performs a multiplication and a subtraction of the . /// TODO: Fix. The arguments are in a different order to the FMA intrinsic. diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleArm.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleArm.cs new file mode 100644 index 0000000000..56e51eb53f --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleArm.cs @@ -0,0 +1,68 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.Arm; +using static SixLabors.ImageSharp.SimdUtils; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Components; + +internal abstract partial class JpegColorConverterBase +{ + internal sealed class GrayscaleArm : JpegColorConverterArm + { + public GrayscaleArm(int precision) + : base(JpegColorSpace.Grayscale, precision) + { + } + + /// + public override void ConvertToRgbInplace(in ComponentValues values) + { + ref Vector128 c0Base = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); + + // Used for the color conversion + var scale = Vector128.Create(1 / this.MaximumValue); + + nint n = values.Component0.Length / Vector128.Count; + for (nint i = 0; i < n; i++) + { + ref Vector128 c0 = ref Unsafe.Add(ref c0Base, i); + c0 = AdvSimd.Multiply(c0, scale); + } + } + + /// + public override void ConvertFromRgb(in ComponentValues values, Span rLane, Span gLane, Span bLane) + { + ref Vector128 destLuminance = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); + + ref Vector128 srcRed = + ref Unsafe.As>(ref MemoryMarshal.GetReference(rLane)); + ref Vector128 srcGreen = + ref Unsafe.As>(ref MemoryMarshal.GetReference(gLane)); + ref Vector128 srcBlue = + ref Unsafe.As>(ref MemoryMarshal.GetReference(bLane)); + + // Used for the color conversion + var f0299 = Vector128.Create(0.299f); + var f0587 = Vector128.Create(0.587f); + var f0114 = Vector128.Create(0.114f); + + nint n = values.Component0.Length / Vector128.Count; + for (nint i = 0; i < n; i++) + { + ref Vector128 r = ref Unsafe.Add(ref srcRed, i); + ref Vector128 g = ref Unsafe.Add(ref srcGreen, i); + ref Vector128 b = ref Unsafe.Add(ref srcBlue, i); + + // luminocity = (0.299 * r) + (0.587 * g) + (0.114 * b) + Unsafe.Add(ref destLuminance, i) = HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(AdvSimd.Multiply(f0114, b), f0587, g), f0299, r); + } + } + } +} diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/GrayscaleColorConversion.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/GrayscaleColorConversion.cs index 47aac3464e..8bf26d721d 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/GrayscaleColorConversion.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/GrayscaleColorConversion.cs @@ -29,4 +29,12 @@ public void SimdVectorAvx() new JpegColorConverterBase.GrayscaleAvx(8).ConvertToRgbInplace(values); } + + [Benchmark] + public void SimdVectorArm() + { + var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); + + new JpegColorConverterBase.GrayscaleArm(8).ConvertToRgbInplace(values); + } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs index 44675aaea2..c71c70c336 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs @@ -307,6 +307,23 @@ public void FromRgbToGrayscaleAvx2(int seed) => new JpegColorConverterBase.GrayscaleScalar(8), precísion: 3); + [Theory] + [MemberData(nameof(Seeds))] + public void FromGrayscaleArm(int seed) => + this.TestConversionToRgb(new JpegColorConverterBase.GrayscaleArm(8), + 1, + seed, + new JpegColorConverterBase.GrayscaleScalar(8)); + + [Theory] + [MemberData(nameof(Seeds))] + public void FromRgbToGrayscaleArm(int seed) => + this.TestConversionFromRgb(new JpegColorConverterBase.GrayscaleArm(8), + 1, + seed, + new JpegColorConverterBase.GrayscaleScalar(8), + precísion: 3); + [Theory] [MemberData(nameof(Seeds))] public void FromRgbAvx2(int seed) => @@ -480,7 +497,7 @@ private static void ValidateConversionFromRgb( JpegColorConverterBase baseLineConverter, int precision = 4) { - // arrange + // arrange JpegColorConverterBase.ComponentValues actual = CreateRandomValues(TestBufferLength, componentCount, seed); JpegColorConverterBase.ComponentValues expected = CreateRandomValues(TestBufferLength, componentCount, seed); Random rnd = new(seed); From 210a3d6e67ff99a1fc43b75bfc43aa0aeaef9c7a Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Thu, 23 Mar 2023 13:50:57 +0100 Subject: [PATCH 2/4] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Günther Foidl --- src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs | 6 +++--- .../ColorConverters/JpegColorConverter.GrayScaleArm.cs | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs index 819837be13..2cb205a7d5 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs @@ -566,9 +566,9 @@ public static Vector256 MultiplyAdd( /// The . [MethodImpl(InliningOptions.AlwaysInline)] public static Vector128 MultiplyAdd( - in Vector128 va, - in Vector128 vm0, - in Vector128 vm1) + Vector128 va, + Vector128 vm0, + Vector128 vm1) { if (Fma.IsSupported) { diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleArm.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleArm.cs index 56e51eb53f..a8d5f49c24 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleArm.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleArm.cs @@ -27,8 +27,8 @@ public override void ConvertToRgbInplace(in ComponentValues values) // Used for the color conversion var scale = Vector128.Create(1 / this.MaximumValue); - nint n = values.Component0.Length / Vector128.Count; - for (nint i = 0; i < n; i++) + nuint n = (uint)values.Component0.Length / (uint)Vector128.Count; + for (nuint i = 0; i < n; i++) { ref Vector128 c0 = ref Unsafe.Add(ref c0Base, i); c0 = AdvSimd.Multiply(c0, scale); @@ -53,8 +53,8 @@ public override void ConvertFromRgb(in ComponentValues values, Span rLane var f0587 = Vector128.Create(0.587f); var f0114 = Vector128.Create(0.114f); - nint n = values.Component0.Length / Vector128.Count; - for (nint i = 0; i < n; i++) + nuint n = (uint)values.Component0.Length / (uint)Vector128.Count; + for (nuint i = 0; i < n; i++) { ref Vector128 r = ref Unsafe.Add(ref srcRed, i); ref Vector128 g = ref Unsafe.Add(ref srcGreen, i); From 65e7761bf9fbec9b30becd676c876a64ee5f4ad2 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Fri, 24 Mar 2023 05:18:37 +0000 Subject: [PATCH 3/4] Update src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Günther Foidl --- src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs index 2cb205a7d5..dce713a807 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs @@ -580,7 +580,7 @@ public static Vector128 MultiplyAdd( return AdvSimd.Add(AdvSimd.Multiply(vm0, vm1), va); } - return Avx.Add(Avx.Multiply(vm0, vm1), va); + return Sse.Add(Sse.Multiply(vm0, vm1), va); } /// From 809ba98186f40729d2c7cbcac57af3524a8c7e90 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Sun, 26 Mar 2023 14:36:38 +0200 Subject: [PATCH 4/4] Added the arm converter to GetGrayScaleConverter --- .../Components/ColorConverters/JpegColorConverterBase.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs index 58b0b9b1b2..44b4ed4ec3 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs @@ -200,6 +200,11 @@ private static JpegColorConverterBase GetGrayScaleConverter(int precision) return new GrayscaleAvx(precision); } + if (JpegColorConverterArm.IsSupported) + { + return new GrayscaleArm(precision); + } + if (JpegColorConverterVector.IsSupported) { return new GrayScaleVector(precision);