Browse Source

clean up Buffer<T> API

af/merge-core
Anton Firszov 8 years ago
parent
commit
b61574fdd0
  1. 4
      src/ImageSharp/Memory/Buffer2D{T}.cs
  2. 83
      src/ImageSharp/Memory/Buffer{T}.cs
  3. 2
      src/ImageSharp/Memory/SimpleManagedMemoryManager.cs
  4. 5
      src/ImageSharp/Processing/Processors/Transforms/WeightsWindow.cs
  5. 77
      tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4ReferenceVsPointer.cs
  6. 73
      tests/ImageSharp.Benchmarks/General/IterateArray.cs
  7. 2
      tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs
  8. 2
      tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs
  9. 8
      tests/ImageSharp.Tests/Memory/Buffer2DTests.cs
  10. 16
      tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs
  11. 50
      tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs
  12. 8
      tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs

4
src/ImageSharp/Memory/Buffer2D{T}.cs

@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Memory
/// <param name="wrappedBuffer">The buffer to wrap</param>
/// <param name="width">The number of elements in a row</param>
/// <param name="height">The number of rows</param>
public Buffer2D(Buffer<T> wrappedBuffer, int width, int height)
public Buffer2D(IBuffer<T> wrappedBuffer, int width, int height)
{
this.Buffer = wrappedBuffer;
this.Width = width;
@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.Memory
public Span<T> Span => this.Buffer.Span;
public Buffer<T> Buffer { get; }
public IBuffer<T> Buffer { get; }
/// <summary>
/// Gets a reference to the element at the specified position.

83
src/ImageSharp/Memory/Buffer{T}.cs

@ -18,16 +18,6 @@ namespace SixLabors.ImageSharp.Memory
{
private MemoryManager memoryManager;
/// <summary>
/// A pointer to the first element of <see cref="array"/> when pinned.
/// </summary>
private IntPtr pointer;
/// <summary>
/// A handle that allows to access the managed <see cref="array"/> as an unmanaged memory by pinning.
/// </summary>
private GCHandle handle;
// why is there such a rule? :S Protected should be fine for a field!
#pragma warning disable SA1401 // Fields should be private
/// <summary>
@ -36,22 +26,7 @@ namespace SixLabors.ImageSharp.Memory
protected T[] array;
#pragma warning restore SA1401 // Fields should be private
/// <summary>
/// Initializes a new instance of the <see cref="Buffer{T}"/> class.
/// </summary>
/// <param name="array">The array to pin.</param>
public Buffer(T[] array)
{
this.Length = array.Length;
this.array = array;
}
/// <summary>
/// Initializes a new instance of the <see cref="Buffer{T}"/> class.
/// </summary>
/// <param name="array">The array to pin.</param>
/// <param name="length">The count of "relevant" elements in 'array'.</param>
public Buffer(T[] array, int length)
internal Buffer(T[] array, int length, MemoryManager memoryManager)
{
if (array.Length < length)
{
@ -60,22 +35,9 @@ namespace SixLabors.ImageSharp.Memory
this.Length = length;
this.array = array;
}
internal Buffer(T[] array, int length, MemoryManager memoryManager)
: this(array, length)
{
this.memoryManager = memoryManager;
}
/// <summary>
/// Finalizes an instance of the <see cref="Buffer{T}"/> class.
/// </summary>
~Buffer()
{
this.UnPin();
}
/// <summary>
/// Gets a value indicating whether this <see cref="Buffer{T}"/> instance is disposed, or has lost ownership of <see cref="array"/>.
/// </summary>
@ -140,7 +102,6 @@ namespace SixLabors.ImageSharp.Memory
}
this.IsDisposedOrLostArrayOwnership = true;
this.UnPin();
this.memoryManager?.Release(this);
@ -166,33 +127,10 @@ namespace SixLabors.ImageSharp.Memory
}
this.IsDisposedOrLostArrayOwnership = true;
this.UnPin();
T[] array = this.array;
T[] a = this.array;
this.array = null;
this.memoryManager = null;
return array;
}
/// <summary>
/// Pins <see cref="array"/>.
/// </summary>
/// <returns>The pinned pointer</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public IntPtr Pin()
{
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;
return a;
}
/// <summary>
@ -202,20 +140,5 @@ namespace SixLabors.ImageSharp.Memory
{
return this.array;
}
/// <summary>
/// Unpins <see cref="array"/>.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void UnPin()
{
if (this.pointer == IntPtr.Zero || !this.handle.IsAllocated)
{
return;
}
this.handle.Free();
this.pointer = IntPtr.Zero;
}
}
}

2
src/ImageSharp/Memory/SimpleManagedMemoryManager.cs

@ -8,7 +8,7 @@
/// <inheritdoc />
internal override Buffer<T> Allocate<T>(int length, bool clear)
{
return new Buffer<T>(new T[length], length);
return new Buffer<T>(new T[length], length, this);
}
internal override IManagedByteBuffer AllocateManagedByteBuffer(int length, bool clear)

5
src/ImageSharp/Processing/Processors/Transforms/WeightsWindow.cs

@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// <summary>
/// The buffer containing the weights values.
/// </summary>
private readonly Buffer<float> buffer;
private readonly IBuffer<float> buffer;
/// <summary>
/// Initializes a new instance of the <see cref="WeightsWindow"/> struct.
@ -57,7 +57,8 @@ namespace SixLabors.ImageSharp.Processing.Processors
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref float GetStartReference()
{
return ref this.buffer[this.flatStartIndex];
Span<float> span = this.buffer.Span;
return ref span[this.flatStartIndex];
}
/// <summary>

77
tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4ReferenceVsPointer.cs

@ -1,77 +0,0 @@
namespace SixLabors.ImageSharp.Benchmarks
{
using System.Numerics;
using System.Runtime.CompilerServices;
using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Memory;
/// <summary>
/// Compares two implementation candidates for general BulkPixelOperations.ToVector4():
/// - One iterating with pointers
/// - One iterating with ref locals
/// </summary>
public unsafe class PackFromVector4ReferenceVsPointer
{
private Buffer<Rgba32> destination;
private Buffer<Vector4> source;
[Params(16, 128, 1024)]
public int Count { get; set; }
[GlobalSetup]
public void Setup()
{
this.destination = Configuration.Default.MemoryManager.Allocate<Rgba32>(this.Count);
this.source = Configuration.Default.MemoryManager.Allocate<Vector4>(this.Count * 4);
this.source.Pin();
this.destination.Pin();
}
[GlobalCleanup]
public void Cleanup()
{
this.source.Dispose();
this.destination.Dispose();
}
[Benchmark(Baseline = true)]
public void PackUsingPointers()
{
Vector4* sp = (Vector4*)this.source.Pin();
byte* dp = (byte*)this.destination.Pin();
int count = this.Count;
int size = sizeof(Rgba32);
for (int i = 0; i < count; i++)
{
Vector4 v = Unsafe.Read<Vector4>(sp);
Rgba32 c = default(Rgba32);
c.PackFromVector4(v);
Unsafe.Write(dp, c);
sp++;
dp += size;
}
}
[Benchmark]
public void PackUsingReferences()
{
ref Vector4 sp = ref this.source.DangerousGetPinnableReference();
ref Rgba32 dp = ref this.destination.DangerousGetPinnableReference();
int count = this.Count;
for (int i = 0; i < count; i++)
{
dp.PackFromVector4(sp);
sp = Unsafe.Add(ref sp, 1);
dp = Unsafe.Add(ref dp, 1);
}
}
}
}

73
tests/ImageSharp.Benchmarks/General/IterateArray.cs

@ -1,73 +0,0 @@
namespace SixLabors.ImageSharp.Benchmarks.General
{
using System.Numerics;
using System.Runtime.CompilerServices;
using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.Memory;
public class IterateArray
{
// Usual pinned stuff
private Buffer<Vector4> buffer;
// An array that's not pinned by intent!
private Vector4[] array;
[Params(64, 1024)]
public int Length { get; set; }
[GlobalSetup]
public void Setup()
{
this.buffer = Configuration.Default.MemoryManager.Allocate<Vector4>(this.Length);
this.buffer.Pin();
this.array = new Vector4[this.Length];
}
[Benchmark(Baseline = true)]
public Vector4 IterateIndexed()
{
Vector4 sum = new Vector4();
Vector4[] a = this.array;
for (int i = 0; i < a.Length; i++)
{
sum += a[i];
}
return sum;
}
[Benchmark]
public unsafe Vector4 IterateUsingPointers()
{
Vector4 sum = new Vector4();
Vector4* ptr = (Vector4*) this.buffer.Pin();
Vector4* end = ptr + this.Length;
for (; ptr < end; ptr++)
{
sum += *ptr;
}
return sum;
}
[Benchmark]
public Vector4 IterateUsingReferences()
{
Vector4 sum = new Vector4();
ref Vector4 start = ref this.array[0];
for (int i = 0; i < this.Length; i++)
{
sum += Unsafe.Add(ref start, i);
}
return sum;
}
}
}

2
tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs

@ -289,7 +289,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
}
// no need to dispose when buffer is not array owner
buffers[i] = new Buffer2D<float>(new Buffer<float>(values, values.Length), values.Length, 1);
buffers[i] = new Buffer2D<float>(new FakeBuffer<float>(values), values.Length, 1);
}
return new JpegColorConverter.ComponentValues(buffers, 0);
}

2
tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs

@ -106,7 +106,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
this.Output.WriteLine($"Component{i}: {diff}");
averageDifference += diff.average;
totalDifference += diff.total;
tolerance += libJpegComponent.SpectralBlocks.Buffer.Length;
tolerance += libJpegComponent.SpectralBlocks.Buffer.Span.Length;
}
averageDifference /= componentCount;

8
tests/ImageSharp.Tests/Memory/Buffer2DTests.cs

@ -16,11 +16,11 @@ namespace SixLabors.ImageSharp.Tests.Memory
// ReSharper disable once ClassNeverInstantiated.Local
private class Assert : Xunit.Assert
{
public static void SpanPointsTo<T>(Span<T> span, Buffer<T> buffer, int bufferOffset = 0)
public static void SpanPointsTo<T>(Span<T> span, IBuffer<T> buffer, int bufferOffset = 0)
where T : struct
{
ref T actual = ref span.DangerousGetPinnableReference();
ref T expected = ref Unsafe.Add(ref buffer[0], bufferOffset);
ref T expected = ref Unsafe.Add(ref buffer.DangerousGetPinnableReference(), bufferOffset);
Assert.True(Unsafe.AreSame(ref expected, ref actual), "span does not point to the expected position");
}
@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Tests.Memory
{
Assert.Equal(width, buffer.Width);
Assert.Equal(height, buffer.Height);
Assert.Equal(width * height, buffer.Buffer.Length);
Assert.Equal(width * height, buffer.Buffer.Length());
}
}
@ -47,7 +47,7 @@ namespace SixLabors.ImageSharp.Tests.Memory
using (Buffer2D<int> buffer = Configuration.Default.MemoryManager.Allocate2D<int>(42, 42, true))
{
Span<int> span = buffer.Span;
for (int j = 0; j < buffer.Buffer.Length; j++)
for (int j = 0; j < span.Length; j++)
{
Assert.Equal(0, span[j]);
span[j] = 666;

16
tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs

@ -333,25 +333,23 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
where TSource : struct
where TDest : struct
{
public Buffer<TSource> SourceBuffer { get; }
public TSource[] SourceBuffer { get; }
public Buffer<TDest> ActualDestBuffer { get; }
public Buffer<TDest> ExpectedDestBuffer { get; }
public TDest[] ExpectedDestBuffer { get; }
public Span<TSource> Source => this.SourceBuffer;
public Span<TDest> ActualDest => this.ActualDestBuffer;
public TestBuffers(TSource[] source, TDest[] expectedDest)
{
this.SourceBuffer = new Buffer<TSource>(source);
this.ExpectedDestBuffer = new Buffer<TDest>(expectedDest);
this.SourceBuffer = source;
this.ExpectedDestBuffer = expectedDest;
this.ActualDestBuffer = Configuration.Default.MemoryManager.Allocate<TDest>(expectedDest.Length);
}
public void Dispose()
{
this.SourceBuffer.Dispose();
this.ActualDestBuffer.Dispose();
this.ExpectedDestBuffer.Dispose();
}
private const float Tolerance = 0.0001f;
@ -363,7 +361,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
if (typeof(TDest) == typeof(Vector4))
{
Span<Vector4> expected = this.ExpectedDestBuffer.Span.NonPortableCast<TDest, Vector4>();
Span<Vector4> expected = this.ExpectedDestBuffer.AsSpan().NonPortableCast<TDest, Vector4>();
Span<Vector4> actual = this.ActualDestBuffer.Span.NonPortableCast<TDest, Vector4>();
for (int i = 0; i < count; i++)
@ -375,7 +373,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
}
else
{
Span<TDest> expected = this.ExpectedDestBuffer.Span;
Span<TDest> expected = this.ExpectedDestBuffer.AsSpan();
Span<TDest> actual = this.ActualDestBuffer.Span;
for (int i = 0; i < count; i++)
{
@ -388,7 +386,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
internal static void TestOperation<TSource, TDest>(
TSource[] source,
TDest[] expected,
Action<Buffer<TSource>, Buffer<TDest>> action)
Action<TSource[], Buffer<TDest>> action)
where TSource : struct
where TDest : struct
{

50
tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs

@ -96,18 +96,20 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs
var image = new Image<TPixel>(w, h);
using (var workBuffer = Configuration.Default.MemoryManager.Allocate<Argb32>(w))
using (IBuffer<Argb32> workBuffer = Configuration.Default.MemoryManager.Allocate<Argb32>(w))
{
var destPtr = (Argb32*)workBuffer.Pin();
for (int y = 0; y < h; y++)
fixed (Argb32* destPtr = &workBuffer.DangerousGetPinnableReference())
{
Span<TPixel> row = image.Frames.RootFrame.GetPixelRowSpan(y);
for (int y = 0; y < h; y++)
{
Span<TPixel> row = image.Frames.RootFrame.GetPixelRowSpan(y);
byte* sourcePtr = sourcePtrBase + data.Stride * y;
byte* sourcePtr = sourcePtrBase + data.Stride * y;
Buffer.MemoryCopy(sourcePtr, destPtr, destRowByteCount, sourceRowByteCount);
Buffer.MemoryCopy(sourcePtr, destPtr, destRowByteCount, sourceRowByteCount);
FromArgb32(workBuffer, row);
FromArgb32(workBuffer.Span, row);
}
}
}
@ -138,18 +140,20 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs
var image = new Image<TPixel>(w, h);
using (var workBuffer = Configuration.Default.MemoryManager.Allocate<Rgb24>(w))
using (IBuffer<Rgb24> workBuffer = Configuration.Default.MemoryManager.Allocate<Rgb24>(w))
{
var destPtr = (Rgb24*)workBuffer.Pin();
for (int y = 0; y < h; y++)
fixed (Rgb24* destPtr = &workBuffer.DangerousGetPinnableReference())
{
Span<TPixel> row = image.Frames.RootFrame.GetPixelRowSpan(y);
for (int y = 0; y < h; y++)
{
Span<TPixel> row = image.Frames.RootFrame.GetPixelRowSpan(y);
byte* sourcePtr = sourcePtrBase + data.Stride * y;
byte* sourcePtr = sourcePtrBase + data.Stride * y;
Buffer.MemoryCopy(sourcePtr, destPtr, destRowByteCount, sourceRowByteCount);
Buffer.MemoryCopy(sourcePtr, destPtr, destRowByteCount, sourceRowByteCount);
FromRgb24(workBuffer, row);
FromRgb24(workBuffer.Span, row);
}
}
}
@ -170,17 +174,19 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs
long destRowByteCount = data.Stride;
long sourceRowByteCount = w * sizeof(Argb32);
using (var workBuffer = Configuration.Default.MemoryManager.Allocate<Argb32>(w))
using (IBuffer<Argb32> workBuffer = image.GetConfiguration().MemoryManager.Allocate<Argb32>(w))
{
var sourcePtr = (Argb32*)workBuffer.Pin();
for (int y = 0; y < h; y++)
fixed (Argb32* sourcePtr = &workBuffer.DangerousGetPinnableReference())
{
Span<TPixel> row = image.Frames.RootFrame.GetPixelRowSpan(y);
ToArgb32(row, workBuffer);
byte* destPtr = destPtrBase + data.Stride * y;
Buffer.MemoryCopy(sourcePtr, destPtr, destRowByteCount, sourceRowByteCount);
for (int y = 0; y < h; y++)
{
Span<TPixel> row = image.Frames.RootFrame.GetPixelRowSpan(y);
ToArgb32(row, workBuffer.Span);
byte* destPtr = destPtrBase + data.Stride * y;
Buffer.MemoryCopy(sourcePtr, destPtr, destRowByteCount, sourceRowByteCount);
}
}
}

8
tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs

@ -151,7 +151,7 @@ namespace SixLabors.ImageSharp.Tests
bool appendPixelTypeToFileName = true)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> firstFrameOnlyImage = new Image<TPixel>(image.Width, image.Height))
using (var firstFrameOnlyImage = new Image<TPixel>(image.Width, image.Height))
using (Image<TPixel> referenceImage = GetReferenceOutputImage<TPixel>(
provider,
testOutputDetails,
@ -378,9 +378,11 @@ namespace SixLabors.ImageSharp.Tests
Span<Rgba32> pixels = image.Frames.RootFrame.GetPixelSpan();
for (int i = 0; i < buffer.Buffer.Length; i++)
Span<float> bufferSpan = buffer.Span;
for (int i = 0; i < bufferSpan.Length; i++)
{
float value = buffer.Buffer[i] * scale;
float value = bufferSpan[i] * scale;
var v = new Vector4(value, value, value, 1f);
pixels[i].PackFromVector4(v);
}

Loading…
Cancel
Save