diff --git a/src/ImageSharp/Colors/Color.BulkOperations.cs b/src/ImageSharp/Colors/Color.BulkOperations.cs index 154cd531d..5c040e04c 100644 --- a/src/ImageSharp/Colors/Color.BulkOperations.cs +++ b/src/ImageSharp/Colors/Color.BulkOperations.cs @@ -32,6 +32,10 @@ namespace ImageSharp /// /// http://stackoverflow.com/a/5362789 /// + /// TODO: We can replace this implementation in the future using new Vector API-s: + /// + /// https://github.com/dotnet/corefx/issues/15957 + /// /// internal static unsafe void ToVector4SimdAligned( BufferPointer sourceColors, @@ -56,8 +60,7 @@ namespace ImageSharp uint* srcEnd = src + count; using (PinnedBuffer tempBuf = new PinnedBuffer( - unpackedRawCount + Vector.Count, - PixelDataPool.Dirty)) + unpackedRawCount + Vector.Count)) { uint* tPtr = (uint*)tempBuf.Pointer; uint[] temp = tempBuf.Array; @@ -66,7 +69,7 @@ namespace ImageSharp for (; src < srcEnd; src++, dst++) { - // TODO: This is the bottleneck now. We can improve it with future Vector API-s (https://github.com/dotnet/corefx/issues/15957) + // This call is the bottleneck now: dst->Load(*src); } diff --git a/src/ImageSharp/Common/Memory/BufferPointer{T}.cs b/src/ImageSharp/Common/Memory/BufferPointer{T}.cs index 4e7a40078..441f6b8ce 100644 --- a/src/ImageSharp/Common/Memory/BufferPointer{T}.cs +++ b/src/ImageSharp/Common/Memory/BufferPointer{T}.cs @@ -130,7 +130,14 @@ namespace ImageSharp [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Clear(int count) { - + if (count < 256) + { + Unsafe.InitBlock((void*)this.PointerAtOffset, 0, BufferPointer.USizeOf(count)); + } + else + { + System.Array.Clear(this.Array, this.Offset, count); + } } } } \ No newline at end of file diff --git a/src/ImageSharp/Common/Memory/PinnedBuffer{T}.cs b/src/ImageSharp/Common/Memory/PinnedBuffer{T}.cs index 6e0d1fcc6..2d3d44dda 100644 --- a/src/ImageSharp/Common/Memory/PinnedBuffer{T}.cs +++ b/src/ImageSharp/Common/Memory/PinnedBuffer{T}.cs @@ -24,32 +24,23 @@ namespace ImageSharp private GCHandle handle; /// - /// The if the is pooled. + /// A value indicating wheter should be returned to + /// when disposing this instance. /// - private PixelDataPool pool; + private bool isPoolingOwner; /// /// Initializes a new instance of the class. /// /// The desired count of elements. (Minimum size for ) - /// The to be used to rent the data. - public PinnedBuffer(int count, PixelDataPool pool) + public PinnedBuffer(int count) { this.Count = count; - this.pool = pool; - this.Array = this.pool.Rent(count); + this.Array = PixelDataPool.Rent(count); + this.isPoolingOwner = true; this.Pin(); } - /// - /// Initializes a new instance of the class. - /// - /// The desired count of elements. (Minimum size for ) - public PinnedBuffer(int count) - : this(count, PixelDataPool.Clean) - { - } - /// /// Initializes a new instance of the class. /// @@ -58,7 +49,7 @@ namespace ImageSharp { this.Count = array.Length; this.Array = array; - this.pool = null; + this.isPoolingOwner = false; this.Pin(); } @@ -76,7 +67,7 @@ namespace ImageSharp this.Count = count; this.Array = array; - this.pool = null; + this.isPoolingOwner = false; this.Pin(); } @@ -153,8 +144,12 @@ namespace ImageSharp this.IsDisposedOrLostArrayOwnership = true; this.UnPin(); - this.pool?.Return(this.Array); - this.pool = null; + if (this.isPoolingOwner) + { + PixelDataPool.Return(this.Array); + } + + this.isPoolingOwner = false; this.Array = null; this.Count = 0; @@ -178,9 +173,19 @@ namespace ImageSharp this.UnPin(); T[] array = this.Array; this.Array = null; + this.isPoolingOwner = false; return array; } + /// + /// Clears the buffer, filling elements between 0 and -1 with default(T) + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Clear() + { + this.Slice().Clear(this.Count); + } + /// /// Pins . /// diff --git a/src/ImageSharp/Common/Memory/PixelDataPool{T}.cs b/src/ImageSharp/Common/Memory/PixelDataPool{T}.cs index a01a941bc..dcd031f6e 100644 --- a/src/ImageSharp/Common/Memory/PixelDataPool{T}.cs +++ b/src/ImageSharp/Common/Memory/PixelDataPool{T}.cs @@ -15,63 +15,32 @@ namespace ImageSharp public class PixelDataPool where T : struct { - /// - /// The which will be always cleared. - /// - private static readonly ArrayPool CleanPool = ArrayPool.Create(CalculateMaxArrayLength(), 50); - /// /// The which is not kept clean. /// - private static readonly ArrayPool DirtyPool = ArrayPool.Create(CalculateMaxArrayLength(), 50); - - /// - /// The backing - /// - private ArrayPool arrayPool; - - /// - /// A value indicating whether clearArray is requested on . - /// - private bool clearArray; - - private PixelDataPool(ArrayPool arrayPool, bool clearArray) - { - this.clearArray = clearArray; - this.arrayPool = arrayPool; - } - - /// - /// Gets the which will always return arrays initialized to default(T) - /// - public static PixelDataPool Clean { get; } = new PixelDataPool(CleanPool, true); - - /// - /// Gets the which does not keep the arrays clean on Rent/Return. - /// - public static PixelDataPool Dirty { get; } = new PixelDataPool(DirtyPool, false); + private static readonly ArrayPool ArrayPool = ArrayPool.Create(CalculateMaxArrayLength(), 50); /// /// Rents the pixel array from the pool. /// /// The minimum length of the array to return. /// The - public T[] Rent(int minimumLength) + public static T[] Rent(int minimumLength) { - return CleanPool.Rent(minimumLength); + return ArrayPool.Rent(minimumLength); } /// /// Returns the rented pixel array back to the pool. /// /// The array to return to the buffer pool. - public void Return(T[] array) + public static void Return(T[] array) { - CleanPool.Return(array, this.clearArray); + ArrayPool.Return(array); } /// - /// Heuristically calculates a reasonable maxArrayLength value for the backing . + /// Heuristically calculates a reasonable maxArrayLength value for the backing . /// /// The maxArrayLength value internal static int CalculateMaxArrayLength() diff --git a/src/ImageSharp/Image/ImageBase{TColor}.cs b/src/ImageSharp/Image/ImageBase{TColor}.cs index 7d7fc843f..7afc27a6d 100644 --- a/src/ImageSharp/Image/ImageBase{TColor}.cs +++ b/src/ImageSharp/Image/ImageBase{TColor}.cs @@ -60,6 +60,7 @@ namespace ImageSharp { this.Configuration = configuration ?? Configuration.Default; this.InitPixels(width, height); + this.ClearPixels(); } /// @@ -221,7 +222,7 @@ namespace ImageSharp /// private void RentPixels() { - this.pixelBuffer = PixelDataPool.Clean.Rent(this.Width * this.Height); + this.pixelBuffer = PixelDataPool.Rent(this.Width * this.Height); } /// @@ -229,8 +230,13 @@ namespace ImageSharp /// private void ReturnPixels() { - PixelDataPool.Clean.Return(this.pixelBuffer); + PixelDataPool.Return(this.pixelBuffer); this.pixelBuffer = null; } + + private void ClearPixels() + { + Array.Clear(this.pixelBuffer, 0, this.Width * this.Height); + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Sandbox46/Program.cs b/tests/ImageSharp.Sandbox46/Program.cs index c361fd6a1..467663a53 100644 --- a/tests/ImageSharp.Sandbox46/Program.cs +++ b/tests/ImageSharp.Sandbox46/Program.cs @@ -46,7 +46,7 @@ namespace ImageSharp.Sandbox46 private static void RunToVector4ProfilingTest() { - BulkPixelOperationsTests.Color tests = new BulkPixelOperationsTests.Color(); + BulkPixelOperationsTests.Color tests = new BulkPixelOperationsTests.Color(new ConsoleOutput()); tests.Benchmark_ToVector4(); } diff --git a/tests/ImageSharp.Tests/Common/BufferPointerTests.cs b/tests/ImageSharp.Tests/Common/BufferPointerTests.cs index 39d76d937..c82b63f11 100644 --- a/tests/ImageSharp.Tests/Common/BufferPointerTests.cs +++ b/tests/ImageSharp.Tests/Common/BufferPointerTests.cs @@ -146,6 +146,11 @@ namespace ImageSharp.Tests.Common // Act: ap.Clear(count); + + Assert.NotEqual(default(Foo), array[offset-1]); + Assert.Equal(default(Foo), array[offset]); + Assert.Equal(default(Foo), array[offset + count-1]); + Assert.NotEqual(default(Foo), array[offset + count]); } } diff --git a/tests/ImageSharp.Tests/Common/PinnedBufferTests.cs b/tests/ImageSharp.Tests/Common/PinnedBufferTests.cs index 65077ae7f..3688763b9 100644 --- a/tests/ImageSharp.Tests/Common/PinnedBufferTests.cs +++ b/tests/ImageSharp.Tests/Common/PinnedBufferTests.cs @@ -47,6 +47,21 @@ } } + [Theory] + [InlineData(42)] + [InlineData(1111)] + public void Clear(int count) + { + Foo[] a = { new Foo() { A = 1, B = 2 }, new Foo() { A = 3, B = 4 } }; + using (PinnedBuffer buffer = new PinnedBuffer(a)) + { + buffer.Clear(); + + Assert.Equal(default(Foo), a[0]); + Assert.Equal(default(Foo), a[1]); + } + } + [Fact] public void Dispose() { diff --git a/tests/ImageSharp.Tests/Common/PixelDataPoolTests.cs b/tests/ImageSharp.Tests/Common/PixelDataPoolTests.cs index db560ba6b..ea747af7d 100644 --- a/tests/ImageSharp.Tests/Common/PixelDataPoolTests.cs +++ b/tests/ImageSharp.Tests/Common/PixelDataPoolTests.cs @@ -15,17 +15,10 @@ namespace ImageSharp.Tests /// public class PixelDataPoolTests { - private static PixelDataPool GetPool(bool clean) - { - return clean ? PixelDataPool.Clean : PixelDataPool.Dirty; - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void PixelDataPoolRentsMinimumSize(bool clean) + [Fact] + public void PixelDataPoolRentsMinimumSize() { - Color[] pixels = GetPool(clean).Rent(1024); + Color[] pixels = PixelDataPool.Rent(1024); Assert.True(pixels.Length >= 1024); } @@ -35,31 +28,29 @@ namespace ImageSharp.Tests { for (int i = 16; i < 1024; i += 16) { - Color[] pixels = PixelDataPool.Clean.Rent(i); + Color[] pixels = PixelDataPool.Rent(i); Assert.True(pixels.All(p => p == default(Color))); - PixelDataPool.Clean.Return(pixels); + PixelDataPool.Return(pixels); } for (int i = 16; i < 1024; i += 16) { - Color[] pixels = PixelDataPool.Clean.Rent(i); + Color[] pixels = PixelDataPool.Rent(i); Assert.True(pixels.All(p => p == default(Color))); - PixelDataPool.Clean.Return(pixels); + PixelDataPool.Return(pixels); } } - [Theory] - [InlineData(false)] - [InlineData(true)] - public void PixelDataPoolDoesNotThrowWhenReturningNonPooled(bool clean) + [Fact] + public void PixelDataPoolDoesNotThrowWhenReturningNonPooled() { Color[] pixels = new Color[1024]; - GetPool(clean).Return(pixels); + PixelDataPool.Return(pixels); Assert.True(pixels.Length >= 1024); } @@ -67,7 +58,7 @@ namespace ImageSharp.Tests [Fact] public void PixelDataPool_Clean_CleansRentedArray() { - Color[] pixels = PixelDataPool.Clean.Rent(256); + Color[] pixels = PixelDataPool.Rent(256); for (int i = 0; i < pixels.Length; i++) { @@ -76,7 +67,7 @@ namespace ImageSharp.Tests Assert.True(pixels.All(p => p == Color.Azure)); - PixelDataPool.Clean.Return(pixels); + PixelDataPool.Return(pixels); Assert.True(pixels.All(p => p == default(Color))); } @@ -95,7 +86,7 @@ namespace ImageSharp.Tests [Fact] public void RentNonIPixelData() { - byte[] data = PixelDataPool.Clean.Rent(16384); + byte[] data = PixelDataPool.Rent(16384); Assert.True(data.Length >= 16384); }