mirror of https://github.com/SixLabors/ImageSharp
18 changed files with 117 additions and 333 deletions
@ -1 +1 @@ |
|||
Subproject commit a75469fdb93fb89b39a5b0b7c01cb7432ceef98f |
|||
Subproject commit 36b2d55f5bb0d91024955bd26ba220ee41cc96e5 |
|||
@ -1,101 +0,0 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Buffers; |
|||
|
|||
namespace SixLabors.ImageSharp.Memory |
|||
{ |
|||
/// <summary>
|
|||
/// Holds a <see cref="System.Memory{T}"/> that is either OWNED or CONSUMED.
|
|||
/// When the memory is being owned, the <see cref="IMemoryOwner{T}"/> instance is also known.
|
|||
/// Implements content transfer logic in <see cref="SwapOrCopyContent"/> that depends on the ownership status.
|
|||
/// This is needed to transfer the contents of a temporary <see cref="Buffer2D{T}"/>
|
|||
/// to a persistent <see cref="SixLabors.ImageSharp.ImageFrame{T}.PixelBuffer"/> without copying the buffer.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// For a deeper understanding of the owner/consumer model, check out the following docs: <br/>
|
|||
/// https://gist.github.com/GrabYourPitchforks/4c3e1935fd4d9fa2831dbfcab35dffc6
|
|||
/// https://www.codemag.com/Article/1807051/Introducing-.NET-Core-2.1-Flagship-Types-Span-T-and-Memory-T
|
|||
/// </remarks>
|
|||
internal struct MemorySource<T> : IDisposable |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="MemorySource{T}"/> struct
|
|||
/// by wrapping an existing <see cref="IMemoryOwner{T}"/>.
|
|||
/// </summary>
|
|||
/// <param name="memoryOwner">The <see cref="IMemoryOwner{T}"/> to wrap</param>
|
|||
/// <param name="isInternalMemorySource">
|
|||
/// A value indicating whether <paramref name="memoryOwner"/> is an internal memory source managed by ImageSharp.
|
|||
/// Eg. allocated by a <see cref="MemoryAllocator"/>.
|
|||
/// </param>
|
|||
public MemorySource(IMemoryOwner<T> memoryOwner, bool isInternalMemorySource) |
|||
{ |
|||
this.MemoryOwner = memoryOwner; |
|||
this.Memory = memoryOwner.Memory; |
|||
this.HasSwappableContents = isInternalMemorySource; |
|||
} |
|||
|
|||
public MemorySource(Memory<T> memory) |
|||
{ |
|||
this.Memory = memory; |
|||
this.MemoryOwner = null; |
|||
this.HasSwappableContents = false; |
|||
} |
|||
|
|||
public IMemoryOwner<T> MemoryOwner { get; private set; } |
|||
|
|||
public Memory<T> Memory { get; private set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets a value indicating whether we are allowed to swap the contents of this buffer
|
|||
/// with an other <see cref="MemorySource{T}"/> instance.
|
|||
/// The value is true only and only if <see cref="MemoryOwner"/> is present,
|
|||
/// and it's coming from an internal source managed by ImageSharp (<see cref="MemoryAllocator"/>).
|
|||
/// </summary>
|
|||
public bool HasSwappableContents { get; } |
|||
|
|||
public Span<T> GetSpan() => this.Memory.Span; |
|||
|
|||
public void Clear() => this.Memory.Span.Clear(); |
|||
|
|||
/// <summary>
|
|||
/// Swaps the contents of 'destination' with 'source' if the buffers are owned (1),
|
|||
/// copies the contents of 'source' to 'destination' otherwise (2). Buffers should be of same size in case 2!
|
|||
/// </summary>
|
|||
public static void SwapOrCopyContent(ref MemorySource<T> destination, ref MemorySource<T> source) |
|||
{ |
|||
if (source.HasSwappableContents && destination.HasSwappableContents) |
|||
{ |
|||
SwapContents(ref destination, ref source); |
|||
} |
|||
else |
|||
{ |
|||
if (destination.Memory.Length != source.Memory.Length) |
|||
{ |
|||
throw new InvalidOperationException("SwapOrCopyContents(): buffers should both owned or the same size!"); |
|||
} |
|||
|
|||
source.Memory.CopyTo(destination.Memory); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
public void Dispose() |
|||
{ |
|||
this.MemoryOwner?.Dispose(); |
|||
} |
|||
|
|||
private static void SwapContents(ref MemorySource<T> a, ref MemorySource<T> b) |
|||
{ |
|||
IMemoryOwner<T> tempOwner = a.MemoryOwner; |
|||
Memory<T> tempMemory = a.Memory; |
|||
|
|||
a.MemoryOwner = b.MemoryOwner; |
|||
a.Memory = b.Memory; |
|||
|
|||
b.MemoryOwner = tempOwner; |
|||
b.Memory = tempMemory; |
|||
} |
|||
} |
|||
} |
|||
@ -1,159 +0,0 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Buffers; |
|||
using SixLabors.ImageSharp.Memory; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
using Xunit; |
|||
|
|||
// ReSharper disable InconsistentNaming
|
|||
namespace SixLabors.ImageSharp.Tests.Memory |
|||
{ |
|||
public class MemorySourceTests |
|||
{ |
|||
public class Construction |
|||
{ |
|||
[Theory] |
|||
[InlineData(false)] |
|||
[InlineData(true)] |
|||
public void InitializeAsOwner(bool isInternalMemorySource) |
|||
{ |
|||
var data = new Rgba32[21]; |
|||
var mmg = new TestMemoryManager<Rgba32>(data); |
|||
|
|||
var a = new MemorySource<Rgba32>(mmg, isInternalMemorySource); |
|||
|
|||
Assert.Equal(mmg, a.MemoryOwner); |
|||
Assert.Equal(mmg.Memory, a.Memory); |
|||
Assert.Equal(isInternalMemorySource, a.HasSwappableContents); |
|||
} |
|||
|
|||
[Fact] |
|||
public void InitializeAsObserver_MemoryOwner_IsNull() |
|||
{ |
|||
var data = new Rgba32[21]; |
|||
var mmg = new TestMemoryManager<Rgba32>(data); |
|||
|
|||
var a = new MemorySource<Rgba32>(mmg.Memory); |
|||
|
|||
Assert.Null(a.MemoryOwner); |
|||
Assert.Equal(mmg.Memory, a.Memory); |
|||
Assert.False(a.HasSwappableContents); |
|||
} |
|||
} |
|||
|
|||
public class Dispose |
|||
{ |
|||
[Theory] |
|||
[InlineData(false)] |
|||
[InlineData(true)] |
|||
public void WhenOwnershipIsTransferred_ShouldDisposeMemoryOwner(bool isInternalMemorySource) |
|||
{ |
|||
var mmg = new TestMemoryManager<int>(new int[10]); |
|||
var bmg = new MemorySource<int>(mmg, isInternalMemorySource); |
|||
|
|||
bmg.Dispose(); |
|||
Assert.True(mmg.IsDisposed); |
|||
} |
|||
|
|||
[Fact] |
|||
public void WhenMemoryObserver_ShouldNotDisposeAnything() |
|||
{ |
|||
var mmg = new TestMemoryManager<int>(new int[10]); |
|||
var bmg = new MemorySource<int>(mmg.Memory); |
|||
|
|||
bmg.Dispose(); |
|||
Assert.False(mmg.IsDisposed); |
|||
} |
|||
} |
|||
|
|||
public class SwapOrCopyContent |
|||
{ |
|||
private MemoryAllocator MemoryAllocator { get; } = new TestMemoryAllocator(); |
|||
|
|||
private MemorySource<T> AllocateMemorySource<T>(int length, AllocationOptions options = AllocationOptions.None) |
|||
where T : struct |
|||
{ |
|||
IMemoryOwner<T> owner = this.MemoryAllocator.Allocate<T>(length, options); |
|||
return new MemorySource<T>(owner, true); |
|||
} |
|||
|
|||
[Fact] |
|||
public void WhenBothAreMemoryOwners_ShouldSwap() |
|||
{ |
|||
MemorySource<int> a = this.AllocateMemorySource<int>(13); |
|||
MemorySource<int> b = this.AllocateMemorySource<int>(17); |
|||
|
|||
IMemoryOwner<int> aa = a.MemoryOwner; |
|||
IMemoryOwner<int> bb = b.MemoryOwner; |
|||
|
|||
Memory<int> aaa = a.Memory; |
|||
Memory<int> bbb = b.Memory; |
|||
|
|||
MemorySource<int>.SwapOrCopyContent(ref a, ref b); |
|||
|
|||
Assert.Equal(bb, a.MemoryOwner); |
|||
Assert.Equal(aa, b.MemoryOwner); |
|||
|
|||
Assert.Equal(bbb, a.Memory); |
|||
Assert.Equal(aaa, b.Memory); |
|||
Assert.NotEqual(a.Memory, b.Memory); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(false, false)] |
|||
[InlineData(true, true)] |
|||
[InlineData(true, false)] |
|||
public void WhenDestIsNotMemoryOwner_SameSize_ShouldCopy(bool sourceIsOwner, bool isInternalMemorySource) |
|||
{ |
|||
var data = new Rgba32[21]; |
|||
var color = new Rgba32(1, 2, 3, 4); |
|||
|
|||
var destOwner = new TestMemoryManager<Rgba32>(data); |
|||
var dest = new MemorySource<Rgba32>(destOwner.Memory); |
|||
|
|||
IMemoryOwner<Rgba32> sourceOwner = this.MemoryAllocator.Allocate<Rgba32>(21); |
|||
|
|||
MemorySource<Rgba32> source = sourceIsOwner |
|||
? new MemorySource<Rgba32>(sourceOwner, isInternalMemorySource) |
|||
: new MemorySource<Rgba32>(sourceOwner.Memory); |
|||
|
|||
sourceOwner.Memory.Span[10] = color; |
|||
|
|||
// Act:
|
|||
MemorySource<Rgba32>.SwapOrCopyContent(ref dest, ref source); |
|||
|
|||
// Assert:
|
|||
Assert.Equal(color, dest.Memory.Span[10]); |
|||
Assert.NotEqual(sourceOwner, dest.MemoryOwner); |
|||
Assert.NotEqual(destOwner, source.MemoryOwner); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(false)] |
|||
[InlineData(true)] |
|||
public void WhenDestIsNotMemoryOwner_DifferentSize_Throws(bool sourceIsOwner) |
|||
{ |
|||
var data = new Rgba32[21]; |
|||
var color = new Rgba32(1, 2, 3, 4); |
|||
|
|||
var destOwner = new TestMemoryManager<Rgba32>(data); |
|||
var dest = new MemorySource<Rgba32>(destOwner.Memory); |
|||
|
|||
IMemoryOwner<Rgba32> sourceOwner = this.MemoryAllocator.Allocate<Rgba32>(22); |
|||
|
|||
MemorySource<Rgba32> source = sourceIsOwner |
|||
? new MemorySource<Rgba32>(sourceOwner, true) |
|||
: new MemorySource<Rgba32>(sourceOwner.Memory); |
|||
sourceOwner.Memory.Span[10] = color; |
|||
|
|||
// Act:
|
|||
Assert.ThrowsAny<InvalidOperationException>(() => MemorySource<Rgba32>.SwapOrCopyContent(ref dest, ref source)); |
|||
|
|||
Assert.Equal(color, source.Memory.Span[10]); |
|||
Assert.NotEqual(color, dest.Memory.Span[10]); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue