Browse Source

Introduce new utilities to replace poor performing runtime methods

pull/2645/head
James Jackson-South 3 years ago
parent
commit
b250ac3923
  1. 173
      src/ImageSharp/Common/Helpers/Shuffle/IComponentShuffle.cs
  2. 26
      src/ImageSharp/Common/Helpers/Shuffle/IPad3Shuffle4.cs
  3. 18
      src/ImageSharp/Common/Helpers/Shuffle/IShuffle3.cs
  4. 178
      src/ImageSharp/Common/Helpers/Shuffle/IShuffle4.cs
  5. 28
      src/ImageSharp/Common/Helpers/Shuffle/IShuffle4Slice3.cs
  6. 230
      src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs
  7. 83
      src/ImageSharp/Common/Helpers/Vector128Utilities.cs
  8. 78
      src/ImageSharp/Common/Helpers/Vector256Utilities.cs
  9. 80
      src/ImageSharp/Common/Helpers/Vector512Utilities.cs

173
src/ImageSharp/Common/Helpers/Shuffle/IComponentShuffle.cs

@ -1,12 +1,6 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Buffers.Binary;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using static SixLabors.ImageSharp.SimdUtils;
// The JIT can detect and optimize rotation idioms ROTL (Rotate Left)
// and ROTR (Rotate Right) emitting efficient CPU instructions:
// https://github.com/dotnet/coreclr/pull/1830
@ -20,7 +14,7 @@ internal interface IComponentShuffle
{
/// <summary>
/// Shuffles then slices 8-bit integers in <paramref name="source"/>
/// using the control and store the results in <paramref name="destination"/>.
/// using a byte control and store the results in <paramref name="destination"/>.
/// If successful, this method will reduce the length of <paramref name="source"/> length
/// by the shuffle amount.
/// </summary>
@ -40,168 +34,3 @@ internal interface IComponentShuffle
/// </remarks>
void Shuffle(ReadOnlySpan<byte> source, Span<byte> destination);
}
/// <inheritdoc/>
internal interface IShuffle4 : IComponentShuffle
{
}
internal readonly struct DefaultShuffle4(byte control) : IShuffle4
{
public byte Control { get; } = control;
[MethodImpl(InliningOptions.ShortMethod)]
public void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> destination)
=> HwIntrinsics.Shuffle4Reduce(ref source, ref destination, this.Control);
[MethodImpl(InliningOptions.ShortMethod)]
public void Shuffle(ReadOnlySpan<byte> source, Span<byte> destination)
{
ref byte sBase = ref MemoryMarshal.GetReference(source);
ref byte dBase = ref MemoryMarshal.GetReference(destination);
SimdUtils.Shuffle.InverseMMShuffle(this.Control, out uint p3, out uint p2, out uint p1, out uint p0);
for (nuint i = 0; i < (uint)source.Length; i += 4)
{
Unsafe.Add(ref dBase, i + 0) = Unsafe.Add(ref sBase, p0 + i);
Unsafe.Add(ref dBase, i + 1) = Unsafe.Add(ref sBase, p1 + i);
Unsafe.Add(ref dBase, i + 2) = Unsafe.Add(ref sBase, p2 + i);
Unsafe.Add(ref dBase, i + 3) = Unsafe.Add(ref sBase, p3 + i);
}
}
}
internal readonly struct WXYZShuffle4 : IShuffle4
{
[MethodImpl(InliningOptions.ShortMethod)]
public void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> destination)
=> HwIntrinsics.Shuffle4Reduce(ref source, ref destination, SimdUtils.Shuffle.MMShuffle2103);
[MethodImpl(InliningOptions.ShortMethod)]
public void Shuffle(ReadOnlySpan<byte> source, Span<byte> destination)
{
ref uint sBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(source));
ref uint dBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(destination));
uint n = (uint)source.Length / 4;
for (nuint i = 0; i < n; i++)
{
uint packed = Unsafe.Add(ref sBase, i);
// packed = [W Z Y X]
// ROTL(8, packed) = [Z Y X W]
Unsafe.Add(ref dBase, i) = (packed << 8) | (packed >> 24);
}
}
}
internal readonly struct WZYXShuffle4 : IShuffle4
{
[MethodImpl(InliningOptions.ShortMethod)]
public void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> destination)
=> HwIntrinsics.Shuffle4Reduce(ref source, ref destination, SimdUtils.Shuffle.MMShuffle0123);
[MethodImpl(InliningOptions.ShortMethod)]
public void Shuffle(ReadOnlySpan<byte> source, Span<byte> destination)
{
ref uint sBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(source));
ref uint dBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(destination));
uint n = (uint)source.Length / 4;
for (nuint i = 0; i < n; i++)
{
uint packed = Unsafe.Add(ref sBase, i);
// packed = [W Z Y X]
// REVERSE(packedArgb) = [X Y Z W]
Unsafe.Add(ref dBase, i) = BinaryPrimitives.ReverseEndianness(packed);
}
}
}
internal readonly struct YZWXShuffle4 : IShuffle4
{
[MethodImpl(InliningOptions.ShortMethod)]
public void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> destination)
=> HwIntrinsics.Shuffle4Reduce(ref source, ref destination, SimdUtils.Shuffle.MMShuffle0321);
[MethodImpl(InliningOptions.ShortMethod)]
public void Shuffle(ReadOnlySpan<byte> source, Span<byte> destination)
{
ref uint sBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(source));
ref uint dBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(destination));
uint n = (uint)source.Length / 4;
for (nuint i = 0; i < n; i++)
{
uint packed = Unsafe.Add(ref sBase, i);
// packed = [W Z Y X]
// ROTR(8, packedArgb) = [Y Z W X]
Unsafe.Add(ref dBase, i) = BitOperations.RotateRight(packed, 8);
}
}
}
internal readonly struct ZYXWShuffle4 : IShuffle4
{
[MethodImpl(InliningOptions.ShortMethod)]
public void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> destination)
=> HwIntrinsics.Shuffle4Reduce(ref source, ref destination, SimdUtils.Shuffle.MMShuffle3012);
[MethodImpl(InliningOptions.ShortMethod)]
public void Shuffle(ReadOnlySpan<byte> source, Span<byte> destination)
{
ref uint sBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(source));
ref uint dBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(destination));
uint n = (uint)source.Length / 4;
for (nuint i = 0; i < n; i++)
{
uint packed = Unsafe.Add(ref sBase, i);
// packed = [W Z Y X]
// tmp1 = [W 0 Y 0]
// tmp2 = [0 Z 0 X]
// tmp3=ROTL(16, tmp2) = [0 X 0 Z]
// tmp1 + tmp3 = [W X Y Z]
uint tmp1 = packed & 0xFF00FF00;
uint tmp2 = packed & 0x00FF00FF;
uint tmp3 = BitOperations.RotateLeft(tmp2, 16);
Unsafe.Add(ref dBase, i) = tmp1 + tmp3;
}
}
}
internal readonly struct XWZYShuffle4 : IShuffle4
{
[MethodImpl(InliningOptions.ShortMethod)]
public void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> destination)
=> HwIntrinsics.Shuffle4Reduce(ref source, ref destination, SimdUtils.Shuffle.MMShuffle1230);
[MethodImpl(InliningOptions.ShortMethod)]
public void Shuffle(ReadOnlySpan<byte> source, Span<byte> destination)
{
ref uint sBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(source));
ref uint dBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(destination));
uint n = (uint)source.Length / 4;
for (nuint i = 0; i < n; i++)
{
uint packed = Unsafe.Add(ref sBase, i);
// packed = [W Z Y X]
// tmp1 = [0 Z 0 X]
// tmp2 = [W 0 Y 0]
// tmp3=ROTL(16, tmp2) = [Y 0 W 0]
// tmp1 + tmp3 = [Y Z W X]
uint tmp1 = packed & 0x00FF00FF;
uint tmp2 = packed & 0xFF00FF00;
uint tmp3 = BitOperations.RotateLeft(tmp2, 16);
Unsafe.Add(ref dBase, i) = tmp1 + tmp3;
}
}
}

26
src/ImageSharp/Common/Helpers/Shuffle/IPad3Shuffle4.cs

@ -1,6 +1,7 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using static SixLabors.ImageSharp.SimdUtils;
@ -12,22 +13,21 @@ internal interface IPad3Shuffle4 : IComponentShuffle
{
}
internal readonly struct DefaultPad3Shuffle4 : IPad3Shuffle4
internal readonly struct DefaultPad3Shuffle4([ConstantExpected] byte control) : IPad3Shuffle4
{
public DefaultPad3Shuffle4(byte control)
=> this.Control = control;
public byte Control { get; }
public byte Control { get; } = control;
[MethodImpl(InliningOptions.ShortMethod)]
public void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> dest)
=> HwIntrinsics.Pad3Shuffle4Reduce(ref source, ref dest, this.Control);
public void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> destination)
#pragma warning disable CA1857 // A constant is expected for the parameter
=> HwIntrinsics.Pad3Shuffle4Reduce(ref source, ref destination, this.Control);
#pragma warning restore CA1857 // A constant is expected for the parameter
[MethodImpl(InliningOptions.ShortMethod)]
public void Shuffle(ReadOnlySpan<byte> source, Span<byte> dest)
public void Shuffle(ReadOnlySpan<byte> source, Span<byte> destination)
{
ref byte sBase = ref MemoryMarshal.GetReference(source);
ref byte dBase = ref MemoryMarshal.GetReference(dest);
ref byte dBase = ref MemoryMarshal.GetReference(destination);
SimdUtils.Shuffle.InverseMMShuffle(this.Control, out uint p3, out uint p2, out uint p1, out uint p0);
@ -51,14 +51,14 @@ internal readonly struct DefaultPad3Shuffle4 : IPad3Shuffle4
internal readonly struct XYZWPad3Shuffle4 : IPad3Shuffle4
{
[MethodImpl(InliningOptions.ShortMethod)]
public void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> dest)
=> HwIntrinsics.Pad3Shuffle4Reduce(ref source, ref dest, SimdUtils.Shuffle.MMShuffle3210);
public void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> destination)
=> HwIntrinsics.Pad3Shuffle4Reduce(ref source, ref destination, SimdUtils.Shuffle.MMShuffle3210);
[MethodImpl(InliningOptions.ShortMethod)]
public void Shuffle(ReadOnlySpan<byte> source, Span<byte> dest)
public void Shuffle(ReadOnlySpan<byte> source, Span<byte> destination)
{
ref byte sBase = ref MemoryMarshal.GetReference(source);
ref byte dBase = ref MemoryMarshal.GetReference(dest);
ref byte dBase = ref MemoryMarshal.GetReference(destination);
ref byte sEnd = ref Unsafe.Add(ref sBase, (uint)source.Length);
ref byte sLoopEnd = ref Unsafe.Subtract(ref sEnd, 4);

18
src/ImageSharp/Common/Helpers/Shuffle/IShuffle3.cs

@ -1,6 +1,7 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using static SixLabors.ImageSharp.SimdUtils;
@ -12,22 +13,21 @@ internal interface IShuffle3 : IComponentShuffle
{
}
internal readonly struct DefaultShuffle3 : IShuffle3
internal readonly struct DefaultShuffle3([ConstantExpected] byte control) : IShuffle3
{
public DefaultShuffle3(byte control)
=> this.Control = control;
public byte Control { get; }
public byte Control { get; } = control;
[MethodImpl(InliningOptions.ShortMethod)]
public void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> dest)
=> HwIntrinsics.Shuffle3Reduce(ref source, ref dest, this.Control);
public void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> destination)
#pragma warning disable CA1857 // A constant is expected for the parameter
=> HwIntrinsics.Shuffle3Reduce(ref source, ref destination, this.Control);
#pragma warning restore CA1857 // A constant is expected for the parameter
[MethodImpl(InliningOptions.ShortMethod)]
public void Shuffle(ReadOnlySpan<byte> source, Span<byte> dest)
public void Shuffle(ReadOnlySpan<byte> source, Span<byte> destination)
{
ref byte sBase = ref MemoryMarshal.GetReference(source);
ref byte dBase = ref MemoryMarshal.GetReference(dest);
ref byte dBase = ref MemoryMarshal.GetReference(destination);
SimdUtils.Shuffle.InverseMMShuffle(this.Control, out _, out uint p2, out uint p1, out uint p0);

178
src/ImageSharp/Common/Helpers/Shuffle/IShuffle4.cs

@ -0,0 +1,178 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Buffers.Binary;
using System.Diagnostics.CodeAnalysis;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using static SixLabors.ImageSharp.SimdUtils;
namespace SixLabors.ImageSharp;
/// <inheritdoc/>
internal interface IShuffle4 : IComponentShuffle
{
}
internal readonly struct DefaultShuffle4([ConstantExpected] byte control) : IShuffle4
{
public byte Control { get; } = control;
[MethodImpl(InliningOptions.ShortMethod)]
public void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> destination)
#pragma warning disable CA1857 // A constant is expected for the parameter
=> HwIntrinsics.Shuffle4Reduce(ref source, ref destination, this.Control);
#pragma warning restore CA1857 // A constant is expected for the parameter
[MethodImpl(InliningOptions.ShortMethod)]
public void Shuffle(ReadOnlySpan<byte> source, Span<byte> destination)
{
ref byte sBase = ref MemoryMarshal.GetReference(source);
ref byte dBase = ref MemoryMarshal.GetReference(destination);
SimdUtils.Shuffle.InverseMMShuffle(this.Control, out uint p3, out uint p2, out uint p1, out uint p0);
for (nuint i = 0; i < (uint)source.Length; i += 4)
{
Unsafe.Add(ref dBase, i + 0) = Unsafe.Add(ref sBase, p0 + i);
Unsafe.Add(ref dBase, i + 1) = Unsafe.Add(ref sBase, p1 + i);
Unsafe.Add(ref dBase, i + 2) = Unsafe.Add(ref sBase, p2 + i);
Unsafe.Add(ref dBase, i + 3) = Unsafe.Add(ref sBase, p3 + i);
}
}
}
internal readonly struct WXYZShuffle4 : IShuffle4
{
[MethodImpl(InliningOptions.ShortMethod)]
public void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> destination)
=> HwIntrinsics.Shuffle4Reduce(ref source, ref destination, SimdUtils.Shuffle.MMShuffle2103);
[MethodImpl(InliningOptions.ShortMethod)]
public void Shuffle(ReadOnlySpan<byte> source, Span<byte> destination)
{
ref uint sBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(source));
ref uint dBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(destination));
uint n = (uint)source.Length / 4;
for (nuint i = 0; i < n; i++)
{
uint packed = Unsafe.Add(ref sBase, i);
// packed = [W Z Y X]
// ROTL(8, packed) = [Z Y X W]
Unsafe.Add(ref dBase, i) = (packed << 8) | (packed >> 24);
}
}
}
internal readonly struct WZYXShuffle4 : IShuffle4
{
[MethodImpl(InliningOptions.ShortMethod)]
public void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> destination)
=> HwIntrinsics.Shuffle4Reduce(ref source, ref destination, SimdUtils.Shuffle.MMShuffle0123);
[MethodImpl(InliningOptions.ShortMethod)]
public void Shuffle(ReadOnlySpan<byte> source, Span<byte> destination)
{
ref uint sBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(source));
ref uint dBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(destination));
uint n = (uint)source.Length / 4;
for (nuint i = 0; i < n; i++)
{
uint packed = Unsafe.Add(ref sBase, i);
// packed = [W Z Y X]
// REVERSE(packedArgb) = [X Y Z W]
Unsafe.Add(ref dBase, i) = BinaryPrimitives.ReverseEndianness(packed);
}
}
}
internal readonly struct YZWXShuffle4 : IShuffle4
{
[MethodImpl(InliningOptions.ShortMethod)]
public void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> destination)
=> HwIntrinsics.Shuffle4Reduce(ref source, ref destination, SimdUtils.Shuffle.MMShuffle0321);
[MethodImpl(InliningOptions.ShortMethod)]
public void Shuffle(ReadOnlySpan<byte> source, Span<byte> destination)
{
ref uint sBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(source));
ref uint dBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(destination));
uint n = (uint)source.Length / 4;
for (nuint i = 0; i < n; i++)
{
uint packed = Unsafe.Add(ref sBase, i);
// packed = [W Z Y X]
// ROTR(8, packedArgb) = [Y Z W X]
Unsafe.Add(ref dBase, i) = BitOperations.RotateRight(packed, 8);
}
}
}
internal readonly struct ZYXWShuffle4 : IShuffle4
{
[MethodImpl(InliningOptions.ShortMethod)]
public void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> destination)
=> HwIntrinsics.Shuffle4Reduce(ref source, ref destination, SimdUtils.Shuffle.MMShuffle3012);
[MethodImpl(InliningOptions.ShortMethod)]
public void Shuffle(ReadOnlySpan<byte> source, Span<byte> destination)
{
ref uint sBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(source));
ref uint dBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(destination));
uint n = (uint)source.Length / 4;
for (nuint i = 0; i < n; i++)
{
uint packed = Unsafe.Add(ref sBase, i);
// packed = [W Z Y X]
// tmp1 = [W 0 Y 0]
// tmp2 = [0 Z 0 X]
// tmp3=ROTL(16, tmp2) = [0 X 0 Z]
// tmp1 + tmp3 = [W X Y Z]
uint tmp1 = packed & 0xFF00FF00;
uint tmp2 = packed & 0x00FF00FF;
uint tmp3 = BitOperations.RotateLeft(tmp2, 16);
Unsafe.Add(ref dBase, i) = tmp1 + tmp3;
}
}
}
internal readonly struct XWZYShuffle4 : IShuffle4
{
[MethodImpl(InliningOptions.ShortMethod)]
public void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> destination)
=> HwIntrinsics.Shuffle4Reduce(ref source, ref destination, SimdUtils.Shuffle.MMShuffle1230);
[MethodImpl(InliningOptions.ShortMethod)]
public void Shuffle(ReadOnlySpan<byte> source, Span<byte> destination)
{
ref uint sBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(source));
ref uint dBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(destination));
uint n = (uint)source.Length / 4;
for (nuint i = 0; i < n; i++)
{
uint packed = Unsafe.Add(ref sBase, i);
// packed = [W Z Y X]
// tmp1 = [0 Z 0 X]
// tmp2 = [W 0 Y 0]
// tmp3=ROTL(16, tmp2) = [Y 0 W 0]
// tmp1 + tmp3 = [Y Z W X]
uint tmp1 = packed & 0x00FF00FF;
uint tmp2 = packed & 0xFF00FF00;
uint tmp3 = BitOperations.RotateLeft(tmp2, 16);
Unsafe.Add(ref dBase, i) = tmp1 + tmp3;
}
}
}

28
src/ImageSharp/Common/Helpers/Shuffle/IShuffle4Slice3.cs

@ -1,6 +1,7 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using static SixLabors.ImageSharp.SimdUtils;
@ -12,26 +13,25 @@ internal interface IShuffle4Slice3 : IComponentShuffle
{
}
internal readonly struct DefaultShuffle4Slice3 : IShuffle4Slice3
internal readonly struct DefaultShuffle4Slice3([ConstantExpected] byte control) : IShuffle4Slice3
{
public DefaultShuffle4Slice3(byte control)
=> this.Control = control;
public byte Control { get; }
public byte Control { get; } = control;
[MethodImpl(InliningOptions.ShortMethod)]
public void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> dest)
=> HwIntrinsics.Shuffle4Slice3Reduce(ref source, ref dest, this.Control);
public void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> destination)
#pragma warning disable CA1857 // A constant is expected for the parameter
=> HwIntrinsics.Shuffle4Slice3Reduce(ref source, ref destination, this.Control);
#pragma warning restore CA1857 // A constant is expected for the parameter
[MethodImpl(InliningOptions.ShortMethod)]
public void Shuffle(ReadOnlySpan<byte> source, Span<byte> dest)
public void Shuffle(ReadOnlySpan<byte> source, Span<byte> destination)
{
ref byte sBase = ref MemoryMarshal.GetReference(source);
ref byte dBase = ref MemoryMarshal.GetReference(dest);
ref byte dBase = ref MemoryMarshal.GetReference(destination);
SimdUtils.Shuffle.InverseMMShuffle(this.Control, out _, out uint p2, out uint p1, out uint p0);
for (nuint i = 0, j = 0; i < (uint)dest.Length; i += 3, j += 4)
for (nuint i = 0, j = 0; i < (uint)destination.Length; i += 3, j += 4)
{
Unsafe.Add(ref dBase, i + 0) = Unsafe.Add(ref sBase, p0 + j);
Unsafe.Add(ref dBase, i + 1) = Unsafe.Add(ref sBase, p1 + j);
@ -43,14 +43,14 @@ internal readonly struct DefaultShuffle4Slice3 : IShuffle4Slice3
internal readonly struct XYZWShuffle4Slice3 : IShuffle4Slice3
{
[MethodImpl(InliningOptions.ShortMethod)]
public void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> dest)
=> HwIntrinsics.Shuffle4Slice3Reduce(ref source, ref dest, SimdUtils.Shuffle.MMShuffle3210);
public void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> destination)
=> HwIntrinsics.Shuffle4Slice3Reduce(ref source, ref destination, SimdUtils.Shuffle.MMShuffle3210);
[MethodImpl(InliningOptions.ShortMethod)]
public void Shuffle(ReadOnlySpan<byte> source, Span<byte> dest)
public void Shuffle(ReadOnlySpan<byte> source, Span<byte> destination)
{
ref uint sBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(source));
ref Byte3 dBase = ref Unsafe.As<byte, Byte3>(ref MemoryMarshal.GetReference(dest));
ref Byte3 dBase = ref Unsafe.As<byte, Byte3>(ref MemoryMarshal.GetReference(destination));
nint n = (nint)(uint)source.Length / 4;
nint m = Numerics.Modulo4(n);

230
src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs

@ -63,7 +63,9 @@ internal static partial class SimdUtils
ref Span<float> destination,
[ConstantExpected] byte control)
{
if (Vector512.IsHardwareAccelerated || Vector256.IsHardwareAccelerated || Vector128.IsHardwareAccelerated)
if ((Vector512.IsHardwareAccelerated && Vector512Utilities.SupportsShuffleFloat) ||
(Vector256.IsHardwareAccelerated && Vector256Utilities.SupportsShuffleFloat) ||
(Vector128.IsHardwareAccelerated && Vector128Utilities.SupportsShuffleFloat))
{
int remainder = 0;
if (Vector512.IsHardwareAccelerated)
@ -105,9 +107,11 @@ internal static partial class SimdUtils
public static void Shuffle4Reduce(
ref ReadOnlySpan<byte> source,
ref Span<byte> destination,
byte control)
[ConstantExpected] byte control)
{
if (Vector512.IsHardwareAccelerated || Vector256.IsHardwareAccelerated || Vector128.IsHardwareAccelerated)
if ((Vector512.IsHardwareAccelerated && Vector512Utilities.SupportsShuffleByte) ||
(Vector256.IsHardwareAccelerated && Vector256Utilities.SupportsShuffleByte) ||
(Vector128.IsHardwareAccelerated && Vector128Utilities.SupportsShuffleByte))
{
int remainder = 0;
if (Vector512.IsHardwareAccelerated)
@ -139,7 +143,7 @@ internal static partial class SimdUtils
}
/// <summary>
/// Shuffles 8-bit integer triplets within 128-bit lanes in <paramref name="source"/>
/// Shuffles 8-bit integer triplets in <paramref name="source"/>
/// using the control and store the results in <paramref name="destination"/>.
/// </summary>
/// <param name="source">The source span of bytes.</param>
@ -149,9 +153,9 @@ internal static partial class SimdUtils
public static void Shuffle3Reduce(
ref ReadOnlySpan<byte> source,
ref Span<byte> destination,
byte control)
[ConstantExpected] byte control)
{
if (Vector128.IsHardwareAccelerated && Vector128Utilities.SupportsRightShift)
if (Vector128.IsHardwareAccelerated && Vector128Utilities.SupportsShuffleByte && Vector128Utilities.SupportsRightAlign)
{
int remainder = source.Length % (Vector128<byte>.Count * 3);
@ -171,67 +175,67 @@ internal static partial class SimdUtils
}
/// <summary>
/// Pads then shuffles 8-bit integers within 128-bit lanes in <paramref name="source"/>
/// using the control and store the results in <paramref name="dest"/>.
/// Pads then shuffles 8-bit integers in <paramref name="source"/>
/// using the control and store the results in <paramref name="destination"/>.
/// </summary>
/// <param name="source">The source span of bytes.</param>
/// <param name="dest">The destination span of bytes.</param>
/// <param name="destination">The destination span of bytes.</param>
/// <param name="control">The byte control.</param>
[MethodImpl(InliningOptions.ShortMethod)]
public static void Pad3Shuffle4Reduce(
ref ReadOnlySpan<byte> source,
ref Span<byte> dest,
byte control)
ref Span<byte> destination,
[ConstantExpected] byte control)
{
if (Ssse3.IsSupported)
if (Vector128.IsHardwareAccelerated && Vector128Utilities.SupportsShuffleByte && Vector128Utilities.SupportsShiftByte)
{
int remainder = source.Length % (Vector128<byte>.Count * 3);
int sourceCount = source.Length - remainder;
int destCount = (int)((uint)sourceCount * 4 / 3);
int destinationCount = (int)((uint)sourceCount * 4 / 3);
if (sourceCount > 0)
{
Pad3Shuffle4(
source[..sourceCount],
dest[..destCount],
destination[..destinationCount],
control);
source = source[sourceCount..];
dest = dest[destCount..];
destination = destination[destinationCount..];
}
}
}
/// <summary>
/// Shuffles then slices 8-bit integers within 128-bit lanes in <paramref name="source"/>
/// using the control and store the results in <paramref name="dest"/>.
/// Shuffles then slices 8-bit integers in <paramref name="source"/>
/// using the control and store the results in <paramref name="destination"/>.
/// </summary>
/// <param name="source">The source span of bytes.</param>
/// <param name="dest">The destination span of bytes.</param>
/// <param name="destination">The destination span of bytes.</param>
/// <param name="control">The byte control.</param>
[MethodImpl(InliningOptions.ShortMethod)]
public static void Shuffle4Slice3Reduce(
ref ReadOnlySpan<byte> source,
ref Span<byte> dest,
byte control)
ref Span<byte> destination,
[ConstantExpected] byte control)
{
if (Ssse3.IsSupported)
if (Vector128.IsHardwareAccelerated && Vector128Utilities.SupportsShuffleByte && Vector128Utilities.SupportsShiftByte)
{
int remainder = source.Length & ((Vector128<byte>.Count * 4) - 1); // bit-hack for modulo
int sourceCount = source.Length - remainder;
int destCount = (int)((uint)sourceCount * 3 / 4);
int destinationCount = (int)((uint)sourceCount * 3 / 4);
if (sourceCount > 0)
{
Shuffle4Slice3(
source[..sourceCount],
dest[..destCount],
destination[..destinationCount],
control);
source = source[sourceCount..];
dest = dest[destCount..];
destination = destination[destinationCount..];
}
}
}
@ -242,12 +246,8 @@ internal static partial class SimdUtils
Span<float> destination,
[ConstantExpected] byte control)
{
if (Vector512.IsHardwareAccelerated)
if (Vector512.IsHardwareAccelerated && Vector512Utilities.SupportsShuffleFloat)
{
Span<int> temp = stackalloc int[Vector512<int>.Count];
Shuffle.MMShuffleSpan(ref temp, control);
Vector512<int> mask = Unsafe.As<int, Vector512<int>>(ref MemoryMarshal.GetReference(temp));
ref Vector512<float> sourceBase = ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(source));
ref Vector512<float> destinationBase = ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(destination));
@ -260,26 +260,22 @@ internal static partial class SimdUtils
ref Vector512<float> vs0 = ref Unsafe.Add(ref sourceBase, i);
ref Vector512<float> vd0 = ref Unsafe.Add(ref destinationBase, i);
vd0 = Vector512.Shuffle(vs0, mask);
Unsafe.Add(ref vd0, (nuint)1) = Vector512.Shuffle(Unsafe.Add(ref vs0, (nuint)1), mask);
Unsafe.Add(ref vd0, (nuint)2) = Vector512.Shuffle(Unsafe.Add(ref vs0, (nuint)2), mask);
Unsafe.Add(ref vd0, (nuint)3) = Vector512.Shuffle(Unsafe.Add(ref vs0, (nuint)3), mask);
vd0 = Vector512Utilities.Shuffle(vs0, control);
Unsafe.Add(ref vd0, (nuint)1) = Vector512Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)1), control);
Unsafe.Add(ref vd0, (nuint)2) = Vector512Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)2), control);
Unsafe.Add(ref vd0, (nuint)3) = Vector512Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)3), control);
}
if (m > 0)
{
for (nuint i = u; i < n; i++)
{
Unsafe.Add(ref destinationBase, i) = Vector512.Shuffle(Unsafe.Add(ref sourceBase, i), mask);
Unsafe.Add(ref destinationBase, i) = Vector512Utilities.Shuffle(Unsafe.Add(ref sourceBase, i), control);
}
}
}
else if (Vector256.IsHardwareAccelerated)
else if (Vector256.IsHardwareAccelerated && Vector256Utilities.SupportsShuffleFloat)
{
Span<int> temp = stackalloc int[Vector256<int>.Count];
Shuffle.MMShuffleSpan(ref temp, control);
Vector256<int> mask = Unsafe.As<int, Vector256<int>>(ref MemoryMarshal.GetReference(temp));
ref Vector256<float> sourceBase = ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(source));
ref Vector256<float> destinationBase = ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(destination));
@ -292,26 +288,22 @@ internal static partial class SimdUtils
ref Vector256<float> vs0 = ref Unsafe.Add(ref sourceBase, i);
ref Vector256<float> vd0 = ref Unsafe.Add(ref destinationBase, i);
vd0 = Vector256.Shuffle(vs0, mask);
Unsafe.Add(ref vd0, (nuint)1) = Vector256.Shuffle(Unsafe.Add(ref vs0, (nuint)1), mask);
Unsafe.Add(ref vd0, (nuint)2) = Vector256.Shuffle(Unsafe.Add(ref vs0, (nuint)2), mask);
Unsafe.Add(ref vd0, (nuint)3) = Vector256.Shuffle(Unsafe.Add(ref vs0, (nuint)3), mask);
vd0 = Vector256Utilities.Shuffle(vs0, control);
Unsafe.Add(ref vd0, (nuint)1) = Vector256Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)1), control);
Unsafe.Add(ref vd0, (nuint)2) = Vector256Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)2), control);
Unsafe.Add(ref vd0, (nuint)3) = Vector256Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)3), control);
}
if (m > 0)
{
for (nuint i = u; i < n; i++)
{
Unsafe.Add(ref destinationBase, i) = Vector256.Shuffle(Unsafe.Add(ref sourceBase, i), mask);
Unsafe.Add(ref destinationBase, i) = Vector256Utilities.Shuffle(Unsafe.Add(ref sourceBase, i), control);
}
}
}
else if (Vector128.IsHardwareAccelerated)
else if (Vector128.IsHardwareAccelerated && Vector128Utilities.SupportsShuffleFloat)
{
Span<int> temp = stackalloc int[Vector128<int>.Count];
Shuffle.MMShuffleSpan(ref temp, control);
Vector128<int> mask = Unsafe.As<int, Vector128<int>>(ref MemoryMarshal.GetReference(temp));
ref Vector128<float> sourceBase = ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(source));
ref Vector128<float> destinationBase = ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(destination));
@ -324,17 +316,17 @@ internal static partial class SimdUtils
ref Vector128<float> vs0 = ref Unsafe.Add(ref sourceBase, i);
ref Vector128<float> vd0 = ref Unsafe.Add(ref destinationBase, i);
vd0 = Vector128.Shuffle(vs0, mask);
Unsafe.Add(ref vd0, (nuint)1) = Vector128.Shuffle(Unsafe.Add(ref vs0, (nuint)1), mask);
Unsafe.Add(ref vd0, (nuint)2) = Vector128.Shuffle(Unsafe.Add(ref vs0, (nuint)2), mask);
Unsafe.Add(ref vd0, (nuint)3) = Vector128.Shuffle(Unsafe.Add(ref vs0, (nuint)3), mask);
vd0 = Vector128Utilities.Shuffle(vs0, control);
Unsafe.Add(ref vd0, (nuint)1) = Vector128Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)1), control);
Unsafe.Add(ref vd0, (nuint)2) = Vector128Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)2), control);
Unsafe.Add(ref vd0, (nuint)3) = Vector128Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)3), control);
}
if (m > 0)
{
for (nuint i = u; i < n; i++)
{
Unsafe.Add(ref destinationBase, i) = Vector128.Shuffle(Unsafe.Add(ref sourceBase, i), mask);
Unsafe.Add(ref destinationBase, i) = Vector128Utilities.Shuffle(Unsafe.Add(ref sourceBase, i), control);
}
}
}
@ -344,9 +336,9 @@ internal static partial class SimdUtils
private static void Shuffle4(
ReadOnlySpan<byte> source,
Span<byte> destination,
byte control)
[ConstantExpected] byte control)
{
if (Vector512.IsHardwareAccelerated)
if (Vector512.IsHardwareAccelerated && Vector512Utilities.SupportsShuffleByte)
{
Span<byte> temp = stackalloc byte[Vector512<byte>.Count];
Shuffle.MMShuffleSpan(ref temp, control);
@ -364,21 +356,21 @@ internal static partial class SimdUtils
ref Vector512<byte> vs0 = ref Unsafe.Add(ref sourceBase, i);
ref Vector512<byte> vd0 = ref Unsafe.Add(ref destinationBase, i);
vd0 = Vector512.Shuffle(vs0, mask);
Unsafe.Add(ref vd0, (nuint)1) = Vector512.Shuffle(Unsafe.Add(ref vs0, (nuint)1), mask);
Unsafe.Add(ref vd0, (nuint)2) = Vector512.Shuffle(Unsafe.Add(ref vs0, (nuint)2), mask);
Unsafe.Add(ref vd0, (nuint)3) = Vector512.Shuffle(Unsafe.Add(ref vs0, (nuint)3), mask);
vd0 = Vector512Utilities.Shuffle(vs0, mask);
Unsafe.Add(ref vd0, (nuint)1) = Vector512Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)1), mask);
Unsafe.Add(ref vd0, (nuint)2) = Vector512Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)2), mask);
Unsafe.Add(ref vd0, (nuint)3) = Vector512Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)3), mask);
}
if (m > 0)
{
for (nuint i = u; i < n; i++)
{
Unsafe.Add(ref destinationBase, i) = Vector512.Shuffle(Unsafe.Add(ref sourceBase, i), mask);
Unsafe.Add(ref destinationBase, i) = Vector512Utilities.Shuffle(Unsafe.Add(ref sourceBase, i), mask);
}
}
}
else if (Vector256.IsHardwareAccelerated)
else if (Vector256.IsHardwareAccelerated && Vector256Utilities.SupportsShuffleByte)
{
Span<byte> temp = stackalloc byte[Vector256<byte>.Count];
Shuffle.MMShuffleSpan(ref temp, control);
@ -396,21 +388,21 @@ internal static partial class SimdUtils
ref Vector256<byte> vs0 = ref Unsafe.Add(ref sourceBase, i);
ref Vector256<byte> vd0 = ref Unsafe.Add(ref destinationBase, i);
vd0 = Vector256.Shuffle(vs0, mask);
Unsafe.Add(ref vd0, (nuint)1) = Vector256.Shuffle(Unsafe.Add(ref vs0, (nuint)1), mask);
Unsafe.Add(ref vd0, (nuint)2) = Vector256.Shuffle(Unsafe.Add(ref vs0, (nuint)2), mask);
Unsafe.Add(ref vd0, (nuint)3) = Vector256.Shuffle(Unsafe.Add(ref vs0, (nuint)3), mask);
vd0 = Vector256Utilities.Shuffle(vs0, mask);
Unsafe.Add(ref vd0, (nuint)1) = Vector256Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)1), mask);
Unsafe.Add(ref vd0, (nuint)2) = Vector256Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)2), mask);
Unsafe.Add(ref vd0, (nuint)3) = Vector256Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)3), mask);
}
if (m > 0)
{
for (nuint i = u; i < n; i++)
{
Unsafe.Add(ref destinationBase, i) = Vector256.Shuffle(Unsafe.Add(ref sourceBase, i), mask);
Unsafe.Add(ref destinationBase, i) = Vector256Utilities.Shuffle(Unsafe.Add(ref sourceBase, i), mask);
}
}
}
else if (Vector128.IsHardwareAccelerated)
else if (Vector128.IsHardwareAccelerated && Vector128Utilities.SupportsShuffleByte)
{
Span<byte> temp = stackalloc byte[Vector128<byte>.Count];
Shuffle.MMShuffleSpan(ref temp, control);
@ -428,17 +420,17 @@ internal static partial class SimdUtils
ref Vector128<byte> vs0 = ref Unsafe.Add(ref sourceBase, i);
ref Vector128<byte> vd0 = ref Unsafe.Add(ref destinationBase, i);
vd0 = Vector128.Shuffle(vs0, mask);
Unsafe.Add(ref vd0, (nuint)1) = Vector128.Shuffle(Unsafe.Add(ref vs0, (nuint)1), mask);
Unsafe.Add(ref vd0, (nuint)2) = Vector128.Shuffle(Unsafe.Add(ref vs0, (nuint)2), mask);
Unsafe.Add(ref vd0, (nuint)3) = Vector128.Shuffle(Unsafe.Add(ref vs0, (nuint)3), mask);
vd0 = Vector128Utilities.Shuffle(vs0, mask);
Unsafe.Add(ref vd0, (nuint)1) = Vector128Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)1), mask);
Unsafe.Add(ref vd0, (nuint)2) = Vector128Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)2), mask);
Unsafe.Add(ref vd0, (nuint)3) = Vector128Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)3), mask);
}
if (m > 0)
{
for (nuint i = u; i < n; i++)
{
Unsafe.Add(ref destinationBase, i) = Vector128.Shuffle(Unsafe.Add(ref sourceBase, i), mask);
Unsafe.Add(ref destinationBase, i) = Vector128Utilities.Shuffle(Unsafe.Add(ref sourceBase, i), mask);
}
}
}
@ -448,9 +440,9 @@ internal static partial class SimdUtils
private static void Shuffle3(
ReadOnlySpan<byte> source,
Span<byte> destination,
byte control)
[ConstantExpected] byte control)
{
if (Vector128.IsHardwareAccelerated && Vector128Utilities.SupportsRightShift)
if (Vector128.IsHardwareAccelerated && Vector128Utilities.SupportsShuffleByte && Vector128Utilities.SupportsRightAlign)
{
Vector128<byte> maskPad4Nx16 = ShuffleMaskPad4Nx16();
Vector128<byte> maskSlice4Nx16 = ShuffleMaskSlice4Nx16();
@ -477,15 +469,15 @@ internal static partial class SimdUtils
v2 = Vector128Utilities.AlignRight(v2, v1, 8);
v1 = Vector128Utilities.AlignRight(v1, v0, 12);
v0 = Vector128.Shuffle(Vector128.Shuffle(v0, maskPad4Nx16), mask);
v1 = Vector128.Shuffle(Vector128.Shuffle(v1, maskPad4Nx16), mask);
v2 = Vector128.Shuffle(Vector128.Shuffle(v2, maskPad4Nx16), mask);
v3 = Vector128.Shuffle(Vector128.Shuffle(v3, maskPad4Nx16), mask);
v0 = Vector128Utilities.Shuffle(Vector128Utilities.Shuffle(v0, maskPad4Nx16), mask);
v1 = Vector128Utilities.Shuffle(Vector128Utilities.Shuffle(v1, maskPad4Nx16), mask);
v2 = Vector128Utilities.Shuffle(Vector128Utilities.Shuffle(v2, maskPad4Nx16), mask);
v3 = Vector128Utilities.Shuffle(Vector128Utilities.Shuffle(v3, maskPad4Nx16), mask);
v0 = Vector128.Shuffle(v0, maskE);
v1 = Vector128.Shuffle(v1, maskSlice4Nx16);
v2 = Vector128.Shuffle(v2, maskE);
v3 = Vector128.Shuffle(v3, maskSlice4Nx16);
v0 = Vector128Utilities.Shuffle(v0, maskE);
v1 = Vector128Utilities.Shuffle(v1, maskSlice4Nx16);
v2 = Vector128Utilities.Shuffle(v2, maskE);
v3 = Vector128Utilities.Shuffle(v3, maskSlice4Nx16);
v0 = Vector128Utilities.AlignRight(v1, v0, 4);
v3 = Vector128Utilities.AlignRight(v3, v2, 12);
@ -508,21 +500,21 @@ internal static partial class SimdUtils
private static void Pad3Shuffle4(
ReadOnlySpan<byte> source,
Span<byte> destination,
byte control)
[ConstantExpected] byte control)
{
if (Ssse3.IsSupported)
if (Vector128.IsHardwareAccelerated && Vector128Utilities.SupportsShuffleByte && Vector128Utilities.SupportsShiftByte)
{
Vector128<byte> vmask = ShuffleMaskPad4Nx16();
Vector128<byte> vfill = Vector128.Create(0xff000000ff000000ul).AsByte();
Vector128<byte> maskPad4Nx16 = ShuffleMaskPad4Nx16();
Vector128<byte> fill = Vector128.Create(0xff000000ff000000ul).AsByte();
Span<byte> bytes = stackalloc byte[Vector128<byte>.Count];
Shuffle.MMShuffleSpan(ref bytes, control);
Vector128<byte> vshuffle = Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(bytes));
Span<byte> temp = stackalloc byte[Vector128<byte>.Count];
Shuffle.MMShuffleSpan(ref temp, control);
Vector128<byte> mask = Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(temp));
ref Vector128<byte> sourceBase =
ref Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(source));
ref Vector128<byte> destBase =
ref Vector128<byte> destinationBase =
ref Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(destination));
nuint n = source.Vector128Count<byte>();
@ -532,17 +524,17 @@ internal static partial class SimdUtils
ref Vector128<byte> v0 = ref Unsafe.Add(ref sourceBase, i);
Vector128<byte> v1 = Unsafe.Add(ref v0, 1);
Vector128<byte> v2 = Unsafe.Add(ref v0, 2);
Vector128<byte> v3 = Sse2.ShiftRightLogical128BitLane(v2, 4);
Vector128<byte> v3 = Vector128Utilities.ShiftRightBytesInVector(v2, 4);
v2 = Ssse3.AlignRight(v2, v1, 8);
v1 = Ssse3.AlignRight(v1, v0, 12);
v2 = Vector128Utilities.AlignRight(v2, v1, 8);
v1 = Vector128Utilities.AlignRight(v1, v0, 12);
ref Vector128<byte> vd = ref Unsafe.Add(ref destBase, j);
ref Vector128<byte> vd = ref Unsafe.Add(ref destinationBase, j);
vd = Ssse3.Shuffle(Sse2.Or(Ssse3.Shuffle(v0, vmask), vfill), vshuffle);
Unsafe.Add(ref vd, 1) = Ssse3.Shuffle(Sse2.Or(Ssse3.Shuffle(v1, vmask), vfill), vshuffle);
Unsafe.Add(ref vd, 2) = Ssse3.Shuffle(Sse2.Or(Ssse3.Shuffle(v2, vmask), vfill), vshuffle);
Unsafe.Add(ref vd, 3) = Ssse3.Shuffle(Sse2.Or(Ssse3.Shuffle(v3, vmask), vfill), vshuffle);
vd = Vector128Utilities.Shuffle(Vector128Utilities.Shuffle(v0, maskPad4Nx16) | fill, mask);
Unsafe.Add(ref vd, 1) = Vector128Utilities.Shuffle(Vector128Utilities.Shuffle(v1, maskPad4Nx16) | fill, mask);
Unsafe.Add(ref vd, 2) = Vector128Utilities.Shuffle(Vector128Utilities.Shuffle(v2, maskPad4Nx16) | fill, mask);
Unsafe.Add(ref vd, 3) = Vector128Utilities.Shuffle(Vector128Utilities.Shuffle(v3, maskPad4Nx16) | fill, mask);
}
}
}
@ -550,23 +542,23 @@ internal static partial class SimdUtils
[MethodImpl(InliningOptions.ShortMethod)]
private static void Shuffle4Slice3(
ReadOnlySpan<byte> source,
Span<byte> dest,
byte control)
Span<byte> destination,
[ConstantExpected] byte control)
{
if (Ssse3.IsSupported)
if (Vector128.IsHardwareAccelerated && Vector128Utilities.SupportsShuffleByte && Vector128Utilities.SupportsShiftByte)
{
Vector128<byte> vmasko = ShuffleMaskSlice4Nx16();
Vector128<byte> vmaske = Ssse3.AlignRight(vmasko, vmasko, 12);
Vector128<byte> maskSlice4Nx16 = ShuffleMaskSlice4Nx16();
Vector128<byte> maskE = Ssse3.AlignRight(maskSlice4Nx16, maskSlice4Nx16, 12);
Span<byte> bytes = stackalloc byte[Vector128<byte>.Count];
Shuffle.MMShuffleSpan(ref bytes, control);
Vector128<byte> vshuffle = Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(bytes));
Span<byte> temp = stackalloc byte[Vector128<byte>.Count];
Shuffle.MMShuffleSpan(ref temp, control);
Vector128<byte> mask = Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(temp));
ref Vector128<byte> sourceBase =
ref Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(source));
ref Vector128<byte> destBase =
ref Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(dest));
ref Vector128<byte> destinationBase =
ref Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(destination));
nuint n = source.Vector128Count<byte>();
@ -579,20 +571,20 @@ internal static partial class SimdUtils
Vector128<byte> v2 = Unsafe.Add(ref vs, 2);
Vector128<byte> v3 = Unsafe.Add(ref vs, 3);
v0 = Ssse3.Shuffle(Ssse3.Shuffle(v0, vshuffle), vmaske);
v1 = Ssse3.Shuffle(Ssse3.Shuffle(v1, vshuffle), vmasko);
v2 = Ssse3.Shuffle(Ssse3.Shuffle(v2, vshuffle), vmaske);
v3 = Ssse3.Shuffle(Ssse3.Shuffle(v3, vshuffle), vmasko);
v0 = Vector128Utilities.Shuffle(Vector128Utilities.Shuffle(v0, mask), maskE);
v1 = Vector128Utilities.Shuffle(Vector128Utilities.Shuffle(v1, mask), maskSlice4Nx16);
v2 = Vector128Utilities.Shuffle(Vector128Utilities.Shuffle(v2, mask), maskE);
v3 = Vector128Utilities.Shuffle(Vector128Utilities.Shuffle(v3, mask), maskSlice4Nx16);
v0 = Ssse3.AlignRight(v1, v0, 4);
v3 = Ssse3.AlignRight(v3, v2, 12);
v0 = Vector128Utilities.AlignRight(v1, v0, 4);
v3 = Vector128Utilities.AlignRight(v3, v2, 12);
v1 = Sse2.ShiftLeftLogical128BitLane(v1, 4);
v2 = Sse2.ShiftRightLogical128BitLane(v2, 4);
v1 = Vector128Utilities.ShiftLeftBytesInVector(v1, 4);
v2 = Vector128Utilities.ShiftRightBytesInVector(v2, 4);
v1 = Ssse3.AlignRight(v2, v1, 8);
v1 = Vector128Utilities.AlignRight(v2, v1, 8);
ref Vector128<byte> vd = ref Unsafe.Add(ref destBase, j);
ref Vector128<byte> vd = ref Unsafe.Add(ref destinationBase, j);
vd = v0;
Unsafe.Add(ref vd, 1) = v1;

83
src/ImageSharp/Common/Helpers/Vector128Utilities.cs

@ -11,20 +11,95 @@ using System.Runtime.Intrinsics.X86;
namespace SixLabors.ImageSharp.Common.Helpers;
/// <summary>
/// Defines utility methods for <see cref="Vector128{T}"/> that have not yet been normalized in the runtime.
/// Defines utility methods for <see cref="Vector128{T}"/> that have either:
/// <list type="number">
/// <item>Not yet been normalized in the runtime.</item>
/// <item>Produce codegen that is poorly optimized by the runtime.</item>
/// </list>
/// Should only be used if the intrinsics are available.
/// </summary>
internal static class Vector128Utilities
{
/// <summary>
/// Gets a value indicating whether right shift operations are supported.
/// Gets a value indicating whether shuffle operations are supported.
/// </summary>
public static bool SupportsRightShift
public static bool SupportsShuffleFloat
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => Sse.IsSupported;
}
/// <summary>
/// Gets a value indicating whether shuffle operations are supported.
/// </summary>
public static bool SupportsShuffleByte
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => Ssse3.IsSupported || AdvSimd.Arm64.IsSupported;
}
/// <summary>
/// Gets a value indicating whether right align operations are supported.
/// </summary>
public static bool SupportsRightAlign
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => Ssse3.IsSupported || AdvSimd.IsSupported;
}
/// <summary>
/// Gets a value indicating whether right or left byte shift operations are supported.
/// </summary>
public static bool SupportsShiftByte
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => Sse2.IsSupported || AdvSimd.IsSupported;
}
/// <summary>
/// Creates a new vector by selecting values from an input vector using the control.
/// </summary>
/// <param name="vector">The input vector from which values are selected.</param>
/// <param name="control">The shuffle control byte.</param>
/// <returns>The <see cref="Vector128{Single}"/>.</returns>
public static Vector128<float> Shuffle(Vector128<float> vector, [ConstantExpected] byte control)
{
if (Sse.IsSupported)
{
return Sse.Shuffle(vector, vector, control);
}
ThrowUnreachableException();
return default;
}
/// <summary>
/// Creates a new vector by selecting values from an input vector using a set of indices.
/// </summary>
/// <param name="vector">
/// The input vector from which values are selected.</param>
/// <param name="indices">
/// The per-element indices used to select a value from <paramref name="vector" />.
/// </param>
/// <returns>
/// A new vector containing the values from <paramref name="vector" /> selected by the given <paramref name="indices" />.
/// </returns>
public static Vector128<byte> Shuffle(Vector128<byte> vector, Vector128<byte> indices)
{
if (Ssse3.IsSupported)
{
return Ssse3.Shuffle(vector, indices);
}
if (AdvSimd.Arm64.IsSupported)
{
return AdvSimd.Arm64.VectorTableLookup(vector, indices);
}
ThrowUnreachableException();
return default;
}
/// <summary>
/// Shifts a 128-bit value right by a specified number of bytes while shifting in zeros.
/// </summary>
@ -80,7 +155,7 @@ internal static class Vector128Utilities
/// <returns>The <see cref="Vector128{Byte}"/>.</returns>
public static Vector128<byte> AlignRight(Vector128<byte> left, Vector128<byte> right, [ConstantExpected(Max = (byte)15)] byte mask)
{
if (Sse3.IsSupported)
if (Ssse3.IsSupported)
{
return Ssse3.AlignRight(left, right, mask);
}

78
src/ImageSharp/Common/Helpers/Vector256Utilities.cs

@ -0,0 +1,78 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
namespace SixLabors.ImageSharp.Common.Helpers;
/// <summary>
/// Defines utility methods for <see cref="Vector256{T}"/> that have either:
/// <list type="number">
/// <item>Not yet been normalized in the runtime.</item>
/// <item>Produce codegen that is poorly optimized by the runtime.</item>
/// </list>
/// Should only be used if the intrinsics are available.
/// </summary>
internal static class Vector256Utilities
{
/// <summary>
/// Gets a value indicating whether shuffle byte operations are supported.
/// </summary>
public static bool SupportsShuffleFloat
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => Avx.IsSupported;
}
/// <summary>
/// Gets a value indicating whether shuffle byte operations are supported.
/// </summary>
public static bool SupportsShuffleByte
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => Avx2.IsSupported;
}
/// <summary>
/// Creates a new vector by selecting values from an input vector using a set of indices.
/// </summary>
/// <param name="vector">The input vector from which values are selected.</param>
/// <param name="control">The shuffle control byte.</param>
/// <returns>The <see cref="Vector256{Single}"/>.</returns>
public static Vector256<float> Shuffle(Vector256<float> vector, [ConstantExpected] byte control)
{
if (Avx.IsSupported)
{
return Avx.Shuffle(vector, vector, control);
}
ThrowUnreachableException();
return default;
}
/// <summary>
/// Creates a new vector by selecting values from an input vector using a set of indices.</summary>
/// <param name="vector">
/// The input vector from which values are selected.</param>
/// <param name="indices">
/// The per-element indices used to select a value from <paramref name="vector" />.
/// </param>
/// <returns>The <see cref="Vector256{Single}"/>.</returns>
public static Vector256<byte> Shuffle(Vector256<byte> vector, Vector256<byte> indices)
{
if (Avx2.IsSupported)
{
return Avx2.Shuffle(vector, indices);
}
ThrowUnreachableException();
return default;
}
[DoesNotReturn]
private static void ThrowUnreachableException() => throw new UnreachableException();
}

80
src/ImageSharp/Common/Helpers/Vector512Utilities.cs

@ -0,0 +1,80 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
namespace SixLabors.ImageSharp.Common.Helpers;
/// <summary>
/// Defines utility methods for <see cref="Vector512{T}"/> that have either:
/// <list type="number">
/// <item>Not yet been normalized in the runtime.</item>
/// <item>Produce codegen that is poorly optimized by the runtime.</item>
/// </list>
/// Should only be used if the intrinsics are available.
/// </summary>
internal static class Vector512Utilities
{
/// <summary>
/// Gets a value indicating whether shuffle float operations are supported.
/// </summary>
public static bool SupportsShuffleFloat
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => Avx512F.IsSupported;
}
/// <summary>
/// Gets a value indicating whether shuffle byte operations are supported.
/// </summary>
public static bool SupportsShuffleByte
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => Avx512BW.IsSupported;
}
/// <summary>
/// Creates a new vector by selecting values from an input vector using the control.
/// </summary>
/// <param name="vector">The input vector from which values are selected.</param>
/// <param name="control">The shuffle control byte.</param>
/// <returns>The <see cref="Vector512{Single}"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<float> Shuffle(Vector512<float> vector, [ConstantExpected] byte control)
{
if (Avx512F.IsSupported)
{
return Avx512F.Shuffle(vector, vector, control);
}
ThrowUnreachableException();
return default;
}
/// <summary>
/// Creates a new vector by selecting values from an input vector using a set of indices.
/// </summary>
/// <param name="vector">The input vector from which values are selected.</param>
/// <param name="indices">
/// The per-element indices used to select a value from <paramref name="vector" />.
/// </param>
/// <returns>The <see cref="Vector512{Byte}"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<byte> Shuffle(Vector512<byte> vector, Vector512<byte> indices)
{
if (Avx512BW.IsSupported)
{
return Avx512BW.Shuffle(vector, indices);
}
ThrowUnreachableException();
return default;
}
[DoesNotReturn]
private static void ThrowUnreachableException() => throw new UnreachableException();
}
Loading…
Cancel
Save