Browse Source

Added avx accelerated rgb unpack method

pull/2120/head
Dmitry Pentin 4 years ago
parent
commit
a83b3b699a
  1. 47
      src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs
  2. 39
      src/ImageSharp/Common/Helpers/SimdUtils.Pack.cs
  3. 10
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromYCbCrAvx.cs
  4. 17
      src/ImageSharp/Formats/Jpeg/Components/Encoder/SpectralConverter{TPixel}.cs
  5. 9
      src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgb24.PixelOperations.cs
  6. 13
      src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs

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

@ -21,6 +21,10 @@ namespace SixLabors.ImageSharp
public static ReadOnlySpan<byte> PermuteMaskSwitchInnerDWords8x32 => new byte[] { 0, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0 };
private static ReadOnlySpan<byte> MoveFirst24BytesToSeparateLanes => new byte[] { 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 6, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 7, 0, 0, 0 };
internal static ReadOnlySpan<byte> ExtractRgb => new byte[] { 0, 3, 6, 9, 1, 4, 7, 10, 2, 5, 8, 11, 0xFF, 0xFF, 0xFF, 0xFF, 0, 3, 6, 9, 1, 4, 7, 10, 2, 5, 8, 11, 0xFF, 0xFF, 0xFF, 0xFF };
private static ReadOnlySpan<byte> ShuffleMaskPad4Nx16 => new byte[] { 0, 1, 2, 0x80, 3, 4, 5, 0x80, 6, 7, 8, 0x80, 9, 10, 11, 0x80 };
private static ReadOnlySpan<byte> ShuffleMaskSlice4Nx16 => new byte[] { 0, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 14, 0x80, 0x80, 0x80, 0x80 };
@ -962,6 +966,49 @@ namespace SixLabors.ImageSharp
blueChannel = blueChannel.Slice(slice);
destination = destination.Slice(slice);
}
internal static void UnpackToRgbPlanesAvx2Reduce(
ref ReadOnlySpan<float> redChannel,
ref ReadOnlySpan<float> greenChannel,
ref ReadOnlySpan<float> blueChannel,
ref Span<Rgb24> source)
{
ref Vector256<byte> rgbByteSpan = ref Unsafe.As<Rgb24, Vector256<byte>>(ref MemoryMarshal.GetReference(source));
ref Vector256<float> destRRef = ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(redChannel));
ref Vector256<float> destGRef = ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(greenChannel));
ref Vector256<float> destBRef = ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(blueChannel));
Vector256<uint> extractToLanesMask = Unsafe.As<byte, Vector256<uint>>(ref MemoryMarshal.GetReference(MoveFirst24BytesToSeparateLanes));
Vector256<byte> extractRgbMask = Unsafe.As<byte, Vector256<byte>>(ref MemoryMarshal.GetReference(ExtractRgb));
Vector256<byte> rgb, rg, bx;
Vector256<float> r, g, b;
const int bytesPerRgbStride = 24;
int count = source.Length / 8;
for (int i = 0; i < count; i++)
{
rgb = Avx2.PermuteVar8x32(Unsafe.AddByteOffset(ref rgbByteSpan, (IntPtr)(bytesPerRgbStride * i)).AsUInt32(), extractToLanesMask).AsByte();
rgb = Avx2.Shuffle(rgb, extractRgbMask);
rg = Avx2.UnpackLow(rgb, Vector256<byte>.Zero);
bx = Avx2.UnpackHigh(rgb, Vector256<byte>.Zero);
r = Avx.ConvertToVector256Single(Avx2.UnpackLow(rg, Vector256<byte>.Zero).AsInt32());
g = Avx.ConvertToVector256Single(Avx2.UnpackHigh(rg, Vector256<byte>.Zero).AsInt32());
b = Avx.ConvertToVector256Single(Avx2.UnpackLow(bx, Vector256<byte>.Zero).AsInt32());
Unsafe.Add(ref destRRef, i) = r;
Unsafe.Add(ref destGRef, i) = g;
Unsafe.Add(ref destBRef, i) = b;
}
int sliceCount = count * 8;
redChannel = redChannel.Slice(sliceCount);
greenChannel = greenChannel.Slice(sliceCount);
blueChannel = blueChannel.Slice(sliceCount);
source = source.Slice(sliceCount);
}
}
}
}

39
src/ImageSharp/Common/Helpers/SimdUtils.Pack.cs

@ -65,6 +65,25 @@ namespace SixLabors.ImageSharp
PackFromRgbPlanesRemainder(redChannel, greenChannel, blueChannel, destination);
}
[MethodImpl(InliningOptions.ShortMethod)]
internal static void UnpackToRgbPlanes(
ReadOnlySpan<float> redChannel,
ReadOnlySpan<float> greenChannel,
ReadOnlySpan<float> blueChannel,
Span<Rgb24> source)
{
DebugGuard.IsTrue(greenChannel.Length == redChannel.Length, nameof(greenChannel), "Channels must be of same size!");
DebugGuard.IsTrue(blueChannel.Length == redChannel.Length, nameof(blueChannel), "Channels must be of same size!");
DebugGuard.IsTrue(source.Length <= redChannel.Length, nameof(source), "'source' span should not be bigger than the destination channels!");
if (Avx2.IsSupported)
{
HwIntrinsics.UnpackToRgbPlanesAvx2Reduce(ref redChannel, ref greenChannel, ref blueChannel, ref source);
}
UnpackToRgbPlanesScalar(redChannel, greenChannel, blueChannel, source);
}
private static void PackFromRgbPlanesScalarBatchedReduce(
ref ReadOnlySpan<byte> redChannel,
ref ReadOnlySpan<byte> greenChannel,
@ -200,5 +219,25 @@ namespace SixLabors.ImageSharp
d.A = 255;
}
}
private static void UnpackToRgbPlanesScalar(
ReadOnlySpan<float> redChannel,
ReadOnlySpan<float> greenChannel,
ReadOnlySpan<float> blueChannel,
Span<Rgb24> source)
{
ref float r = ref MemoryMarshal.GetReference(redChannel);
ref float g = ref MemoryMarshal.GetReference(greenChannel);
ref float b = ref MemoryMarshal.GetReference(blueChannel);
ref Rgb24 rgb = ref MemoryMarshal.GetReference(source);
for (int i = 0; i < source.Length; i++)
{
ref Rgb24 src = ref Unsafe.Add(ref rgb, i);
Unsafe.Add(ref r, i) = src.R;
Unsafe.Add(ref g, i) = src.G;
Unsafe.Add(ref b, i) = src.B;
}
}
}
}

10
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromYCbCrAvx.cs

@ -80,6 +80,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
// Used for the color conversion
var chromaOffset = Vector256.Create(this.HalfValue);
var scale = Vector256.Create(this.MaximumValue);
var f0299 = Vector256.Create(0.299f);
var f0587 = Vector256.Create(0.587f);
@ -97,9 +98,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
ref Vector256<float> c1 = ref Unsafe.Add(ref c1Base, i);
ref Vector256<float> c2 = ref Unsafe.Add(ref c2Base, i);
Vector256<float> r = Avx.Multiply(c0, scale);
Vector256<float> g = Avx.Multiply(c1, scale);
Vector256<float> b = Avx.Multiply(c2, scale);
// Vector256<float> r = Avx.Multiply(c0, scale);
// Vector256<float> g = Avx.Multiply(c1, scale);
// Vector256<float> b = Avx.Multiply(c2, scale);
Vector256<float> r = c0;
Vector256<float> g = c1;
Vector256<float> b = c2;
// y = 0 + (0.299 * r) + (0.587 * g) + (0.114 * b)
// cb = 128 - (0.168736 * r) - (0.331264 * g) + (0.5 * b)

17
src/ImageSharp/Formats/Jpeg/Components/Encoder/SpectralConverter{TPixel}.cs

@ -21,8 +21,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
private int pixelRowCounter;
private IMemoryOwner<byte> rgbBuffer;
private Buffer2D<TPixel> pixelBuffer;
private int alignedPixelWidth;
@ -57,9 +55,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
this.componentProcessors[i] = new JpegComponentPostProcessor(allocator, component, postProcessorBufferSize, dequantTables[component.QuantizationTableIndex]);
}
// single 'stride' rgba32 buffer for conversion between spectral and TPixel
this.rgbBuffer = allocator.Allocate<byte>(this.alignedPixelWidth * 3);
// color converter from Rgb24 to YCbCr
this.colorConverter = JpegColorConverterBase.GetConverter(colorSpace: frame.ColorSpace, precision: 8);
}
@ -80,25 +75,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
// 4. Convert color buffer to spectral blocks with component post processors
int maxY = Math.Min(this.pixelBuffer.Height, this.pixelRowCounter + this.pixelRowsPerStep);
int width = this.pixelBuffer.Width;
Span<byte> r = this.rgbBuffer.Slice(0, this.alignedPixelWidth);
Span<byte> g = this.rgbBuffer.Slice(this.alignedPixelWidth, this.alignedPixelWidth);
Span<byte> b = this.rgbBuffer.Slice(this.alignedPixelWidth * 2, this.alignedPixelWidth);
for (int yy = this.pixelRowCounter; yy < maxY; yy++)
{
int y = yy - this.pixelRowCounter;
// unpack TPixel to r/g/b planes
Span<TPixel> sourceRow = this.pixelBuffer.DangerousGetRowSpan(yy);
PixelOperations<TPixel>.Instance.UnpackIntoRgbPlanes(this.configuration, r, g, b, sourceRow);
var values = new JpegColorConverterBase.ComponentValues(this.componentProcessors, y);
SimdUtils.ByteToNormalizedFloat(r, values.Component0);
SimdUtils.ByteToNormalizedFloat(g, values.Component1);
SimdUtils.ByteToNormalizedFloat(b, values.Component2);
PixelOperations<TPixel>.Instance.UnpackIntoRgbPlanes(this.configuration, values.Component0, values.Component1, values.Component2, sourceRow);
this.colorConverter.ConvertFromRgbInplace(values);
}

9
src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgb24.PixelOperations.cs

@ -36,6 +36,15 @@ namespace SixLabors.ImageSharp.PixelFormats
SimdUtils.PackFromRgbPlanes(configuration, redChannel, greenChannel, blueChannel, destination);
}
/// <inheritdoc />
internal override void UnpackIntoRgbPlanes(
Configuration configuration,
ReadOnlySpan<float> redChannel,
ReadOnlySpan<float> greenChannel,
ReadOnlySpan<float> blueChannel,
Span<Rgb24> source)
=> SimdUtils.UnpackToRgbPlanes(redChannel, greenChannel, blueChannel, source);
}
}
}

13
src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs

@ -209,9 +209,9 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <param name="source">A <see cref="Span{T}"/> to the destination pixels.</param>
internal virtual void UnpackIntoRgbPlanes(
Configuration configuration,
ReadOnlySpan<byte> redChannel,
ReadOnlySpan<byte> greenChannel,
ReadOnlySpan<byte> blueChannel,
ReadOnlySpan<float> redChannel,
ReadOnlySpan<float> greenChannel,
ReadOnlySpan<float> blueChannel,
Span<TPixel> source)
{
Guard.NotNull(configuration, nameof(configuration));
@ -219,15 +219,14 @@ namespace SixLabors.ImageSharp.PixelFormats
int count = redChannel.Length;
Rgba32 rgba32 = default;
ref byte r = ref MemoryMarshal.GetReference(redChannel);
ref byte g = ref MemoryMarshal.GetReference(greenChannel);
ref byte b = ref MemoryMarshal.GetReference(blueChannel);
ref float r = ref MemoryMarshal.GetReference(redChannel);
ref float g = ref MemoryMarshal.GetReference(greenChannel);
ref float b = ref MemoryMarshal.GetReference(blueChannel);
ref TPixel d = ref MemoryMarshal.GetReference(source);
for (int i = 0; i < count; i++)
{
// TODO: Create ToRgb24 method in IPixel
// TODO: create a fast intrinsic accelerated Rgb24 -> r/g/b planes overload
Unsafe.Add(ref d, i).ToRgba32(ref rgba32);
Unsafe.Add(ref r, i) = rgba32.R;
Unsafe.Add(ref g, i) = rgba32.G;

Loading…
Cancel
Save