From 134fa595ed13eb0d2bef997236b6961edfa1425b Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Wed, 20 Sep 2017 02:48:39 +0200 Subject: [PATCH] JpegColorConverter.FromYCbCrSimdAvx2 works! --- src/ImageSharp/Common/Tuples/Vector4Pair.cs | 5 ++ .../Formats/Jpeg/Common/Block8x8.cs | 3 + .../Formats/Jpeg/Common/Block8x8F.CopyTo.cs | 5 +- .../JpegColorConverter.FromCmyk.cs | 5 +- .../JpegColorConverter.FromGrayScale.cs | 5 +- .../JpegColorConverter.FromRgb.cs | 5 +- .../JpegColorConverter.FromYCbCrBasic.cs | 5 +- .../JpegColorConverter.FromYCbCrSimd.cs | 5 +- ...> JpegColorConverter.FromYCbCrSimdAvx2.cs} | 35 ++++++++--- .../JpegColorConverter.FromYccK.cs | 7 ++- .../ColorConverters/JpegColorConverter.cs | 11 +++- .../Formats/Jpeg/Common/SizeExtensions.cs | 3 + .../Color/Bulk/PackFromVector4.cs | 2 +- tests/ImageSharp.Benchmarks/Config.cs | 4 +- .../General/ArrayCopy.cs | 2 +- .../Image/EncodeBmpMultiple.cs | 2 +- .../Image/Jpeg/DecodeJpeg.cs | 2 +- .../Image/Jpeg/DecodeJpegMultiple.cs | 2 +- .../Image/Jpeg/EncodeJpegMultiple.cs | 2 +- .../Image/Jpeg/YCbCrColorConversion.cs | 62 +++++++++++++++++-- .../Formats/Jpg/JpegColorConverterTests.cs | 2 + 21 files changed, 141 insertions(+), 33 deletions(-) rename src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/{JpegColorConverter.FromYCbCr.cs => JpegColorConverter.FromYCbCrSimdAvx2.cs} (77%) diff --git a/src/ImageSharp/Common/Tuples/Vector4Pair.cs b/src/ImageSharp/Common/Tuples/Vector4Pair.cs index 4f43c9811..309d5e2e5 100644 --- a/src/ImageSharp/Common/Tuples/Vector4Pair.cs +++ b/src/ImageSharp/Common/Tuples/Vector4Pair.cs @@ -71,5 +71,10 @@ namespace SixLabors.ImageSharp.Common.Tuples v *= new Vector(1 / 255f); self = v; } + + public override string ToString() + { + return $"{this.A}, {this.B}"; + } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs b/src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs index 96ed0a8c9..1066cfa80 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs @@ -1,3 +1,6 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + using System; using System.Diagnostics; using System.Runtime.CompilerServices; diff --git a/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.CopyTo.cs b/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.CopyTo.cs index c1c5cfcde..39a6bee2e 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.CopyTo.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.CopyTo.cs @@ -1,4 +1,7 @@ -using System.Numerics; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Memory; diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromCmyk.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromCmyk.cs index b67d73fd4..86d595784 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromCmyk.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromCmyk.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; using System.Numerics; namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs index ffc3f80df..5c720e61c 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; using System.Numerics; namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromRgb.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromRgb.cs index fb6bf91ff..7f01eedad 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromRgb.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromRgb.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; using System.Numerics; namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrBasic.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrBasic.cs index 88b190675..ddd2197d4 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrBasic.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrBasic.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; using System.Numerics; namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs index 9d985dca6..a7fc136af 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Common.Tuples; diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCr.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs similarity index 77% rename from src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCr.cs rename to src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs index 7fa6c92e9..77e74c32b 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCr.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs @@ -1,8 +1,12 @@ -using System; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Common.Tuples; +// ReSharper disable ImpureMethodCallOnReadonlyValueField namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters { internal abstract partial class JpegColorConverter @@ -14,7 +18,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters { } - public static bool IsAvailable => Vector.IsHardwareAccelerated && Vector.Count == 8; + public static bool IsAvailable => Vector.IsHardwareAccelerated && SimdUtils.IsAvx2CompatibleArchitecture; public override void ConvertToRGBA(ComponentValues values, Span result) { @@ -56,6 +60,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters // Walking 8 elements at one step: int n = result.Length / 8; + var rr = default(Vector4Pair); + var gg = default(Vector4Pair); + var bb = default(Vector4Pair); + + ref Vector rrRefAsVector = ref Unsafe.As>(ref rr); + ref Vector ggRefAsVector = ref Unsafe.As>(ref gg); + ref Vector bbRefAsVector = ref Unsafe.As>(ref bb); + + var scale = new Vector(1 / 255f); + for (int i = 0; i < n; i++) { // y = yVals[i]; @@ -73,15 +87,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters Vector g = y - (cb * new Vector(0.344136F)) - (cr * new Vector(0.714136F)); Vector b = y + (cb * new Vector(1.772F)); - // Vector has no .Clamp(), need to switch to Vector4 for the next operation: - // TODO: Is it worth to use Vector at all? - Vector4Pair rr = Unsafe.As, Vector4Pair>(ref r); - Vector4Pair gg = Unsafe.As, Vector4Pair>(ref g); - Vector4Pair bb = Unsafe.As, Vector4Pair>(ref b); + r = r.FastRound(); + g = g.FastRound(); + b = b.FastRound(); + r *= scale; + g *= scale; + b *= scale; - rr.RoundAndDownscaleAvx2(); - gg.RoundAndDownscaleAvx2(); - bb.RoundAndDownscaleAvx2(); + rrRefAsVector = r; + ggRefAsVector = g; + bbRefAsVector = b; // Collect (r0,r1...r8) (g0,g1...g8) (b0,b1...b8) vector values in the expected (r0,g0,g1,1), (r1,g1,g2,1) ... order: ref Vector4Octet destination = ref Unsafe.Add(ref resultBase, i); diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYccK.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYccK.cs index 22e524c70..6d8e6ef5a 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYccK.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYccK.cs @@ -1,11 +1,14 @@ -using System; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; using System.Numerics; namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters { internal abstract partial class JpegColorConverter { - internal class FromYccK : ColorConverters.JpegColorConverter + internal class FromYccK : JpegColorConverter { public FromYccK() : base(JpegColorSpace.Ycck) diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.cs index 0bdb8ff7b..e0abc3215 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.cs @@ -1,3 +1,6 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + using System; using System.Collections.Generic; using System.Linq; @@ -17,7 +20,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters /// private static readonly JpegColorConverter[] Converters = { - new FromYCbCrSimd(), new FromYccK(), new FromCmyk(), new FromGrayScale(), new FromRgb() + GetYCbCrConverter(), new FromYccK(), new FromCmyk(), new FromGrayScale(), new FromRgb() }; /// @@ -54,6 +57,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters /// The destination buffer of values public abstract void ConvertToRGBA(ComponentValues values, Span result); + /// + /// Returns the for the YCbCr colorspace that matches the current CPU architecture. + /// + private static JpegColorConverter GetYCbCrConverter() => + FromYCbCrSimdAvx2.IsAvailable ? (JpegColorConverter)new FromYCbCrSimdAvx2() : new FromYCbCrSimd(); + /// /// A stack-only struct to reference the input buffers using -s. /// diff --git a/src/ImageSharp/Formats/Jpeg/Common/SizeExtensions.cs b/src/ImageSharp/Formats/Jpeg/Common/SizeExtensions.cs index b9bfe425a..f6b02bbaf 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/SizeExtensions.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/SizeExtensions.cs @@ -1,3 +1,6 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + using System.Numerics; using SixLabors.Primitives; diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs index e88981959..83c2a2ee8 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Color.Bulk using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; - [Config(typeof(Config.Short))] + [Config(typeof(Config.ShortClr))] public abstract class PackFromVector4 where TPixel : struct, IPixel { diff --git a/tests/ImageSharp.Benchmarks/Config.cs b/tests/ImageSharp.Benchmarks/Config.cs index 9577bebd3..17ce3a07d 100644 --- a/tests/ImageSharp.Benchmarks/Config.cs +++ b/tests/ImageSharp.Benchmarks/Config.cs @@ -17,9 +17,9 @@ namespace SixLabors.ImageSharp.Benchmarks this.Add(new BenchmarkDotNet.Diagnosers.MemoryDiagnoser()); } - public class Short : Config + public class ShortClr : Config { - public Short() + public ShortClr() { this.Add( Job.Clr.WithLaunchCount(1) diff --git a/tests/ImageSharp.Benchmarks/General/ArrayCopy.cs b/tests/ImageSharp.Benchmarks/General/ArrayCopy.cs index ddfa0f08b..ac6b3f93c 100644 --- a/tests/ImageSharp.Benchmarks/General/ArrayCopy.cs +++ b/tests/ImageSharp.Benchmarks/General/ArrayCopy.cs @@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.Benchmarks.General using BenchmarkDotNet.Attributes; - [Config(typeof(Config.Short))] + [Config(typeof(Config.ShortClr))] public class ArrayCopy { [Params(10, 100, 1000, 10000)] diff --git a/tests/ImageSharp.Benchmarks/Image/EncodeBmpMultiple.cs b/tests/ImageSharp.Benchmarks/Image/EncodeBmpMultiple.cs index e83712ffc..c509d3555 100644 --- a/tests/ImageSharp.Benchmarks/Image/EncodeBmpMultiple.cs +++ b/tests/ImageSharp.Benchmarks/Image/EncodeBmpMultiple.cs @@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Image using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Bmp; - [Config(typeof(Config.Short))] + [Config(typeof(Config.ShortClr))] public class EncodeBmpMultiple : MultiImageBenchmarkBase.WithImagesPreloaded { protected override IEnumerable InputImageSubfoldersOrFiles => new[] { "Bmp/", "Jpg/baseline" }; diff --git a/tests/ImageSharp.Benchmarks/Image/Jpeg/DecodeJpeg.cs b/tests/ImageSharp.Benchmarks/Image/Jpeg/DecodeJpeg.cs index 6d1354ca2..6cb5f8a21 100644 --- a/tests/ImageSharp.Benchmarks/Image/Jpeg/DecodeJpeg.cs +++ b/tests/ImageSharp.Benchmarks/Image/Jpeg/DecodeJpeg.cs @@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Image.Jpeg using CoreSize = SixLabors.Primitives.Size; - [Config(typeof(Config.Short))] + [Config(typeof(Config.ShortClr))] public class DecodeJpeg : BenchmarkBase { private byte[] jpegBytes; diff --git a/tests/ImageSharp.Benchmarks/Image/Jpeg/DecodeJpegMultiple.cs b/tests/ImageSharp.Benchmarks/Image/Jpeg/DecodeJpegMultiple.cs index d5eb5a20c..bc60c5669 100644 --- a/tests/ImageSharp.Benchmarks/Image/Jpeg/DecodeJpegMultiple.cs +++ b/tests/ImageSharp.Benchmarks/Image/Jpeg/DecodeJpegMultiple.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Image.Jpeg using CoreImage = SixLabors.ImageSharp.Image; - [Config(typeof(Config.Short))] + [Config(typeof(Config.ShortClr))] public class DecodeJpegMultiple : MultiImageBenchmarkBase { protected override IEnumerable InputImageSubfoldersOrFiles => new[] diff --git a/tests/ImageSharp.Benchmarks/Image/Jpeg/EncodeJpegMultiple.cs b/tests/ImageSharp.Benchmarks/Image/Jpeg/EncodeJpegMultiple.cs index 27144ff6f..4d28f5a19 100644 --- a/tests/ImageSharp.Benchmarks/Image/Jpeg/EncodeJpegMultiple.cs +++ b/tests/ImageSharp.Benchmarks/Image/Jpeg/EncodeJpegMultiple.cs @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Image.Jpeg using SixLabors.ImageSharp.Formats.Jpeg; - [Config(typeof(Config.Short))] // It's long enough to iterate through multiple files + [Config(typeof(Config.ShortClr))] // It's long enough to iterate through multiple files public class EncodeJpegMultiple : MultiImageBenchmarkBase.WithImagesPreloaded { protected override IEnumerable InputImageSubfoldersOrFiles => new[] { "Bmp/", "Jpg/baseline" }; diff --git a/tests/ImageSharp.Benchmarks/Image/Jpeg/YCbCrColorConversion.cs b/tests/ImageSharp.Benchmarks/Image/Jpeg/YCbCrColorConversion.cs index b76a79588..eced38803 100644 --- a/tests/ImageSharp.Benchmarks/Image/Jpeg/YCbCrColorConversion.cs +++ b/tests/ImageSharp.Benchmarks/Image/Jpeg/YCbCrColorConversion.cs @@ -1,15 +1,64 @@ namespace SixLabors.ImageSharp.Benchmarks.Image.Jpeg { using System; + using System.Numerics; + + using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder; + using SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters; using SixLabors.ImageSharp.Memory; - - using JpegColorConverter = SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters.JpegColorConverter; - + + [Config(typeof(Config.ShortClr))] public class YCbCrColorConversion { - private static JpegColorConverter.ComponentValues CreateRandomValues( + private Buffer2D[] input; + + private Vector4[] output; + + public const int Count = 64; + + [GlobalSetup] + public void Setup() + { + this.input = CreateRandomValues(3, Count); + this.output = new Vector4[Count]; + } + + [GlobalCleanup] + public void Cleanup() + { + foreach (Buffer2D buffer in this.input) + { + buffer.Dispose(); + } + } + + [Benchmark(Baseline = true)] + public void Scalar() + { + var values = new JpegColorConverter.ComponentValues(this.input, 0); + + JpegColorConverter.FromYCbCrBasic.ConvertCore(values, this.output); + } + + [Benchmark] + public void SimdVector4() + { + var values = new JpegColorConverter.ComponentValues(this.input, 0); + + JpegColorConverter.FromYCbCrSimd.ConvertCore(values, this.output); + } + + [Benchmark] + public void SimdAvx2() + { + var values = new JpegColorConverter.ComponentValues(this.input, 0); + + JpegColorConverter.FromYCbCrSimdAvx2.ConvertCore(values, this.output); + } + + private static Buffer2D[] CreateRandomValues( int componentCount, int inputBufferLength, float minVal = 0f, @@ -29,8 +78,9 @@ // no need to dispose when buffer is not array owner buffers[i] = new Buffer2D(values, values.Length, 1); } - return new JpegColorConverter.ComponentValues(buffers, 0); - } + return buffers; + } + } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs index 239d9d8d9..46f137f81 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs @@ -89,6 +89,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg return; } + //JpegColorConverter.FromYCbCrSimdAvx2.LogPlz = s => this.Output.WriteLine(s); + ValidateConversion( new JpegColorConverter.FromYCbCrSimdAvx2(), 3,