diff --git a/src/ImageSharp.Drawing/Brushes/ImageBrush{TColor}.cs b/src/ImageSharp.Drawing/Brushes/ImageBrush{TColor}.cs index ace929bd6a..b68b02eff1 100644 --- a/src/ImageSharp.Drawing/Brushes/ImageBrush{TColor}.cs +++ b/src/ImageSharp.Drawing/Brushes/ImageBrush{TColor}.cs @@ -117,7 +117,7 @@ namespace ImageSharp.Drawing.Brushes { Guard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth)); - using (PinnedBuffer buffer = new PinnedBuffer(scanlineBuffer)) + using (Buffer buffer = new Buffer(scanlineBuffer)) { BufferSpan slice = buffer.Slice(offset); diff --git a/src/ImageSharp.Drawing/Brushes/PatternBrush{TColor}.cs b/src/ImageSharp.Drawing/Brushes/PatternBrush{TColor}.cs index df492a764e..1af58bc62f 100644 --- a/src/ImageSharp.Drawing/Brushes/PatternBrush{TColor}.cs +++ b/src/ImageSharp.Drawing/Brushes/PatternBrush{TColor}.cs @@ -150,7 +150,7 @@ namespace ImageSharp.Drawing.Brushes { Guard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth)); - using (PinnedBuffer buffer = new PinnedBuffer(scanlineBuffer)) + using (Buffer buffer = new Buffer(scanlineBuffer)) { BufferSpan slice = buffer.Slice(offset); diff --git a/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs b/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs index 46444e5503..0ef11e1611 100644 --- a/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs +++ b/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs @@ -55,7 +55,7 @@ namespace ImageSharp.Drawing.Processors { DebugGuard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth)); - using (PinnedBuffer buffer = new PinnedBuffer(scanlineBuffer)) + using (Buffer buffer = new Buffer(scanlineBuffer)) { BufferSpan slice = buffer.Slice(offset); diff --git a/src/ImageSharp.Drawing/Brushes/RecolorBrush{TColor}.cs b/src/ImageSharp.Drawing/Brushes/RecolorBrush{TColor}.cs index 257eeb3ae5..4e3cd01ae8 100644 --- a/src/ImageSharp.Drawing/Brushes/RecolorBrush{TColor}.cs +++ b/src/ImageSharp.Drawing/Brushes/RecolorBrush{TColor}.cs @@ -141,7 +141,7 @@ namespace ImageSharp.Drawing.Brushes { Guard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth)); - using (PinnedBuffer buffer = new PinnedBuffer(scanlineBuffer)) + using (Buffer buffer = new Buffer(scanlineBuffer)) { BufferSpan slice = buffer.Slice(offset); diff --git a/src/ImageSharp.Drawing/Brushes/SolidBrush{TColor}.cs b/src/ImageSharp.Drawing/Brushes/SolidBrush{TColor}.cs index 125b07bcac..74c7081b3b 100644 --- a/src/ImageSharp.Drawing/Brushes/SolidBrush{TColor}.cs +++ b/src/ImageSharp.Drawing/Brushes/SolidBrush{TColor}.cs @@ -89,7 +89,7 @@ namespace ImageSharp.Drawing.Brushes { Guard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth)); - using (PinnedBuffer buffer = new PinnedBuffer(scanlineBuffer)) + using (Buffer buffer = new Buffer(scanlineBuffer)) { BufferSpan slice = buffer.Slice(offset); diff --git a/src/ImageSharp/Colors/Color.BulkOperations.cs b/src/ImageSharp/Colors/Color.BulkOperations.cs index 462d0c0529..e67e29356e 100644 --- a/src/ImageSharp/Colors/Color.BulkOperations.cs +++ b/src/ImageSharp/Colors/Color.BulkOperations.cs @@ -64,7 +64,7 @@ namespace ImageSharp ref uint src = ref Unsafe.As(ref sourceColors.DangerousGetPinnableReference()); - using (PinnedBuffer tempBuf = new PinnedBuffer( + using (Buffer tempBuf = new Buffer( unpackedRawCount + Vector.Count)) { uint[] temp = tempBuf.Array; diff --git a/src/ImageSharp/Common/Memory/PinnedBuffer{T}.cs b/src/ImageSharp/Common/Memory/Buffer.cs similarity index 72% rename from src/ImageSharp/Common/Memory/PinnedBuffer{T}.cs rename to src/ImageSharp/Common/Memory/Buffer.cs index 665e92e097..c26b8ea180 100644 --- a/src/ImageSharp/Common/Memory/PinnedBuffer{T}.cs +++ b/src/ImageSharp/Common/Memory/Buffer.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -11,13 +11,18 @@ namespace ImageSharp using System.Runtime.InteropServices; /// - /// Manages a pinned buffer of value type objects as a Disposable resource. + /// Manages a buffer of value type objects as a Disposable resource. /// The backing array is either pooled or comes from the outside. /// /// The value type. - internal class PinnedBuffer : IDisposable + internal class Buffer : IDisposable where T : struct { + /// + /// A pointer to the first element of when pinned. + /// + private IntPtr pointer; + /// /// A handle that allows to access the managed as an unmanaged memory by pinning. /// @@ -25,40 +30,38 @@ namespace ImageSharp /// /// A value indicating wheter should be returned to - /// when disposing this instance. + /// when disposing this instance. /// private bool isPoolingOwner; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The desired count of elements. (Minimum size for ) - public PinnedBuffer(int length) + public Buffer(int length) { this.Length = length; this.Array = PixelDataPool.Rent(length); this.isPoolingOwner = true; - this.Pin(); } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The array to pin. - public PinnedBuffer(T[] array) + public Buffer(T[] array) { this.Length = array.Length; this.Array = array; this.isPoolingOwner = false; - this.Pin(); } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The array to pin. /// The count of "relevant" elements in 'array'. - public PinnedBuffer(T[] array, int length) + public Buffer(T[] array, int length) { if (array.Length < length) { @@ -68,19 +71,18 @@ namespace ImageSharp this.Length = length; this.Array = array; this.isPoolingOwner = false; - this.Pin(); } /// - /// Finalizes an instance of the class. + /// Finalizes an instance of the class. /// - ~PinnedBuffer() + ~Buffer() { this.UnPin(); } /// - /// Gets a value indicating whether this instance is disposed, or has lost ownership of . + /// Gets a value indicating whether this instance is disposed, or has lost ownership of . /// public bool IsDisposedOrLostArrayOwnership { get; private set; } @@ -94,11 +96,6 @@ namespace ImageSharp /// public T[] Array { get; private set; } - /// - /// Gets a pointer to the pinned . - /// - public IntPtr Pointer { get; private set; } - /// /// Gets a to the backing buffer. /// @@ -109,37 +106,35 @@ namespace ImageSharp /// /// The index /// The reference to the specified element - public unsafe ref T this[int index] + public ref T this[int index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { DebugGuard.MustBeLessThan(index, this.Length, nameof(index)); - - byte* ptr = (byte*)this.Pointer + BufferSpan.SizeOf(index); - return ref Unsafe.AsRef(ptr); + return ref this.Array[index]; } } /// - /// Converts to an . + /// Converts to an . /// - /// The to convert. + /// The to convert. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator BufferSpan(PinnedBuffer buffer) + public static implicit operator BufferSpan(Buffer buffer) { return new BufferSpan(buffer.Array, 0, buffer.Length); } /// - /// Creates a clean instance of initializing it's elements with 'default(T)'. + /// Creates a clean instance of initializing it's elements with 'default(T)'. /// /// The desired count of elements. (Minimum size for ) - /// The instance + /// The instance [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static PinnedBuffer CreateClean(int count) + public static Buffer CreateClean(int count) { - PinnedBuffer buffer = new PinnedBuffer(count); + Buffer buffer = new Buffer(count); buffer.Clear(); return buffer; } @@ -168,7 +163,7 @@ namespace ImageSharp } /// - /// Disposes the instance by unpinning the array, and returning the pooled buffer when necessary. + /// Disposes the instance by unpinning the array, and returning the pooled buffer when necessary. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Dispose() @@ -199,11 +194,11 @@ namespace ImageSharp /// /// The unpinned [MethodImpl(MethodImplOptions.AggressiveInlining)] - public T[] UnPinAndTakeArrayOwnership() + public T[] TakeArrayOwnership() { if (this.IsDisposedOrLostArrayOwnership) { - throw new InvalidOperationException("UnPinAndTakeArrayOwnership() is invalid: either PinnedBuffer is disposed or UnPinAndTakeArrayOwnership() has been called multiple times!"); + throw new InvalidOperationException("TakeArrayOwnership() is invalid: either Buffer is disposed or TakeArrayOwnership() has been called multiple times!"); } this.IsDisposedOrLostArrayOwnership = true; @@ -220,17 +215,29 @@ namespace ImageSharp [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Clear() { - ((BufferSpan)this).Clear(); + this.Span.Clear(); } /// /// Pins . /// + /// The pinned pointer [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void Pin() + public IntPtr Pin() { - this.handle = GCHandle.Alloc(this.Array, GCHandleType.Pinned); - this.Pointer = this.handle.AddrOfPinnedObject(); + if (this.IsDisposedOrLostArrayOwnership) + { + throw new InvalidOperationException( + "Pin() is invalid on a buffer with IsDisposedOrLostArrayOwnership == true!"); + } + + if (this.pointer == IntPtr.Zero) + { + this.handle = GCHandle.Alloc(this.Array, GCHandleType.Pinned); + this.pointer = this.handle.AddrOfPinnedObject(); + } + + return this.pointer; } /// @@ -239,13 +246,13 @@ namespace ImageSharp [MethodImpl(MethodImplOptions.AggressiveInlining)] private void UnPin() { - if (this.Pointer == IntPtr.Zero || !this.handle.IsAllocated) + if (this.pointer == IntPtr.Zero || !this.handle.IsAllocated) { return; } this.handle.Free(); - this.Pointer = IntPtr.Zero; + this.pointer = IntPtr.Zero; } } } \ No newline at end of file diff --git a/src/ImageSharp/Common/Memory/PinnedImageBuffer{T}.cs b/src/ImageSharp/Common/Memory/Buffer2D.cs similarity index 69% rename from src/ImageSharp/Common/Memory/PinnedImageBuffer{T}.cs rename to src/ImageSharp/Common/Memory/Buffer2D.cs index 3ff174c5dd..c4eb507007 100644 --- a/src/ImageSharp/Common/Memory/PinnedImageBuffer{T}.cs +++ b/src/ImageSharp/Common/Memory/Buffer2D.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -9,19 +9,19 @@ namespace ImageSharp using System.Runtime.CompilerServices; /// - /// Represents a pinned buffer of value type objects + /// Represents a buffer of value type objects /// interpreted as a 2D region of x elements. /// /// The value type. - internal class PinnedImageBuffer : PinnedBuffer, IPinnedImageBuffer + internal class Buffer2D : Buffer, IBuffer2D where T : struct { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The number of elements in a row /// The number of rows - public PinnedImageBuffer(int width, int height) + public Buffer2D(int width, int height) : base(width * height) { this.Width = width; @@ -29,12 +29,12 @@ namespace ImageSharp } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The array to pin /// The number of elements in a row /// The number of rows - public PinnedImageBuffer(T[] array, int width, int height) + public Buffer2D(T[] array, int width, int height) : base(array, width * height) { this.Width = width; @@ -63,14 +63,14 @@ namespace ImageSharp } /// - /// Creates a clean instance of initializing it's elements with 'default(T)'. + /// Creates a clean instance of initializing it's elements with 'default(T)'. /// /// The number of elements in a row /// The number of rows - /// The instance - public static PinnedImageBuffer CreateClean(int width, int height) + /// The instance + public static Buffer2D CreateClean(int width, int height) { - PinnedImageBuffer buffer = new PinnedImageBuffer(width, height); + Buffer2D buffer = new Buffer2D(width, height); buffer.Clear(); return buffer; } diff --git a/src/ImageSharp/Common/Memory/BufferSpan{T}.cs b/src/ImageSharp/Common/Memory/BufferSpan{T}.cs index d61f7f77c8..1b0bef314d 100644 --- a/src/ImageSharp/Common/Memory/BufferSpan{T}.cs +++ b/src/ImageSharp/Common/Memory/BufferSpan{T}.cs @@ -12,7 +12,7 @@ namespace ImageSharp /// /// Represents a contiguous region of a pinned managed array. - /// The array is usually owned by a instance. + /// The array is usually owned by a instance. /// /// /// is very similar to corefx System.Span<T>, and we try to maintain a compatible API. diff --git a/src/ImageSharp/Common/Memory/IPinnedImageBuffer{T}.cs b/src/ImageSharp/Common/Memory/IBuffer2D.cs similarity index 86% rename from src/ImageSharp/Common/Memory/IPinnedImageBuffer{T}.cs rename to src/ImageSharp/Common/Memory/IBuffer2D.cs index 374cbed997..1ba08e49f5 100644 --- a/src/ImageSharp/Common/Memory/IPinnedImageBuffer{T}.cs +++ b/src/ImageSharp/Common/Memory/IBuffer2D.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -10,7 +10,7 @@ namespace ImageSharp /// interpreted as a 2D region of x elements. /// /// The value type. - internal interface IPinnedImageBuffer + internal interface IBuffer2D where T : struct { /// diff --git a/src/ImageSharp/Common/Memory/PinnedImageBufferExtensions.cs b/src/ImageSharp/Common/Memory/PinnedImageBufferExtensions.cs index fcd5b3831e..f47bc666ee 100644 --- a/src/ImageSharp/Common/Memory/PinnedImageBufferExtensions.cs +++ b/src/ImageSharp/Common/Memory/PinnedImageBufferExtensions.cs @@ -9,7 +9,7 @@ namespace ImageSharp using System.Runtime.CompilerServices; /// - /// Defines extension methods for . + /// Defines extension methods for . /// internal static class PinnedImageBufferExtensions { @@ -22,7 +22,7 @@ namespace ImageSharp /// The element type /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static BufferSpan GetRowSpan(this IPinnedImageBuffer buffer, int x, int y) + public static BufferSpan GetRowSpan(this IBuffer2D buffer, int x, int y) where T : struct { return buffer.Span.Slice((y * buffer.Width) + x, buffer.Width - x); @@ -36,7 +36,7 @@ namespace ImageSharp /// The element type /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static BufferSpan GetRowSpan(this IPinnedImageBuffer buffer, int y) + public static BufferSpan GetRowSpan(this IBuffer2D buffer, int y) where T : struct { return buffer.Span.Slice(y * buffer.Width, buffer.Width); diff --git a/src/ImageSharp/Image/PixelAccessor{TColor}.cs b/src/ImageSharp/Image/PixelAccessor{TColor}.cs index 5ff2911d4e..fb3613adf4 100644 --- a/src/ImageSharp/Image/PixelAccessor{TColor}.cs +++ b/src/ImageSharp/Image/PixelAccessor{TColor}.cs @@ -15,7 +15,7 @@ namespace ImageSharp /// Provides per-pixel access to generic pixels. /// /// The pixel format. - public sealed class PixelAccessor : IDisposable, IPinnedImageBuffer + public sealed class PixelAccessor : IDisposable, IBuffer2D where TColor : struct, IPixel { /// @@ -30,9 +30,9 @@ namespace ImageSharp private bool isDisposed; /// - /// The containing the pixel data. + /// The containing the pixel data. /// - private PinnedImageBuffer pixelBuffer; + private Buffer2D pixelBuffer; /// /// Initializes a new instance of the class. @@ -54,7 +54,7 @@ namespace ImageSharp /// The width of the image represented by the pixel buffer. /// The height of the image represented by the pixel buffer. public PixelAccessor(int width, int height) - : this(width, height, PinnedImageBuffer.CreateClean(width, height)) + : this(width, height, Buffer2D.CreateClean(width, height)) { } @@ -64,7 +64,7 @@ namespace ImageSharp /// The width of the image represented by the pixel buffer. /// The height of the image represented by the pixel buffer. /// The pixel buffer. - private PixelAccessor(int width, int height, PinnedImageBuffer pixels) + private PixelAccessor(int width, int height, Buffer2D pixels) { Guard.NotNull(pixels, nameof(pixels)); Guard.MustBeGreaterThan(width, 0, nameof(width)); @@ -114,7 +114,7 @@ namespace ImageSharp public ParallelOptions ParallelOptions { get; } /// - BufferSpan IPinnedImageBuffer.Span => this.pixelBuffer; + BufferSpan IBuffer2D.Span => this.pixelBuffer; private static BulkPixelOperations Operations => BulkPixelOperations.Instance; @@ -239,7 +239,7 @@ namespace ImageSharp /// If is true then caller is responsible for ensuring is called. internal TColor[] ReturnCurrentPixelsAndReplaceThemInternally(int width, int height, TColor[] pixels) { - TColor[] oldPixels = this.pixelBuffer.UnPinAndTakeArrayOwnership(); + TColor[] oldPixels = this.pixelBuffer.TakeArrayOwnership(); this.SetPixelBufferUnsafe(width, height, pixels); return oldPixels; } @@ -410,7 +410,7 @@ namespace ImageSharp private void SetPixelBufferUnsafe(int width, int height, TColor[] pixels) { - this.SetPixelBufferUnsafe(width, height, new PinnedImageBuffer(pixels, width, height)); + this.SetPixelBufferUnsafe(width, height, new Buffer2D(pixels, width, height)); } /// @@ -419,7 +419,7 @@ namespace ImageSharp /// The width. /// The height. /// The pixel buffer - private void SetPixelBufferUnsafe(int width, int height, PinnedImageBuffer pixels) + private void SetPixelBufferUnsafe(int width, int height, Buffer2D pixels) { this.pixelBuffer = pixels; diff --git a/src/ImageSharp/Image/PixelArea{TColor}.cs b/src/ImageSharp/Image/PixelArea{TColor}.cs index 87e6792058..176eb0a160 100644 --- a/src/ImageSharp/Image/PixelArea{TColor}.cs +++ b/src/ImageSharp/Image/PixelArea{TColor}.cs @@ -30,7 +30,7 @@ namespace ImageSharp /// /// The underlying buffer containing the raw pixel data. /// - private PinnedBuffer byteBuffer; + private Buffer byteBuffer; /// /// Initializes a new instance of the class. @@ -66,7 +66,7 @@ namespace ImageSharp this.RowStride = width * GetComponentCount(componentOrder); this.Length = bytes.Length; // TODO: Is this the right value for Length? - this.byteBuffer = new PinnedBuffer(bytes); + this.byteBuffer = new Buffer(bytes); } /// @@ -116,7 +116,7 @@ namespace ImageSharp this.RowStride = (width * GetComponentCount(componentOrder)) + padding; this.Length = this.RowStride * height; - this.byteBuffer = new PinnedBuffer(this.Length); + this.byteBuffer = new Buffer(this.Length); } /// diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs index 886c055692..4c43d654d0 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs @@ -107,7 +107,7 @@ namespace ImageSharp.Processing.Processors /// The column position /// The weighted sum [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ComputeWeightedColumnSum(PinnedImageBuffer firstPassPixels, int x) + public Vector4 ComputeWeightedColumnSum(Buffer2D firstPassPixels, int x) { ref float verticalValues = ref this.Ptr; int left = this.Left; @@ -131,7 +131,7 @@ namespace ImageSharp.Processing.Processors /// internal class WeightsBuffer : IDisposable { - private PinnedImageBuffer dataBuffer; + private Buffer2D dataBuffer; /// /// Initializes a new instance of the class. @@ -140,7 +140,7 @@ namespace ImageSharp.Processing.Processors /// The size of the destination window public WeightsBuffer(int sourceSize, int destinationSize) { - this.dataBuffer = PinnedImageBuffer.CreateClean(sourceSize, destinationSize); + this.dataBuffer = Buffer2D.CreateClean(sourceSize, destinationSize); this.Weights = new WeightsWindow[destinationSize]; } diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs index 944e245ac8..08d96e283c 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs @@ -106,7 +106,7 @@ namespace ImageSharp.Processing.Processors using (PixelAccessor targetPixels = new PixelAccessor(width, height)) { using (PixelAccessor sourcePixels = source.Lock()) - using (PinnedImageBuffer firstPassPixels = new PinnedImageBuffer(width, source.Height)) + using (Buffer2D firstPassPixels = new Buffer2D(width, source.Height)) { firstPassPixels.Clear(); @@ -117,7 +117,7 @@ namespace ImageSharp.Processing.Processors y => { // TODO: Without Parallel.For() this buffer object could be reused: - using (PinnedBuffer tempRowBuffer = new PinnedBuffer(sourcePixels.Width)) + using (Buffer tempRowBuffer = new Buffer(sourcePixels.Width)) { BufferSpan sourceRow = sourcePixels.GetRowSpan(y); diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4ReferenceVsPointer.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4ReferenceVsPointer.cs index e912ea29f6..f02cf56639 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4ReferenceVsPointer.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4ReferenceVsPointer.cs @@ -14,9 +14,9 @@ /// public unsafe class PackFromVector4ReferenceVsPointer { - private PinnedBuffer destination; + private Buffer destination; - private PinnedBuffer source; + private Buffer source; [Params(16, 128, 1024)] public int Count { get; set; } @@ -24,8 +24,10 @@ [Setup] public void Setup() { - this.destination = new PinnedBuffer(this.Count); - this.source = new PinnedBuffer(this.Count * 4); + this.destination = new Buffer(this.Count); + this.source = new Buffer(this.Count * 4); + this.source.Pin(); + this.destination.Pin(); } [Cleanup] @@ -38,8 +40,8 @@ [Benchmark(Baseline = true)] public void PackUsingPointers() { - Vector4* sp = (Vector4*)this.source.Pointer; - byte* dp = (byte*)this.destination.Pointer; + Vector4* sp = (Vector4*)this.source.Pin(); + byte* dp = (byte*)this.destination.Pin(); int count = this.Count; int size = sizeof(ImageSharp.Color); diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs index 1c541d28b3..33969b8fba 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs @@ -8,9 +8,9 @@ namespace ImageSharp.Benchmarks.Color.Bulk public abstract class PackFromXyzw where TColor : struct, IPixel { - private PinnedBuffer destination; + private Buffer destination; - private PinnedBuffer source; + private Buffer source; [Params(16, 128, 1024)] public int Count { get; set; } @@ -18,8 +18,8 @@ namespace ImageSharp.Benchmarks.Color.Bulk [Setup] public void Setup() { - this.destination = new PinnedBuffer(this.Count); - this.source = new PinnedBuffer(this.Count * 4); + this.destination = new Buffer(this.Count); + this.source = new Buffer(this.Count * 4); } [Cleanup] diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs index b48eaa35af..4cabfc01f4 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs @@ -8,9 +8,9 @@ namespace ImageSharp.Benchmarks.Color.Bulk public abstract class ToVector4 where TColor : struct, IPixel { - private PinnedBuffer source; + private Buffer source; - private PinnedBuffer destination; + private Buffer destination; [Params(64, 300, 1024)] public int Count { get; set; } @@ -18,8 +18,8 @@ namespace ImageSharp.Benchmarks.Color.Bulk [Setup] public void Setup() { - this.source = new PinnedBuffer(this.Count); - this.destination = new PinnedBuffer(this.Count); + this.source = new Buffer(this.Count); + this.destination = new Buffer(this.Count); } [Cleanup] diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs index bc59dba4eb..f6ae4256db 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs @@ -8,9 +8,9 @@ namespace ImageSharp.Benchmarks.Color.Bulk public abstract class ToXyz where TColor : struct, IPixel { - private PinnedBuffer source; + private Buffer source; - private PinnedBuffer destination; + private Buffer destination; [Params(16, 128, 1024)] public int Count { get; set; } @@ -18,8 +18,8 @@ namespace ImageSharp.Benchmarks.Color.Bulk [Setup] public void Setup() { - this.source = new PinnedBuffer(this.Count); - this.destination = new PinnedBuffer(this.Count * 3); + this.source = new Buffer(this.Count); + this.destination = new Buffer(this.Count * 3); } [Cleanup] diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs index a4ec6f6dc3..05fc4094e4 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs @@ -13,9 +13,9 @@ namespace ImageSharp.Benchmarks.Color.Bulk public abstract class ToXyzw where TColor : struct, IPixel { - private PinnedBuffer source; + private Buffer source; - private PinnedBuffer destination; + private Buffer destination; [Params(16, 128, 1024)] public int Count { get; set; } @@ -23,8 +23,8 @@ namespace ImageSharp.Benchmarks.Color.Bulk [Setup] public void Setup() { - this.source = new PinnedBuffer(this.Count); - this.destination = new PinnedBuffer(this.Count * 4); + this.source = new Buffer(this.Count); + this.destination = new Buffer(this.Count * 4); } [Cleanup] diff --git a/tests/ImageSharp.Benchmarks/General/ClearBuffer.cs b/tests/ImageSharp.Benchmarks/General/ClearBuffer.cs index 9aa836de59..e2f96f191c 100644 --- a/tests/ImageSharp.Benchmarks/General/ClearBuffer.cs +++ b/tests/ImageSharp.Benchmarks/General/ClearBuffer.cs @@ -11,7 +11,7 @@ namespace ImageSharp.Benchmarks.General public unsafe class ClearBuffer { - private PinnedBuffer buffer; + private Buffer buffer; [Params(32, 128, 512)] public int Count { get; set; } @@ -19,7 +19,7 @@ namespace ImageSharp.Benchmarks.General [Setup] public void Setup() { - this.buffer = new PinnedBuffer(this.Count); + this.buffer = new Buffer(this.Count); } [Cleanup] @@ -37,7 +37,7 @@ namespace ImageSharp.Benchmarks.General [Benchmark] public void Unsafe_InitBlock() { - Unsafe.InitBlock((void*)this.buffer.Pointer, default(byte), (uint)this.Count*sizeof(uint)); + Unsafe.InitBlock((void*)this.buffer.Pin(), default(byte), (uint)this.Count*sizeof(uint)); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/General/IterateArray.cs b/tests/ImageSharp.Benchmarks/General/IterateArray.cs index 48f2316a27..eeab6506a2 100644 --- a/tests/ImageSharp.Benchmarks/General/IterateArray.cs +++ b/tests/ImageSharp.Benchmarks/General/IterateArray.cs @@ -8,7 +8,7 @@ namespace ImageSharp.Benchmarks.General public class IterateArray { // Usual pinned stuff - private PinnedBuffer buffer; + private Buffer buffer; // An array that's not pinned by intent! private Vector4[] array; @@ -19,7 +19,8 @@ namespace ImageSharp.Benchmarks.General [Setup] public void Setup() { - this.buffer = new PinnedBuffer(this.Length); + this.buffer = new Buffer(this.Length); + this.buffer.Pin(); this.array = new Vector4[this.Length]; } @@ -41,7 +42,7 @@ namespace ImageSharp.Benchmarks.General { Vector4 sum = new Vector4(); - Vector4* ptr = (Vector4*) this.buffer.Pointer; + Vector4* ptr = (Vector4*) this.buffer.Pin(); Vector4* end = ptr + this.Length; for (; ptr < end; ptr++) diff --git a/tests/ImageSharp.Benchmarks/General/PixelIndexing.cs b/tests/ImageSharp.Benchmarks/General/PixelIndexing.cs index 6db0eef36f..d9237b8010 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelIndexing.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelIndexing.cs @@ -29,9 +29,9 @@ private int width; - public Data(PinnedImageBuffer buffer) + public Data(Buffer2D buffer) { - this.pointer = (Vector4*)buffer.Pointer; + this.pointer = (Vector4*)buffer.Pin(); this.pinnable = Unsafe.As>(buffer.Array); this.array = buffer.Array; this.width = buffer.Width; @@ -126,7 +126,7 @@ } } - internal PinnedImageBuffer buffer; + internal Buffer2D buffer; protected int width; @@ -147,8 +147,8 @@ public void Setup() { this.width = 2048; - this.buffer = new PinnedImageBuffer(2048, 2048); - this.pointer = (Vector4*)this.buffer.Pointer; + this.buffer = new Buffer2D(2048, 2048); + this.pointer = (Vector4*)this.buffer.Pin(); this.array = this.buffer.Array; this.pinnable = Unsafe.As>(this.array); diff --git a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs index fe005ad014..7db5a45f34 100644 --- a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs @@ -45,8 +45,8 @@ namespace ImageSharp.Tests.Colors int times = 200000; int count = 1024; - using (PinnedBuffer source = new PinnedBuffer(count)) - using (PinnedBuffer dest = new PinnedBuffer(count)) + using (Buffer source = new Buffer(count)) + using (Buffer dest = new Buffer(count)) { this.Measure( times, @@ -310,18 +310,18 @@ namespace ImageSharp.Tests.Colors where TSource : struct where TDest : struct { - public PinnedBuffer SourceBuffer { get; } - public PinnedBuffer ActualDestBuffer { get; } - public PinnedBuffer ExpectedDestBuffer { get; } + public Buffer SourceBuffer { get; } + public Buffer ActualDestBuffer { get; } + public Buffer ExpectedDestBuffer { get; } public BufferSpan Source => this.SourceBuffer; public BufferSpan ActualDest => this.ActualDestBuffer; public TestBuffers(TSource[] source, TDest[] expectedDest) { - this.SourceBuffer = new PinnedBuffer(source); - this.ExpectedDestBuffer = new PinnedBuffer(expectedDest); - this.ActualDestBuffer = new PinnedBuffer(expectedDest.Length); + this.SourceBuffer = new Buffer(source); + this.ExpectedDestBuffer = new Buffer(expectedDest); + this.ActualDestBuffer = new Buffer(expectedDest.Length); } public void Dispose() diff --git a/tests/ImageSharp.Tests/Common/PinnedImageBufferTests.cs b/tests/ImageSharp.Tests/Common/Buffer2DTests.cs similarity index 72% rename from tests/ImageSharp.Tests/Common/PinnedImageBufferTests.cs rename to tests/ImageSharp.Tests/Common/Buffer2DTests.cs index a8ccea5eb8..ac92ab87ba 100644 --- a/tests/ImageSharp.Tests/Common/PinnedImageBufferTests.cs +++ b/tests/ImageSharp.Tests/Common/Buffer2DTests.cs @@ -8,19 +8,18 @@ namespace ImageSharp.Tests.Common using static TestStructs; - public unsafe class PinnedImageBufferTests + public unsafe class Buffer2DTests { // ReSharper disable once ClassNeverInstantiated.Local private class Assert : Xunit.Assert { - public static void SpanPointsTo(IntPtr ptr, BufferSpan span) + public static void SpanPointsTo(BufferSpan span, Buffer buffer, int bufferOffset = 0) where T : struct { - ref byte r = ref Unsafe.As(ref span.DangerousGetPinnableReference()); + ref T actual = ref span.DangerousGetPinnableReference(); + ref T expected = ref Unsafe.Add(ref buffer[0], bufferOffset); - void* p = Unsafe.AsPointer(ref r); - - Assert.Equal(ptr, (IntPtr)p); + Assert.True(Unsafe.AreSame(ref expected, ref actual), "span does not point to the expected position"); } } @@ -29,7 +28,7 @@ namespace ImageSharp.Tests.Common [InlineData(1025, 17)] public void Construct(int width, int height) { - using (PinnedImageBuffer buffer = new PinnedImageBuffer(width, height)) + using (Buffer2D buffer = new Buffer2D(width, height)) { Assert.Equal(width, buffer.Width); Assert.Equal(height, buffer.Height); @@ -43,7 +42,7 @@ namespace ImageSharp.Tests.Common public void Construct_FromExternalArray(int width, int height) { Foo[] array = new Foo[width * height + 10]; - using (PinnedImageBuffer buffer = new PinnedImageBuffer(array, width, height)) + using (Buffer2D buffer = new Buffer2D(array, width, height)) { Assert.Equal(width, buffer.Width); Assert.Equal(height, buffer.Height); @@ -57,7 +56,7 @@ namespace ImageSharp.Tests.Common { for (int i = 0; i < 100; i++) { - using (PinnedImageBuffer buffer = PinnedImageBuffer.CreateClean(42, 42)) + using (Buffer2D buffer = Buffer2D.CreateClean(42, 42)) { for (int j = 0; j < buffer.Length; j++) { @@ -74,13 +73,13 @@ namespace ImageSharp.Tests.Common [InlineData(17, 42, 41)] public void GetRowSpanY(int width, int height, int y) { - using (PinnedImageBuffer buffer = new PinnedImageBuffer(width, height)) + using (Buffer2D buffer = new Buffer2D(width, height)) { BufferSpan span = buffer.GetRowSpan(y); Assert.Equal(width * y, span.Start); Assert.Equal(width, span.Length); - Assert.SpanPointsTo(buffer.Pointer + sizeof(Foo) * width * y, span); + Assert.SpanPointsTo(span, buffer, width * y); } } @@ -90,13 +89,13 @@ namespace ImageSharp.Tests.Common [InlineData(17, 42, 0, 41)] public void GetRowSpanXY(int width, int height, int x, int y) { - using (PinnedImageBuffer buffer = new PinnedImageBuffer(width, height)) + using (Buffer2D buffer = new Buffer2D(width, height)) { BufferSpan span = buffer.GetRowSpan(x, y); Assert.Equal(width * y + x, span.Start); Assert.Equal(width - x, span.Length); - Assert.SpanPointsTo(buffer.Pointer + sizeof(Foo) * (width * y + x), span); + Assert.SpanPointsTo(span, buffer, width * y + x); } } @@ -106,7 +105,7 @@ namespace ImageSharp.Tests.Common [InlineData(99, 88, 98, 87)] public void Indexer(int width, int height, int x, int y) { - using (PinnedImageBuffer buffer = new PinnedImageBuffer(width, height)) + using (Buffer2D buffer = new Buffer2D(width, height)) { Foo[] array = buffer.Array; diff --git a/tests/ImageSharp.Tests/Common/BufferSpanTests.cs b/tests/ImageSharp.Tests/Common/BufferSpanTests.cs index 58a217b18e..067ef66224 100644 --- a/tests/ImageSharp.Tests/Common/BufferSpanTests.cs +++ b/tests/ImageSharp.Tests/Common/BufferSpanTests.cs @@ -28,7 +28,7 @@ namespace ImageSharp.Tests.Common { Foo[] fooz = { new Foo(1, 2), new Foo(3, 4), new Foo(5, 6) }; - using (PinnedBuffer colorBuf = new PinnedBuffer(fooz)) + using (Buffer colorBuf = new Buffer(fooz)) { BufferSpan orig = colorBuf.Slice(1); BufferSpan asBytes = orig.AsBytes(); @@ -414,8 +414,8 @@ namespace ImageSharp.Tests.Common { Color[] colors = { new Color(0, 1, 2, 3), new Color(4, 5, 6, 7), new Color(8, 9, 10, 11), }; - using (PinnedBuffer colorBuf = new PinnedBuffer(colors)) - using (PinnedBuffer byteBuf = new PinnedBuffer(colors.Length * 4)) + using (Buffer colorBuf = new Buffer(colors)) + using (Buffer byteBuf = new Buffer(colors.Length * 4)) { BufferSpan.Copy(colorBuf.Span.AsBytes(), byteBuf, colorBuf.Length*sizeof(Color)); diff --git a/tests/ImageSharp.Tests/Common/PinnedBufferTests.cs b/tests/ImageSharp.Tests/Common/BufferTests.cs similarity index 63% rename from tests/ImageSharp.Tests/Common/PinnedBufferTests.cs rename to tests/ImageSharp.Tests/Common/BufferTests.cs index 67430976a7..23205d0124 100644 --- a/tests/ImageSharp.Tests/Common/PinnedBufferTests.cs +++ b/tests/ImageSharp.Tests/Common/BufferTests.cs @@ -1,4 +1,5 @@ -namespace ImageSharp.Tests.Common +// ReSharper disable InconsistentNaming +namespace ImageSharp.Tests.Common { using System; using System.Runtime.CompilerServices; @@ -9,18 +10,23 @@ using static TestStructs; - public unsafe class PinnedBufferTests + public unsafe class BufferTests { + // ReSharper disable once ClassNeverInstantiated.Local private class Assert : Xunit.Assert { - public static void SpanPointsTo(IntPtr ptr, BufferSpan span) + public static void SpanPointsTo(BufferSpan span, Buffer buffer, int bufferOffset = 0) where T : struct { - ref byte r = ref Unsafe.As(ref span.DangerousGetPinnableReference()); + ref T actual = ref span.DangerousGetPinnableReference(); + ref T expected = ref Unsafe.Add(ref buffer[0], bufferOffset); - void* p = Unsafe.AsPointer(ref r); + Assert.True(Unsafe.AreSame(ref expected, ref actual), "span does not point to the expected position"); + } - Assert.Equal(ptr, (IntPtr)p); + public static void Equal(void* expected, void* actual) + { + Assert.Equal((IntPtr)expected, (IntPtr)actual); } } @@ -29,14 +35,12 @@ [InlineData(1111)] public void ConstructWithOwnArray(int count) { - using (PinnedBuffer buffer = new PinnedBuffer(count)) + using (Buffer buffer = new Buffer(count)) { Assert.False(buffer.IsDisposedOrLostArrayOwnership); Assert.NotNull(buffer.Array); Assert.Equal(count, buffer.Length); Assert.True(buffer.Array.Length >= count); - - VerifyPointer(buffer); } } @@ -46,13 +50,11 @@ public void ConstructWithExistingArray(int count) { Foo[] array = new Foo[count]; - using (PinnedBuffer buffer = new PinnedBuffer(array)) + using (Buffer buffer = new Buffer(array)) { Assert.False(buffer.IsDisposedOrLostArrayOwnership); Assert.Equal(array, buffer.Array); Assert.Equal(count, buffer.Length); - - VerifyPointer(buffer); } } @@ -62,7 +64,7 @@ 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)) + using (Buffer buffer = new Buffer(a)) { buffer.Clear(); @@ -76,7 +78,7 @@ { for (int i = 0; i < 100; i++) { - using (PinnedBuffer buffer = PinnedBuffer.CreateClean(42)) + using (Buffer buffer = Buffer.CreateClean(42)) { for (int j = 0; j < buffer.Length; j++) { @@ -103,7 +105,7 @@ { Foo[] a = Foo.CreateArray(length); - using (PinnedBuffer buffer = new PinnedBuffer(a)) + using (Buffer buffer = new Buffer(a)) { Foo element = buffer[index]; @@ -117,7 +119,7 @@ { Foo[] a = Foo.CreateArray(length); - using (PinnedBuffer buffer = new PinnedBuffer(a)) + using (Buffer buffer = new Buffer(a)) { buffer[index] = new Foo(666, 666); @@ -129,7 +131,7 @@ [Fact] public void Dispose() { - PinnedBuffer buffer = new PinnedBuffer(42); + Buffer buffer = new Buffer(42); buffer.Dispose(); Assert.True(buffer.IsDisposedOrLostArrayOwnership); @@ -140,13 +142,13 @@ [InlineData(123)] public void CastToSpan(int bufferLength) { - using (PinnedBuffer buffer = new PinnedBuffer(bufferLength)) + using (Buffer buffer = new Buffer(bufferLength)) { BufferSpan span = buffer; Assert.Equal(buffer.Array, span.Array); Assert.Equal(0, span.Start); - Assert.SpanPointsTo(buffer.Pointer, span); + Assert.SpanPointsTo(span, buffer); Assert.Equal(span.Length, bufferLength); } } @@ -154,13 +156,13 @@ [Fact] public void Span() { - using (PinnedBuffer buffer = new PinnedBuffer(42)) + using (Buffer buffer = new Buffer(42)) { BufferSpan span = buffer.Span; Assert.Equal(buffer.Array, span.Array); Assert.Equal(0, span.Start); - Assert.SpanPointsTo(buffer.Pointer, span); + Assert.SpanPointsTo(span, buffer); Assert.Equal(span.Length, 42); } } @@ -173,13 +175,13 @@ [InlineData(123, 17)] public void WithStartOnly(int bufferLength, int start) { - using (PinnedBuffer buffer = new PinnedBuffer(bufferLength)) + using (Buffer buffer = new Buffer(bufferLength)) { BufferSpan span = buffer.Slice(start); Assert.Equal(buffer.Array, span.Array); Assert.Equal(start, span.Start); - Assert.SpanPointsTo(buffer.Pointer + start * Unsafe.SizeOf(), span); + Assert.SpanPointsTo(span, buffer, start); Assert.Equal(span.Length, bufferLength - start); } } @@ -189,13 +191,13 @@ [InlineData(123, 17, 42)] public void WithStartAndLength(int bufferLength, int start, int spanLength) { - using (PinnedBuffer buffer = new PinnedBuffer(bufferLength)) + using (Buffer buffer = new Buffer(bufferLength)) { BufferSpan span = buffer.Slice(start, spanLength); Assert.Equal(buffer.Array, span.Array); Assert.Equal(start, span.Start); - Assert.SpanPointsTo(buffer.Pointer + start * Unsafe.SizeOf(), span); + Assert.SpanPointsTo(span, buffer, start); Assert.Equal(span.Length, spanLength); } } @@ -205,9 +207,9 @@ public void UnPinAndTakeArrayOwnership() { Foo[] data = null; - using (PinnedBuffer buffer = new PinnedBuffer(42)) + using (Buffer buffer = new Buffer(42)) { - data = buffer.UnPinAndTakeArrayOwnership(); + data = buffer.TakeArrayOwnership(); Assert.True(buffer.IsDisposedOrLostArrayOwnership); } @@ -215,10 +217,41 @@ Assert.True(data.Length >= 42); } - private static void VerifyPointer(PinnedBuffer buffer) + public class Pin { - IntPtr ptr = (IntPtr)Unsafe.AsPointer(ref buffer.Array[0]); - Assert.Equal(ptr, buffer.Pointer); + [Fact] + public void ReturnsPinnedPointerToTheBeginningOfArray() + { + using (Buffer buffer = new Buffer(42)) + { + Foo* actual = (Foo*)buffer.Pin(); + fixed (Foo* expected = buffer.Array) + { + Assert.Equal(expected, actual); + } + } + } + + [Fact] + public void SecondCallReturnsTheSamePointer() + { + using (Buffer buffer = new Buffer(42)) + { + IntPtr ptr1 = buffer.Pin(); + IntPtr ptr2 = buffer.Pin(); + + Assert.Equal(ptr1, ptr2); + } + } + + [Fact] + public void WhenCalledOnDisposedBuffer_ThrowsInvalidOperationException() + { + Buffer buffer = new Buffer(42); + buffer.Dispose(); + + Assert.Throws(() => buffer.Pin()); + } } } } \ No newline at end of file