Browse Source

Merge pull request #2397 from stefannikolei/stefannikolei/jpegcolorconverter_arm

Add Arm intrinsics to JpegColorConverter RGB
pull/2412/head
James Jackson-South 3 years ago
committed by GitHub
parent
commit
5283d7736f
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      .github/workflows/build-and-test.yml
  2. 53
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbArm.cs
  3. 35
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterArm.cs
  4. 5
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs
  5. 8
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/RgbColorConversion.cs
  6. 47
      tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests.cs

5
.github/workflows/build-and-test.yml

@ -56,11 +56,6 @@ jobs:
sdk: 6.0.x
runtime: -x64
codecov: false
- os: buildjet-4vcpu-ubuntu-2204-arm
framework: net6.0
sdk: 6.0.x
runtime: -x64
codecov: false
exclude:
- isARM: false
options:

53
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbArm.cs

@ -0,0 +1,53 @@
// 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 System.Runtime.Intrinsics.X86;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components;
internal abstract partial class JpegColorConverterBase
{
internal sealed class RgbArm : JpegColorConverterArm
{
public RgbArm(int precision)
: base(JpegColorSpace.RGB, precision)
{
}
/// <inheritdoc/>
public override void ConvertToRgbInplace(in ComponentValues values)
{
ref Vector128<float> rBase =
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component0));
ref Vector128<float> gBase =
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component1));
ref Vector128<float> bBase =
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component2));
// Used for the color conversion
var scale = Vector128.Create(1 / this.MaximumValue);
nint n = (nint)(uint)values.Component0.Length / Vector128<float>.Count;
for (nint i = 0; i < n; i++)
{
ref Vector128<float> r = ref Unsafe.Add(ref rBase, i);
ref Vector128<float> g = ref Unsafe.Add(ref gBase, i);
ref Vector128<float> b = ref Unsafe.Add(ref bBase, i);
r = AdvSimd.Multiply(r, scale);
g = AdvSimd.Multiply(g, scale);
b = AdvSimd.Multiply(b, scale);
}
}
/// <inheritdoc/>
public override void ConvertFromRgb(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane)
{
rLane.CopyTo(values.Component0);
gLane.CopyTo(values.Component1);
bLane.CopyTo(values.Component2);
}
}
}

35
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterArm.cs

@ -0,0 +1,35 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.Arm;
using System.Runtime.Intrinsics.X86;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components;
internal abstract partial class JpegColorConverterBase
{
/// <summary>
/// <see cref="JpegColorConverterBase"/> abstract base for implementations
/// based on <see cref="Avx"/> instructions.
/// </summary>
/// <remarks>
/// Converters of this family would expect input buffers lengths to be
/// divisible by 8 without a remainder.
/// This is guaranteed by real-life data as jpeg stores pixels via 8x8 blocks.
/// DO NOT pass test data of invalid size to these converters as they
/// potentially won't do a bound check and return a false positive result.
/// </remarks>
internal abstract class JpegColorConverterArm : JpegColorConverterBase
{
protected JpegColorConverterArm(JpegColorSpace colorSpace, int precision)
: base(colorSpace, precision)
{
}
public static bool IsSupported => AdvSimd.IsSupported;
public sealed override bool IsAvailable => IsSupported;
public sealed override int ElementsPerBatch => Vector128<float>.Count;
}
}

5
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs

@ -214,6 +214,11 @@ internal abstract partial class JpegColorConverterBase
return new RgbAvx(precision);
}
if (JpegColorConverterArm.IsSupported)
{
return new RgbArm(precision);
}
if (JpegColorConverterVector.IsSupported)
{
return new RgbScalar(precision);

8
tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/RgbColorConversion.cs

@ -37,4 +37,12 @@ public class RgbColorConversion : ColorConversionBenchmark
new JpegColorConverterBase.RgbAvx(8).ConvertToRgbInplace(values);
}
[Benchmark]
public void SimdVectorArm()
{
var values = new JpegColorConverterBase.ComponentValues(this.Input, 0);
new JpegColorConverterBase.RgbArm(8).ConvertToRgbInplace(values);
}
}

47
tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests.cs

@ -3,6 +3,8 @@
using System.Numerics;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
using Castle.Components.DictionaryAdapter;
using SixLabors.ImageSharp.PixelFormats.PixelBlenders;
using SixLabors.ImageSharp.Tests.TestUtilities;
@ -30,6 +32,11 @@ public class PorterDuffFunctionsTests
[MemberData(nameof(NormalBlendFunctionData))]
public void NormalBlendFunction256(TestVector4 back, TestVector4 source, float amount, TestVector4 expected)
{
if (!Avx.IsSupported)
{
return;
}
Vector256<float> back256 = Vector256.Create(back.X, back.Y, back.Z, back.W, back.X, back.Y, back.Z, back.W);
Vector256<float> source256 = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W);
@ -57,6 +64,11 @@ public class PorterDuffFunctionsTests
[MemberData(nameof(MultiplyFunctionData))]
public void MultiplyFunction256(TestVector4 back, TestVector4 source, float amount, TestVector4 expected)
{
if (!Avx.IsSupported)
{
return;
}
Vector256<float> back256 = Vector256.Create(back.X, back.Y, back.Z, back.W, back.X, back.Y, back.Z, back.W);
Vector256<float> source256 = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W);
@ -84,6 +96,11 @@ public class PorterDuffFunctionsTests
[MemberData(nameof(AddFunctionData))]
public void AddFunction256(TestVector4 back, TestVector4 source, float amount, TestVector4 expected)
{
if (!Avx.IsSupported)
{
return;
}
Vector256<float> back256 = Vector256.Create(back.X, back.Y, back.Z, back.W, back.X, back.Y, back.Z, back.W);
Vector256<float> source256 = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W);
@ -111,6 +128,11 @@ public class PorterDuffFunctionsTests
[MemberData(nameof(SubtractFunctionData))]
public void SubtractFunction256(TestVector4 back, TestVector4 source, float amount, TestVector4 expected)
{
if (!Avx.IsSupported)
{
return;
}
Vector256<float> back256 = Vector256.Create(back.X, back.Y, back.Z, back.W, back.X, back.Y, back.Z, back.W);
Vector256<float> source256 = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W);
@ -138,6 +160,11 @@ public class PorterDuffFunctionsTests
[MemberData(nameof(ScreenFunctionData))]
public void ScreenFunction256(TestVector4 back, TestVector4 source, float amount, TestVector4 expected)
{
if (!Avx.IsSupported)
{
return;
}
Vector256<float> back256 = Vector256.Create(back.X, back.Y, back.Z, back.W, back.X, back.Y, back.Z, back.W);
Vector256<float> source256 = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W);
@ -165,6 +192,11 @@ public class PorterDuffFunctionsTests
[MemberData(nameof(DarkenFunctionData))]
public void DarkenFunction256(TestVector4 back, TestVector4 source, float amount, TestVector4 expected)
{
if (!Avx.IsSupported)
{
return;
}
Vector256<float> back256 = Vector256.Create(back.X, back.Y, back.Z, back.W, back.X, back.Y, back.Z, back.W);
Vector256<float> source256 = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W);
@ -192,6 +224,11 @@ public class PorterDuffFunctionsTests
[MemberData(nameof(LightenFunctionData))]
public void LightenFunction256(TestVector4 back, TestVector4 source, float amount, TestVector4 expected)
{
if (!Avx.IsSupported)
{
return;
}
Vector256<float> back256 = Vector256.Create(back.X, back.Y, back.Z, back.W, back.X, back.Y, back.Z, back.W);
Vector256<float> source256 = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W);
@ -219,6 +256,11 @@ public class PorterDuffFunctionsTests
[MemberData(nameof(OverlayFunctionData))]
public void OverlayFunction256(TestVector4 back, TestVector4 source, float amount, TestVector4 expected)
{
if (!Avx.IsSupported)
{
return;
}
Vector256<float> back256 = Vector256.Create(back.X, back.Y, back.Z, back.W, back.X, back.Y, back.Z, back.W);
Vector256<float> source256 = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W);
@ -246,6 +288,11 @@ public class PorterDuffFunctionsTests
[MemberData(nameof(HardLightFunctionData))]
public void HardLightFunction256(TestVector4 back, TestVector4 source, float amount, TestVector4 expected)
{
if (!Avx.IsSupported)
{
return;
}
Vector256<float> back256 = Vector256.Create(back.X, back.Y, back.Z, back.W, back.X, back.Y, back.Z, back.W);
Vector256<float> source256 = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W);

Loading…
Cancel
Save