Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,11 @@ private static JpegColorConverterBase GetRgbConverter(int precision)
return new RgbAvx(precision);
}

if (JpegColorConverterArm.IsSupported)
{
return new RgbArm(precision);
}

if (JpegColorConverterVector.IsSupported)
{
return new RgbScalar(precision);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,12 @@ public void SimdVectorAvx()

new JpegColorConverterBase.RgbAvx(8).ConvertToRgbInplace(values);
}

[Benchmark]
public void SimdVectorArm()
{
var values = new JpegColorConverterBase.ComponentValues(this.Input, 0);

new JpegColorConverterBase.RgbArm(8).ConvertToRgbInplace(values);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -30,6 +32,11 @@ public void NormalBlendFunction(TestVector4 back, TestVector4 source, float amou
[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);

Expand Down Expand Up @@ -57,6 +64,11 @@ public void MultiplyFunction(TestVector4 back, TestVector4 source, float amount,
[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);

Expand Down Expand Up @@ -84,6 +96,11 @@ public void AddFunction(TestVector4 back, TestVector4 source, float amount, Test
[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);

Expand Down Expand Up @@ -111,6 +128,11 @@ public void SubtractFunction(TestVector4 back, TestVector4 source, float amount,
[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);

Expand Down Expand Up @@ -138,6 +160,11 @@ public void ScreenFunction(TestVector4 back, TestVector4 source, float amount, T
[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);

Expand Down Expand Up @@ -165,6 +192,11 @@ public void DarkenFunction(TestVector4 back, TestVector4 source, float amount, T
[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);

Expand Down Expand Up @@ -192,6 +224,11 @@ public void LightenFunction(TestVector4 back, TestVector4 source, float amount,
[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);

Expand Down Expand Up @@ -219,6 +256,11 @@ public void OverlayFunction(TestVector4 back, TestVector4 source, float amount,
[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);

Expand Down Expand Up @@ -246,6 +288,11 @@ public void HardLightFunction(TestVector4 back, TestVector4 source, float amount
[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);

Expand Down