Browse Source

refactor Vector4 implementation code from PixelOperation<T> to Vector4Converters

pull/751/head
Anton Firszov 7 years ago
parent
commit
31369c7435
  1. 9
      src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.cs
  2. 9
      src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.cs
  3. 9
      src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.cs
  4. 1
      src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray16.PixelOperations.Generated.cs
  5. 1
      src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray8.PixelOperations.Generated.cs
  6. 9
      src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.cs
  7. 1
      src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb48.PixelOperations.Generated.cs
  8. 1
      src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba32.PixelOperations.Generated.cs
  9. 1
      src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba64.PixelOperations.Generated.cs
  10. 9
      src/ImageSharp/PixelFormats/PixelImplementations/Generated/_Common.ttinclude
  11. 147
      src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs
  12. 3
      src/ImageSharp/PixelFormats/Utils/PixelConverter.cs
  13. 2
      src/ImageSharp/PixelFormats/Utils/PixelExtensions.cs
  14. 89
      src/ImageSharp/PixelFormats/Utils/Vector4Converters.Default.cs
  15. 154
      src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs
  16. 21
      tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4_Bgra32.cs
  17. 1
      tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromRgba32.cs
  18. 1
      tests/ImageSharp.Tests/PixelFormats/PixelConverterTests.cs

9
src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.cs

@ -3,6 +3,7 @@
// <auto-generated />
using SixLabors.ImageSharp.PixelFormats.Utils;
using System;
using System.Buffers;
using System.Numerics;
@ -45,25 +46,25 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc />
internal override void FromVector4(Configuration configuration, ReadOnlySpan<Vector4> sourceVectors, Span<Argb32> destPixels)
{
this.RunRgba32CompatibleFromVector4Conversion(configuration, sourceVectors, destPixels);
Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, false);
}
/// <inheritdoc />
internal override void ToVector4(Configuration configuration, ReadOnlySpan<Argb32> sourcePixels, Span<Vector4> destVectors)
{
this.RunRgba32CompatibleToVector4Conversion(configuration, sourcePixels, destVectors);
Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, false);
}
/// <inheritdoc />
internal override void FromScaledVector4(Configuration configuration, ReadOnlySpan<Vector4> sourceVectors, Span<Argb32> destPixels)
{
this.RunRgba32CompatibleFromVector4Conversion(configuration, sourceVectors, destPixels);
Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, true);
}
/// <inheritdoc />
internal override void ToScaledVector4(Configuration configuration, ReadOnlySpan<Argb32> sourcePixels, Span<Vector4> destVectors)
{
this.RunRgba32CompatibleToVector4Conversion(configuration, sourcePixels, destVectors);
Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, true);
}
/// <inheritdoc />

9
src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.cs

@ -3,6 +3,7 @@
// <auto-generated />
using SixLabors.ImageSharp.PixelFormats.Utils;
using System;
using System.Buffers;
using System.Numerics;
@ -45,25 +46,25 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc />
internal override void FromVector4(Configuration configuration, ReadOnlySpan<Vector4> sourceVectors, Span<Bgr24> destPixels)
{
this.RunRgba32CompatibleFromVector4Conversion(configuration, sourceVectors, destPixels);
Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, false);
}
/// <inheritdoc />
internal override void ToVector4(Configuration configuration, ReadOnlySpan<Bgr24> sourcePixels, Span<Vector4> destVectors)
{
this.RunRgba32CompatibleToVector4Conversion(configuration, sourcePixels, destVectors);
Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, false);
}
/// <inheritdoc />
internal override void FromScaledVector4(Configuration configuration, ReadOnlySpan<Vector4> sourceVectors, Span<Bgr24> destPixels)
{
this.RunRgba32CompatibleFromVector4Conversion(configuration, sourceVectors, destPixels);
Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, true);
}
/// <inheritdoc />
internal override void ToScaledVector4(Configuration configuration, ReadOnlySpan<Bgr24> sourcePixels, Span<Vector4> destVectors)
{
this.RunRgba32CompatibleToVector4Conversion(configuration, sourcePixels, destVectors);
Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, true);
}

9
src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.cs

@ -3,6 +3,7 @@
// <auto-generated />
using SixLabors.ImageSharp.PixelFormats.Utils;
using System;
using System.Buffers;
using System.Numerics;
@ -45,25 +46,25 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc />
internal override void FromVector4(Configuration configuration, ReadOnlySpan<Vector4> sourceVectors, Span<Bgra32> destPixels)
{
this.RunRgba32CompatibleFromVector4Conversion(configuration, sourceVectors, destPixels);
Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, false);
}
/// <inheritdoc />
internal override void ToVector4(Configuration configuration, ReadOnlySpan<Bgra32> sourcePixels, Span<Vector4> destVectors)
{
this.RunRgba32CompatibleToVector4Conversion(configuration, sourcePixels, destVectors);
Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, false);
}
/// <inheritdoc />
internal override void FromScaledVector4(Configuration configuration, ReadOnlySpan<Vector4> sourceVectors, Span<Bgra32> destPixels)
{
this.RunRgba32CompatibleFromVector4Conversion(configuration, sourceVectors, destPixels);
Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, true);
}
/// <inheritdoc />
internal override void ToScaledVector4(Configuration configuration, ReadOnlySpan<Bgra32> sourcePixels, Span<Vector4> destVectors)
{
this.RunRgba32CompatibleToVector4Conversion(configuration, sourcePixels, destVectors);
Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, true);
}
/// <inheritdoc />

1
src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray16.PixelOperations.Generated.cs

@ -3,6 +3,7 @@
// <auto-generated />
using SixLabors.ImageSharp.PixelFormats.Utils;
using System;
using System.Buffers;
using System.Numerics;

1
src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray8.PixelOperations.Generated.cs

@ -3,6 +3,7 @@
// <auto-generated />
using SixLabors.ImageSharp.PixelFormats.Utils;
using System;
using System.Buffers;
using System.Numerics;

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

@ -3,6 +3,7 @@
// <auto-generated />
using SixLabors.ImageSharp.PixelFormats.Utils;
using System;
using System.Buffers;
using System.Numerics;
@ -45,25 +46,25 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc />
internal override void FromVector4(Configuration configuration, ReadOnlySpan<Vector4> sourceVectors, Span<Rgb24> destPixels)
{
this.RunRgba32CompatibleFromVector4Conversion(configuration, sourceVectors, destPixels);
Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, false);
}
/// <inheritdoc />
internal override void ToVector4(Configuration configuration, ReadOnlySpan<Rgb24> sourcePixels, Span<Vector4> destVectors)
{
this.RunRgba32CompatibleToVector4Conversion(configuration, sourcePixels, destVectors);
Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, false);
}
/// <inheritdoc />
internal override void FromScaledVector4(Configuration configuration, ReadOnlySpan<Vector4> sourceVectors, Span<Rgb24> destPixels)
{
this.RunRgba32CompatibleFromVector4Conversion(configuration, sourceVectors, destPixels);
Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, true);
}
/// <inheritdoc />
internal override void ToScaledVector4(Configuration configuration, ReadOnlySpan<Rgb24> sourcePixels, Span<Vector4> destVectors)
{
this.RunRgba32CompatibleToVector4Conversion(configuration, sourcePixels, destVectors);
Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, true);
}

1
src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb48.PixelOperations.Generated.cs

@ -3,6 +3,7 @@
// <auto-generated />
using SixLabors.ImageSharp.PixelFormats.Utils;
using System;
using System.Buffers;
using System.Numerics;

1
src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba32.PixelOperations.Generated.cs

@ -3,6 +3,7 @@
// <auto-generated />
using SixLabors.ImageSharp.PixelFormats.Utils;
using System;
using System.Buffers;
using System.Numerics;

1
src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba64.PixelOperations.Generated.cs

@ -3,6 +3,7 @@
// <auto-generated />
using SixLabors.ImageSharp.PixelFormats.Utils;
using System;
using System.Buffers;
using System.Numerics;

9
src/ImageSharp/PixelFormats/PixelImplementations/Generated/_Common.ttinclude

@ -8,6 +8,7 @@
// <auto-generated />
using SixLabors.ImageSharp.PixelFormats.Utils;
using System;
using System.Buffers;
using System.Numerics;
@ -114,25 +115,25 @@ using System.Runtime.InteropServices;
/// <inheritdoc />
internal override void FromVector4(Configuration configuration, ReadOnlySpan<Vector4> sourceVectors, Span<<#=pixelType#>> destPixels)
{
this.RunRgba32CompatibleFromVector4Conversion(configuration, sourceVectors, destPixels);
Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, false);
}
/// <inheritdoc />
internal override void ToVector4(Configuration configuration, ReadOnlySpan<<#=pixelType#>> sourcePixels, Span<Vector4> destVectors)
{
this.RunRgba32CompatibleToVector4Conversion(configuration, sourcePixels, destVectors);
Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, false);
}
/// <inheritdoc />
internal override void FromScaledVector4(Configuration configuration, ReadOnlySpan<Vector4> sourceVectors, Span<<#=pixelType#>> destPixels)
{
this.RunRgba32CompatibleFromVector4Conversion(configuration, sourceVectors, destPixels);
Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, true);
}
/// <inheritdoc />
internal override void ToScaledVector4(Configuration configuration, ReadOnlySpan<<#=pixelType#>> sourcePixels, Span<Vector4> destVectors)
{
this.RunRgba32CompatibleToVector4Conversion(configuration, sourcePixels, destVectors);
Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, true);
}
<#+

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

@ -17,12 +17,6 @@ namespace SixLabors.ImageSharp.PixelFormats
public partial class PixelOperations<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// It's not worth to bother the transitive pixel conversion method below this limit.
/// The value depends on the actual gain brought by the SIMD characteristics of the executing CPU and JIT.
/// </summary>
private static readonly int Vector4ConversionThreshold = CalculateVector4ConversionThreshold();
/// <summary>
/// Gets the global <see cref="PixelOperations{TPixel}"/> instance for the pixel type <typeparamref name="TPixel"/>
/// </summary>
@ -42,7 +36,7 @@ namespace SixLabors.ImageSharp.PixelFormats
Guard.NotNull(configuration, nameof(configuration));
Guard.DestinationShouldNotBeTooShort(sourceVectors, destPixels, nameof(destPixels));
FromVector4DefaultImpl(sourceVectors, destPixels);
Utils.Vector4Converters.Default.DangerousFromVector4(sourceVectors, destPixels);
}
/// <summary>
@ -59,7 +53,7 @@ namespace SixLabors.ImageSharp.PixelFormats
Guard.NotNull(configuration, nameof(configuration));
Guard.DestinationShouldNotBeTooShort(sourcePixels, destVectors, nameof(destVectors));
ToVector4DefaultImpl(sourcePixels, destVectors);
Utils.Vector4Converters.Default.DangerousToVector4(sourcePixels, destVectors);
}
/// <summary>
@ -76,15 +70,7 @@ namespace SixLabors.ImageSharp.PixelFormats
Guard.NotNull(configuration, nameof(configuration));
Guard.DestinationShouldNotBeTooShort(sourceVectors, destinationColors, nameof(destinationColors));
ref Vector4 sourceRef = ref MemoryMarshal.GetReference(sourceVectors);
ref TPixel destRef = ref MemoryMarshal.GetReference(destinationColors);
for (int i = 0; i < sourceVectors.Length; i++)
{
ref Vector4 sp = ref Unsafe.Add(ref sourceRef, i);
ref TPixel dp = ref Unsafe.Add(ref destRef, i);
dp.FromScaledVector4(sp);
}
Utils.Vector4Converters.Default.DangerousFromScaledVector4(sourceVectors, destinationColors);
}
/// <summary>
@ -101,15 +87,7 @@ namespace SixLabors.ImageSharp.PixelFormats
Guard.NotNull(configuration, nameof(configuration));
Guard.DestinationShouldNotBeTooShort(sourceColors, destinationVectors, nameof(destinationVectors));
ref TPixel sourceRef = ref MemoryMarshal.GetReference(sourceColors);
ref Vector4 destRef = ref MemoryMarshal.GetReference(destinationVectors);
for (int i = 0; i < sourceColors.Length; i++)
{
ref TPixel sp = ref Unsafe.Add(ref sourceRef, i);
ref Vector4 dp = ref Unsafe.Add(ref destRef, i);
dp = sp.ToScaledVector4();
}
Utils.Vector4Converters.Default.DangerousToScaledVector4(sourceColors, destinationVectors);
}
/// <summary>
@ -170,122 +148,5 @@ namespace SixLabors.ImageSharp.PixelFormats
dp.FromScaledVector4(sp.ToScaledVector4());
}
}
// TODO: The Vector4 helpers should be moved to a utility class.
/// <summary>
/// Provides an efficient default implementation for <see cref="ToVector4"/> and <see cref="ToScaledVector4"/>
/// which is applicable for <see cref="Rgba32"/>-compatible pixel types where <see cref="IPixel.ToVector4"/>
/// returns the same scaled result as <see cref="IPixel.ToScaledVector4"/>.
/// The method is works by internally converting to a <see cref="Rgba32"/> therefore it's not applicable for that type!
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
internal void RunRgba32CompatibleToVector4Conversion(
Configuration configuration,
ReadOnlySpan<TPixel> sourcePixels,
Span<Vector4> destVectors)
{
Guard.NotNull(configuration, nameof(configuration));
Guard.DestinationShouldNotBeTooShort(sourcePixels, destVectors, nameof(destVectors));
int count = sourcePixels.Length;
// Not worth for small buffers:
if (count < Vector4ConversionThreshold)
{
ToVector4DefaultImpl(sourcePixels, destVectors);
return;
}
// Using the last quarter of 'destVectors' as a temporary buffer to avoid allocation:
int countWithoutLastItem = count - 1;
ReadOnlySpan<TPixel> reducedSource = sourcePixels.Slice(0, countWithoutLastItem);
Span<Rgba32> lastQuarterOfDestBuffer = MemoryMarshal.Cast<Vector4, Rgba32>(destVectors).Slice((3 * count) + 1, countWithoutLastItem);
this.ToRgba32(configuration, reducedSource, lastQuarterOfDestBuffer);
// 'destVectors' and 'lastQuarterOfDestBuffer' are ovelapping buffers,
// but we are always reading/writing at different positions:
SimdUtils.BulkConvertByteToNormalizedFloat(
MemoryMarshal.Cast<Rgba32, byte>(lastQuarterOfDestBuffer),
MemoryMarshal.Cast<Vector4, float>(destVectors.Slice(0, countWithoutLastItem)));
destVectors[countWithoutLastItem] = sourcePixels[countWithoutLastItem].ToVector4();
}
/// <summary>
/// Provides an efficient default implementation for <see cref="FromVector4"/> and <see cref="FromScaledVector4"/>
/// which is applicable for <see cref="Rgba32"/>-compatible pixel types where <see cref="IPixel.ToVector4"/>
/// returns the same scaled result as <see cref="IPixel.ToScaledVector4"/>.
/// The method is works by internally converting to a <see cref="Rgba32"/> therefore it's not applicable for that type!
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
internal void RunRgba32CompatibleFromVector4Conversion(
Configuration configuration,
ReadOnlySpan<Vector4> sourceVectors,
Span<TPixel> destPixels)
{
Guard.NotNull(configuration, nameof(configuration));
Guard.DestinationShouldNotBeTooShort(sourceVectors, destPixels, nameof(destPixels));
int count = sourceVectors.Length;
// Not worth for small buffers:
if (count < Vector4ConversionThreshold)
{
FromVector4DefaultImpl(sourceVectors, destPixels);
return;
}
// For the opposite direction it's not easy to implement the trick used in RunRgba32CompatibleToVector4Conversion,
// so let's allocate a temporary buffer as usually:
using (IMemoryOwner<Rgba32> tempBuffer = configuration.MemoryAllocator.Allocate<Rgba32>(count))
{
Span<Rgba32> tempSpan = tempBuffer.Memory.Span;
SimdUtils.BulkConvertNormalizedFloatToByteClampOverflows(
MemoryMarshal.Cast<Vector4, float>(sourceVectors),
MemoryMarshal.Cast<Rgba32, byte>(tempSpan));
this.FromRgba32(configuration, tempSpan, destPixels);
}
}
[MethodImpl(InliningOptions.ShortMethod)]
private static void FromVector4DefaultImpl(ReadOnlySpan<Vector4> sourceVectors, Span<TPixel> destPixels)
{
ref Vector4 sourceRef = ref MemoryMarshal.GetReference(sourceVectors);
ref TPixel destRef = ref MemoryMarshal.GetReference(destPixels);
for (int i = 0; i < sourceVectors.Length; i++)
{
ref Vector4 sp = ref Unsafe.Add(ref sourceRef, i);
ref TPixel dp = ref Unsafe.Add(ref destRef, i);
dp.FromVector4(sp);
}
}
[MethodImpl(InliningOptions.ShortMethod)]
private static void ToVector4DefaultImpl(ReadOnlySpan<TPixel> sourcePixels, Span<Vector4> destVectors)
{
ref TPixel sourceRef = ref MemoryMarshal.GetReference(sourcePixels);
ref Vector4 destRef = ref MemoryMarshal.GetReference(destVectors);
for (int i = 0; i < sourcePixels.Length; i++)
{
ref TPixel sp = ref Unsafe.Add(ref sourceRef, i);
ref Vector4 dp = ref Unsafe.Add(ref destRef, i);
dp = sp.ToVector4();
}
}
private static int CalculateVector4ConversionThreshold()
{
if (!Vector.IsHardwareAccelerated)
{
return int.MaxValue;
}
return SimdUtils.ExtendedIntrinsics.IsAvailable && SimdUtils.IsAvx2CompatibleArchitecture ? 256 : 128;
}
}
}

3
src/ImageSharp/PixelFormats/PixelConverter.cs → src/ImageSharp/PixelFormats/Utils/PixelConverter.cs

@ -1,11 +1,10 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers.Binary;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.PixelFormats
namespace SixLabors.ImageSharp.PixelFormats.Utils
{
/// <summary>
/// Contains optimized implementations for conversion between pixel formats.

2
src/ImageSharp/PixelFormats/PixelExtensions.cs → src/ImageSharp/PixelFormats/Utils/PixelExtensions.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.PixelFormats
namespace SixLabors.ImageSharp.PixelFormats.Utils
{
/// <summary>
/// Low-performance extension methods to help conversion syntax, suitable for testing purposes.

89
src/ImageSharp/PixelFormats/Utils/Vector4Converters.Default.cs

@ -0,0 +1,89 @@
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.PixelFormats.Utils
{
/// <summary>
/// Helper class for (bulk) conversion of <see cref="Vector4"/> buffers to/from other buffer types.
/// </summary>
internal static partial class Vector4Converters
{
/// <summary>
/// Provides default implementations for batched to/from <see cref="Vector4"/> conversion.
/// WARNING: The methods are operating without bounds checking and input validation!
/// Input validation is the responsibility of the caller!
/// </summary>
public static class Default
{
[MethodImpl(InliningOptions.ShortMethod)]
internal static void DangerousFromVector4<TPixel>(
ReadOnlySpan<Vector4> sourceVectors,
Span<TPixel> destPixels)
where TPixel : struct, IPixel<TPixel>
{
ref Vector4 sourceRef = ref MemoryMarshal.GetReference(sourceVectors);
ref TPixel destRef = ref MemoryMarshal.GetReference(destPixels);
for (int i = 0; i < sourceVectors.Length; i++)
{
ref Vector4 sp = ref Unsafe.Add(ref sourceRef, i);
ref TPixel dp = ref Unsafe.Add(ref destRef, i);
dp.FromVector4(sp);
}
}
[MethodImpl(InliningOptions.ShortMethod)]
internal static void DangerousToVector4<TPixel>(
ReadOnlySpan<TPixel> sourcePixels,
Span<Vector4> destVectors)
where TPixel : struct, IPixel<TPixel>
{
ref TPixel sourceRef = ref MemoryMarshal.GetReference(sourcePixels);
ref Vector4 destRef = ref MemoryMarshal.GetReference(destVectors);
for (int i = 0; i < sourcePixels.Length; i++)
{
ref TPixel sp = ref Unsafe.Add(ref sourceRef, i);
ref Vector4 dp = ref Unsafe.Add(ref destRef, i);
dp = sp.ToVector4();
}
}
[MethodImpl(InliningOptions.ShortMethod)]
internal static void DangerousFromScaledVector4<TPixel>(
ReadOnlySpan<Vector4> sourceVectors,
Span<TPixel> destinationColors)
where TPixel : struct, IPixel<TPixel>
{
ref Vector4 sourceRef = ref MemoryMarshal.GetReference(sourceVectors);
ref TPixel destRef = ref MemoryMarshal.GetReference(destinationColors);
for (int i = 0; i < sourceVectors.Length; i++)
{
ref Vector4 sp = ref Unsafe.Add(ref sourceRef, i);
ref TPixel dp = ref Unsafe.Add(ref destRef, i);
dp.FromScaledVector4(sp);
}
}
[MethodImpl(InliningOptions.ShortMethod)]
internal static void DangerousToScaledVector4<TPixel>(
ReadOnlySpan<TPixel> sourceColors,
Span<Vector4> destinationVectors)
where TPixel : struct, IPixel<TPixel>
{
ref TPixel sourceRef = ref MemoryMarshal.GetReference(sourceColors);
ref Vector4 destRef = ref MemoryMarshal.GetReference(destinationVectors);
for (int i = 0; i < sourceColors.Length; i++)
{
ref TPixel sp = ref Unsafe.Add(ref sourceRef, i);
ref Vector4 dp = ref Unsafe.Add(ref destRef, i);
dp = sp.ToScaledVector4();
}
}
}
}
}

154
src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs

@ -0,0 +1,154 @@
using System;
using System.Buffers;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.PixelFormats.Utils
{
/// <content>
/// Contains <see cref="RgbaCompatible"/>
/// </content>
internal static partial class Vector4Converters
{
/// <summary>
/// Provides efficient implementations for batched to/from <see cref="Vector4"/> conversion.
/// which is applicable for <see cref="Rgba32"/>-compatible pixel types where <see cref="IPixel.ToVector4"/>
/// returns the same scaled result as <see cref="IPixel.ToScaledVector4"/>.
/// The method is works by internally converting to a <see cref="Rgba32"/> therefore it's not applicable for that type!
/// </summary>
public static class RgbaCompatible
{
/// <summary>
/// It's not worth to bother the transitive pixel conversion method below this limit.
/// The value depends on the actual gain brought by the SIMD characteristics of the executing CPU and JIT.
/// </summary>
private static readonly int Vector4ConversionThreshold = CalculateVector4ConversionThreshold();
/// <summary>
/// Provides an efficient default implementation for <see cref="PixelOperations{TPixel}.ToVector4"/>
/// and <see cref="PixelOperations{TPixel}.ToScaledVector4"/>
/// which is applicable for <see cref="Rgba32"/>-compatible pixel types where <see cref="IPixel.ToVector4"/>
/// returns the same scaled result as <see cref="IPixel.ToScaledVector4"/>.
/// The method is works by internally converting to a <see cref="Rgba32"/> therefore it's not applicable for that type!
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
internal static void ToVector4<TPixel>(
Configuration configuration,
PixelOperations<TPixel> pixelOperations,
ReadOnlySpan<TPixel> sourcePixels,
Span<Vector4> destVectors,
bool scaled)
where TPixel : struct, IPixel<TPixel>
{
Guard.NotNull(configuration, nameof(configuration));
Guard.DestinationShouldNotBeTooShort(sourcePixels, destVectors, nameof(destVectors));
int count = sourcePixels.Length;
// Not worth for small buffers:
if (count < Vector4ConversionThreshold)
{
ToVector4Fallback(sourcePixels, destVectors, scaled);
return;
}
// Using the last quarter of 'destVectors' as a temporary buffer to avoid allocation:
int countWithoutLastItem = count - 1;
ReadOnlySpan<TPixel> reducedSource = sourcePixels.Slice(0, countWithoutLastItem);
Span<Rgba32> lastQuarterOfDestBuffer = MemoryMarshal.Cast<Vector4, Rgba32>(destVectors).Slice((3 * count) + 1, countWithoutLastItem);
pixelOperations.ToRgba32(configuration, reducedSource, lastQuarterOfDestBuffer);
// 'destVectors' and 'lastQuarterOfDestBuffer' are ovelapping buffers,
// but we are always reading/writing at different positions:
SimdUtils.BulkConvertByteToNormalizedFloat(
MemoryMarshal.Cast<Rgba32, byte>(lastQuarterOfDestBuffer),
MemoryMarshal.Cast<Vector4, float>(destVectors.Slice(0, countWithoutLastItem)));
destVectors[countWithoutLastItem] = sourcePixels[countWithoutLastItem].ToVector4();
}
/// <summary>
/// Provides an efficient default implementation for <see cref="PixelOperations{TPixel}.FromVector4"/>
/// and <see cref="PixelOperations{TPixel}.FromScaledVector4"/>
/// which is applicable for <see cref="Rgba32"/>-compatible pixel types where <see cref="IPixel.ToVector4"/>
/// returns the same scaled result as <see cref="IPixel.ToScaledVector4"/>.
/// The method is works by internally converting to a <see cref="Rgba32"/> therefore it's not applicable for that type!
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
internal static void FromVector4<TPixel>(
Configuration configuration,
PixelOperations<TPixel> pixelOperations,
ReadOnlySpan<Vector4> sourceVectors,
Span<TPixel> destPixels,
bool scaled)
where TPixel : struct, IPixel<TPixel>
{
Guard.NotNull(configuration, nameof(configuration));
Guard.DestinationShouldNotBeTooShort(sourceVectors, destPixels, nameof(destPixels));
int count = sourceVectors.Length;
// Not worth for small buffers:
if (count < Vector4ConversionThreshold)
{
FromVector4Fallback(sourceVectors, destPixels, scaled);
return;
}
// For the opposite direction it's not easy to implement the trick used in RunRgba32CompatibleToVector4Conversion,
// so let's allocate a temporary buffer as usually:
using (IMemoryOwner<Rgba32> tempBuffer = configuration.MemoryAllocator.Allocate<Rgba32>(count))
{
Span<Rgba32> tempSpan = tempBuffer.Memory.Span;
SimdUtils.BulkConvertNormalizedFloatToByteClampOverflows(
MemoryMarshal.Cast<Vector4, float>(sourceVectors),
MemoryMarshal.Cast<Rgba32, byte>(tempSpan));
pixelOperations.FromRgba32(configuration, tempSpan, destPixels);
}
}
[MethodImpl(InliningOptions.ColdPath)]
private static void ToVector4Fallback<TPixel>(ReadOnlySpan<TPixel> sourcePixels, Span<Vector4> destVectors, bool scaled)
where TPixel : struct, IPixel<TPixel>
{
if (scaled)
{
Default.DangerousToScaledVector4(sourcePixels, destVectors);
}
else
{
Default.DangerousToVector4(sourcePixels, destVectors);
}
}
[MethodImpl(InliningOptions.ColdPath)]
private static void FromVector4Fallback<TPixel>(ReadOnlySpan<Vector4> sourceVectors, Span<TPixel> destPixels, bool scaled)
where TPixel : struct, IPixel<TPixel>
{
if (scaled)
{
Default.DangerousFromScaledVector4(sourceVectors, destPixels);
}
else
{
Default.DangerousFromVector4(sourceVectors, destPixels);
}
}
private static int CalculateVector4ConversionThreshold()
{
if (!Vector.IsHardwareAccelerated)
{
return int.MaxValue;
}
return SimdUtils.ExtendedIntrinsics.IsAvailable && SimdUtils.IsAvx2CompatibleArchitecture ? 256 : 128;
}
}
}
}

21
tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4_Bgra32.cs

@ -16,5 +16,26 @@ namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk
this.source.GetSpan(),
this.destination.GetSpan());
}
// RESULTS:
// Method | Runtime | Count | Mean | Error | StdDev | Scaled | ScaledSD | Gen 0 | Allocated |
// ---------------------------- |-------- |------ |-----------:|------------:|-----------:|-------:|---------:|-------:|----------:|
// PixelOperations_Base | Clr | 64 | 339.9 ns | 138.30 ns | 7.8144 ns | 1.00 | 0.00 | 0.0072 | 24 B |
// PixelOperations_Specialized | Clr | 64 | 338.1 ns | 13.30 ns | 0.7515 ns | 0.99 | 0.02 | - | 0 B |
// | | | | | | | | | |
// PixelOperations_Base | Core | 64 | 245.6 ns | 29.05 ns | 1.6413 ns | 1.00 | 0.00 | 0.0072 | 24 B |
// PixelOperations_Specialized | Core | 64 | 257.1 ns | 37.89 ns | 2.1407 ns | 1.05 | 0.01 | - | 0 B |
// | | | | | | | | | |
// PixelOperations_Base | Clr | 256 | 972.7 ns | 61.98 ns | 3.5020 ns | 1.00 | 0.00 | 0.0057 | 24 B |
// PixelOperations_Specialized | Clr | 256 | 882.9 ns | 126.21 ns | 7.1312 ns | 0.91 | 0.01 | - | 0 B |
// | | | | | | | | | |
// PixelOperations_Base | Core | 256 | 910.0 ns | 90.87 ns | 5.1346 ns | 1.00 | 0.00 | 0.0067 | 24 B |
// PixelOperations_Specialized | Core | 256 | 448.4 ns | 15.77 ns | 0.8910 ns | 0.49 | 0.00 | - | 0 B |
// | | | | | | | | | |
// PixelOperations_Base | Clr | 2048 | 6,951.8 ns | 1,299.01 ns | 73.3963 ns | 1.00 | 0.00 | - | 24 B |
// PixelOperations_Specialized | Clr | 2048 | 5,852.3 ns | 630.56 ns | 35.6279 ns | 0.84 | 0.01 | - | 0 B |
// | | | | | | | | | |
// PixelOperations_Base | Core | 2048 | 6,937.5 ns | 1,692.19 ns | 95.6121 ns | 1.00 | 0.00 | - | 24 B |
// PixelOperations_Specialized | Core | 2048 | 2,994.5 ns | 1,126.65 ns | 63.6578 ns | 0.43 | 0.01 | - | 0 B |
}
}

1
tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromRgba32.cs

@ -7,6 +7,7 @@ using System.Runtime.InteropServices;
using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.PixelFormats.Utils;
namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion
{

1
tests/ImageSharp.Tests/PixelFormats/PixelConverterTests.cs

@ -1,4 +1,5 @@
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.PixelFormats.Utils;
using Xunit;

Loading…
Cancel
Save