diff --git a/src/ImageSharp/Common/Memory/BufferSpan.cs b/src/ImageSharp/Common/Memory/BufferSpan.cs index c51c110be4..f8a5453a8d 100644 --- a/src/ImageSharp/Common/Memory/BufferSpan.cs +++ b/src/ImageSharp/Common/Memory/BufferSpan.cs @@ -6,6 +6,7 @@ namespace ImageSharp { using System; + using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -14,6 +15,19 @@ namespace ImageSharp /// internal static class BufferSpan { + /// + /// Fetches a from the beginning of the span. + /// + /// The value type + /// The span to fetch the vector from + /// A reference to the beginning of the span + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref Vector FetchVector(this BufferSpan span) + where T : struct + { + return ref Unsafe.As>(ref span.DangerousGetPinnableReference()); + } + /// /// Copy 'count' number of elements of the same type from 'source' to 'dest' /// diff --git a/src/ImageSharp/PixelFormats/Rgba32.PixelOperations.cs b/src/ImageSharp/PixelFormats/Rgba32.PixelOperations.cs index 9745d01338..372ac23922 100644 --- a/src/ImageSharp/PixelFormats/Rgba32.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/Rgba32.PixelOperations.cs @@ -61,38 +61,33 @@ namespace ImageSharp int unpackedRawCount = count * 4; - ref uint src = ref Unsafe.As(ref sourceColors.DangerousGetPinnableReference()); + ref uint bSource = ref Unsafe.As(ref sourceColors.DangerousGetPinnableReference()); + ref UnpackedRGBA bDestUnpacked = ref Unsafe.As(ref destVectors.DangerousGetPinnableReference()); + ref Vector bDestUint = ref Unsafe.As>(ref bDestUnpacked); + ref Vector bDestFloat = ref Unsafe.As>(ref bDestUnpacked); - using (Buffer tempBuf = new Buffer( - unpackedRawCount + Vector.Count)) + for (int i = 0; i < count; i++) { - uint[] temp = tempBuf.Array; - float[] fTemp = Unsafe.As(temp); - - ref UnpackedRGBA tempBase = ref Unsafe.As(ref tempBuf[0]); + uint sVal = Unsafe.Add(ref bSource, i); + ref UnpackedRGBA dst = ref Unsafe.Add(ref bDestUnpacked, i); - for (int i = 0; i < count; i++) - { - uint sVal = Unsafe.Add(ref src, i); - ref UnpackedRGBA dst = ref Unsafe.Add(ref tempBase, i); + // This call is the bottleneck now: + dst.Load(sVal); + } - // This call is the bottleneck now: - dst.Load(sVal); - } + int n = unpackedRawCount / vecSize; - for (int i = 0; i < unpackedRawCount; i += vecSize) - { - Vector vi = new Vector(temp, i); + for (int i = 0; i < n; i++) + { + Vector vi = Unsafe.Add(ref bDestUint, i); - vi &= mask; - vi |= magicInt; + vi &= mask; + vi |= magicInt; - Vector vf = Vector.AsVectorSingle(vi); - vf = (vf - magicFloat) * bVec; - vf.CopyTo(fTemp, i); - } + Vector vf = Vector.AsVectorSingle(vi); + vf = (vf - magicFloat) * bVec; - BufferSpan.Copy(tempBuf.Span.AsBytes(), destVectors.AsBytes(), unpackedRawCount * sizeof(uint)); + Unsafe.Add(ref bDestFloat, i) = vf; } } diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs index efec90c996..d363769d01 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs @@ -57,7 +57,7 @@ namespace ImageSharp.Benchmarks.Color.Bulk } } - public class PackFromXyzw_Color : PackFromXyzw + public class PackFromXyzw_Rgba32 : PackFromXyzw { } } \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs index e2c1ac7265..cd17975584 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs @@ -57,7 +57,7 @@ namespace ImageSharp.Benchmarks.Color.Bulk } } - public class ToVector4_Color : ToVector4 + public class ToVector4_Rgba32 : ToVector4 { } } \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs index 88dac21cdf..663f85fb7c 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs @@ -55,7 +55,7 @@ namespace ImageSharp.Benchmarks.Color.Bulk } } - public class ToXyz_Color : ToXyz + public class ToXyz_Rgba32 : ToXyz { } } \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs index 11545d3d95..7ac6211131 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs @@ -59,11 +59,11 @@ namespace ImageSharp.Benchmarks.Color.Bulk } } - public class ToXyzw_Color : ToXyzw + public class ToXyzw_Rgba32 : ToXyzw { } - public class ToXyzw_Argb : ToXyzw + public class ToXyzw_Argb32 : ToXyzw { } } diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/VectorFetching.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/VectorFetching.cs index 93f4095e96..e6d1a2fd01 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/VectorFetching.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/VectorFetching.cs @@ -86,5 +86,23 @@ namespace ImageSharp.Benchmarks.General.Vectorization a = a * v; } } + + [Benchmark] + public void FetchWithBufferSpanUtility() + { + Vector v = new Vector(this.testValue); + + BufferSpan span = new BufferSpan(this.data); + + ref Vector start = ref span.FetchVector(); + + int n = this.InputSize / Vector.Count; + + for (int i = 0; i < n; i++) + { + ref Vector a = ref Unsafe.Add(ref start, i); + a = a * v; + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colors/PixelOperationsTests.cs b/tests/ImageSharp.Tests/Colors/PixelOperationsTests.cs index 3d03927536..4deeb40e85 100644 --- a/tests/ImageSharp.Tests/Colors/PixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/Colors/PixelOperationsTests.cs @@ -12,7 +12,7 @@ namespace ImageSharp.Tests.Colors public class PixelOperationsTests { - public class Color32 : BulkPixelOperationsTests + public class Color32 : PixelOperationsTests { public Color32(ITestOutputHelper output) : base(output) @@ -20,7 +20,7 @@ namespace ImageSharp.Tests.Colors } // For 4.6 test runner MemberData does not work without redeclaring the public field in the derived test class: - //public static new TheoryData ArraySizesData => new TheoryData { 7, 16, 1111 }; + public static new TheoryData ArraySizesData => new TheoryData { 7, 16, 1111 }; [Fact] public void IsSpecialImplementation() @@ -60,7 +60,7 @@ namespace ImageSharp.Tests.Colors } } - public class Argb : BulkPixelOperationsTests + public class Argb : PixelOperationsTests { // For 4.6 test runner MemberData does not work without redeclaring the public field in the derived test class: public Argb(ITestOutputHelper output) @@ -68,7 +68,7 @@ namespace ImageSharp.Tests.Colors { } - //public static new TheoryData ArraySizesData => new TheoryData { 7, 16, 1111 }; + public static new TheoryData ArraySizesData => new TheoryData { 7, 16, 1111 }; } [Theory] @@ -80,10 +80,10 @@ namespace ImageSharp.Tests.Colors } } - public abstract class BulkPixelOperationsTests : MeasureFixture + public abstract class PixelOperationsTests : MeasureFixture where TPixel : struct, IPixel { - protected BulkPixelOperationsTests(ITestOutputHelper output) + protected PixelOperationsTests(ITestOutputHelper output) : base(output) { } diff --git a/tests/ImageSharp.Tests/Common/BufferSpanTests.cs b/tests/ImageSharp.Tests/Common/BufferSpanTests.cs index 3bc59df64a..e5fcbf9ffd 100644 --- a/tests/ImageSharp.Tests/Common/BufferSpanTests.cs +++ b/tests/ImageSharp.Tests/Common/BufferSpanTests.cs @@ -4,6 +4,7 @@ namespace ImageSharp.Tests.Common { using System; + using System.Numerics; using System.Runtime.CompilerServices; using ImageSharp.PixelFormats; @@ -25,6 +26,21 @@ namespace ImageSharp.Tests.Common } } + [Fact] + public void FetchVector() + { + float[] stuff = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; + + BufferSpan span = new BufferSpan(stuff); + + ref Vector v = ref span.FetchVector(); + + Assert.Equal(0, v[0]); + Assert.Equal(1, v[1]); + Assert.Equal(2, v[2]); + Assert.Equal(3, v[3]); + } + [Fact] public void AsBytes() {