Browse Source

PreferContiguousImageBuffers implemented

af/UniformUnmanagedMemoryPoolMemoryAllocator-02-MemoryGuards
Anton Firszov 5 years ago
parent
commit
5839a5a483
  1. 8
      src/ImageSharp/Configuration.cs
  2. 16
      src/ImageSharp/ImageFrame{TPixel}.cs
  3. 52
      src/ImageSharp/Memory/MemoryAllocatorExtensions.cs
  4. 23
      tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.Generic.cs
  5. 12
      tests/ImageSharp.Tests/Image/LargeImageIntegrationTests.cs
  6. 19
      tests/ImageSharp.Tests/Memory/Buffer2DTests.cs

8
src/ImageSharp/Configuration.cs

@ -94,6 +94,14 @@ namespace SixLabors.ImageSharp
}
}
/// <summary>
/// Gets or sets a value indicating whether to force image buffers to be contiguous whenever possible.
/// </summary>
/// <remarks>
/// Contiguous allocations are not possible, if the image needs a buffer larger than <see cref="int.MaxValue"/>.
/// </remarks>
public bool PreferContiguousImageBuffers { get; set; }
/// <summary>
/// Gets a set of properties for the Configuration.
/// </summary>

16
src/ImageSharp/ImageFrame{TPixel}.cs

@ -58,7 +58,11 @@ namespace SixLabors.ImageSharp
Guard.MustBeGreaterThan(width, 0, nameof(width));
Guard.MustBeGreaterThan(height, 0, nameof(height));
this.PixelBuffer = this.GetConfiguration().MemoryAllocator.Allocate2D<TPixel>(width, height, AllocationOptions.Clean);
this.PixelBuffer = this.GetConfiguration().MemoryAllocator.Allocate2D<TPixel>(
width,
height,
configuration.PreferContiguousImageBuffers,
AllocationOptions.Clean);
}
/// <summary>
@ -87,7 +91,10 @@ namespace SixLabors.ImageSharp
Guard.MustBeGreaterThan(width, 0, nameof(width));
Guard.MustBeGreaterThan(height, 0, nameof(height));
this.PixelBuffer = this.GetConfiguration().MemoryAllocator.Allocate2D<TPixel>(width, height);
this.PixelBuffer = this.GetConfiguration().MemoryAllocator.Allocate2D<TPixel>(
width,
height,
configuration.PreferContiguousImageBuffers);
this.Clear(backgroundColor);
}
@ -131,7 +138,10 @@ namespace SixLabors.ImageSharp
Guard.NotNull(configuration, nameof(configuration));
Guard.NotNull(source, nameof(source));
this.PixelBuffer = this.GetConfiguration().MemoryAllocator.Allocate2D<TPixel>(source.PixelBuffer.Width, source.PixelBuffer.Height);
this.PixelBuffer = this.GetConfiguration().MemoryAllocator.Allocate2D<TPixel>(
source.PixelBuffer.Width,
source.PixelBuffer.Height,
configuration.PreferContiguousImageBuffers);
source.PixelBuffer.FastMemoryGroup.CopyTo(this.PixelBuffer.FastMemoryGroup);
}

52
src/ImageSharp/Memory/MemoryAllocatorExtensions.cs

@ -20,20 +20,68 @@ namespace SixLabors.ImageSharp.Memory
/// <param name="memoryAllocator">The memory allocator.</param>
/// <param name="width">The buffer width.</param>
/// <param name="height">The buffer height.</param>
/// <param name="preferContiguosImageBuffers">A value indicating whether the allocated buffer should be contiguous, unless bigger than <see cref="int.MaxValue"/>.</param>
/// <param name="options">The allocation options.</param>
/// <returns>The <see cref="Buffer2D{T}"/>.</returns>
public static Buffer2D<T> Allocate2D<T>(
this MemoryAllocator memoryAllocator,
int width,
int height,
bool preferContiguosImageBuffers,
AllocationOptions options = AllocationOptions.None)
where T : struct
{
long groupLength = (long)width * height;
MemoryGroup<T> memoryGroup = memoryAllocator.AllocateGroup<T>(groupLength, width, options);
MemoryGroup<T> memoryGroup;
if (preferContiguosImageBuffers && groupLength < int.MaxValue)
{
IMemoryOwner<T> buffer = memoryAllocator.Allocate<T>((int)groupLength, options);
memoryGroup = MemoryGroup<T>.CreateContiguous(buffer, false);
}
else
{
memoryGroup = memoryAllocator.AllocateGroup<T>(groupLength, width, options);
}
return new Buffer2D<T>(memoryGroup, width, height);
}
/// <summary>
/// Allocates a buffer of value type objects interpreted as a 2D region
/// of <paramref name="width"/> x <paramref name="height"/> elements.
/// </summary>
/// <typeparam name="T">The type of buffer items to allocate.</typeparam>
/// <param name="memoryAllocator">The memory allocator.</param>
/// <param name="width">The buffer width.</param>
/// <param name="height">The buffer height.</param>
/// <param name="options">The allocation options.</param>
/// <returns>The <see cref="Buffer2D{T}"/>.</returns>
public static Buffer2D<T> Allocate2D<T>(
this MemoryAllocator memoryAllocator,
int width,
int height,
AllocationOptions options = AllocationOptions.None)
where T : struct =>
Allocate2D<T>(memoryAllocator, width, height, false, options);
/// <summary>
/// Allocates a buffer of value type objects interpreted as a 2D region
/// of <paramref name="size"/> width x <paramref name="size"/> height elements.
/// </summary>
/// <typeparam name="T">The type of buffer items to allocate.</typeparam>
/// <param name="memoryAllocator">The memory allocator.</param>
/// <param name="size">The buffer size.</param>
/// <param name="preferContiguosImageBuffers">A value indicating whether the allocated buffer should be contiguous, unless bigger than <see cref="int.MaxValue"/>.</param>
/// <param name="options">The allocation options.</param>
/// <returns>The <see cref="Buffer2D{T}"/>.</returns>
public static Buffer2D<T> Allocate2D<T>(
this MemoryAllocator memoryAllocator,
Size size,
bool preferContiguosImageBuffers,
AllocationOptions options = AllocationOptions.None)
where T : struct =>
Allocate2D<T>(memoryAllocator, size.Width, size.Height, preferContiguosImageBuffers, options);
/// <summary>
/// Allocates a buffer of value type objects interpreted as a 2D region
/// of <paramref name="size"/> width x <paramref name="size"/> height elements.
@ -48,7 +96,7 @@ namespace SixLabors.ImageSharp.Memory
Size size,
AllocationOptions options = AllocationOptions.None)
where T : struct =>
Allocate2D<T>(memoryAllocator, size.Width, size.Height, options);
Allocate2D<T>(memoryAllocator, size.Width, size.Height, false, options);
internal static Buffer2D<T> Allocate2DOveraligned<T>(
this MemoryAllocator memoryAllocator,

23
tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.Generic.cs

@ -2,10 +2,11 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Generic;
using System.Linq;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tests.Memory;
using Xunit;
namespace SixLabors.ImageSharp.Tests
@ -339,6 +340,26 @@ namespace SixLabors.ImageSharp.Tests
Assert.False(this.Image.Frames.Contains(frame));
}
[Fact]
public void PreferContiguousImageBuffers_True_AppliedToAllFrames()
{
Configuration configuration = Configuration.Default.Clone();
configuration.MemoryAllocator = new TestMemoryAllocator { BufferCapacityInBytes = 1000 };
configuration.PreferContiguousImageBuffers = true;
using var image = new Image<Rgba32>(configuration, 100, 100);
image.Frames.CreateFrame();
image.Frames.InsertFrame(0, image.Frames[0]);
image.Frames.CreateFrame(Color.Red);
Assert.Equal(4, image.Frames.Count);
IEnumerable<ImageFrame<Rgba32>> frames = image.Frames;
foreach (ImageFrame<Rgba32> frame in frames)
{
Assert.True(frame.TryGetSinglePixelSpan(out Span<Rgba32> span));
}
}
[Fact]
public void DisposeCall_NoThrowIfCalledMultiple()
{

12
tests/ImageSharp.Tests/Image/LargeImageIntegrationTests.cs

@ -22,17 +22,17 @@ namespace SixLabors.ImageSharp.Tests
}
[Fact]
public unsafe void Set_MaximumPoolSizeMegabytes_CreateImage_MaximumPoolSizeMegabytes()
public void PreferContiguousImageBuffers_CreateImage_MaximumPoolSizeMegabytes()
{
// Run remotely to avoid large allocation in the test process:
RemoteExecutor.Invoke(RunTest).Dispose();
static void RunTest()
{
Configuration.Default.MemoryAllocator = MemoryAllocator.CreateDefault(new MemoryAllocatorOptions()
{
MinimumContiguousBlockSizeBytes = sizeof(Rgba32) * 8192 * 4096
});
using Image<Rgba32> image = new Image<Rgba32>(8192, 4096);
Configuration configuration = Configuration.Default.Clone();
configuration.PreferContiguousImageBuffers = true;
using var image = new Image<Rgba32>(configuration, 8192, 4096);
Assert.True(image.TryGetSinglePixelSpan(out Span<Rgba32> span));
Assert.Equal(8192 * 4096, span.Length);
}

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

@ -68,6 +68,25 @@ namespace SixLabors.ImageSharp.Tests.Memory
}
}
[Theory]
[InlineData(false)]
[InlineData(true)]
public void Construct_PreferContiguousImageBuffers_AllocatesContiguousRegardlessOfCapacity(bool useSizeOverload)
{
this.MemoryAllocator.BufferCapacityInBytes = 10_000;
using Buffer2D<byte> buffer = useSizeOverload ?
this.MemoryAllocator.Allocate2D<byte>(
new Size(200, 200),
preferContiguosImageBuffers: true) :
this.MemoryAllocator.Allocate2D<byte>(
200,
200,
preferContiguosImageBuffers: true);
Assert.Equal(1, buffer.FastMemoryGroup.Count);
Assert.Equal(200 * 200, buffer.FastMemoryGroup.TotalLength);
}
[Theory]
[InlineData(50, 10, 20, 4)]
public void Allocate2DOveraligned(int bufferCapacity, int width, int height, int alignmentMultiplier)

Loading…
Cancel
Save