Browse Source

renamed BufferManager<T> to MemorySource<T> + enable consuming external IMemoryOwner<T>

pull/660/head
Anton Firszov 8 years ago
parent
commit
33d32d7407
  1. 2
      src/ImageSharp/Advanced/AdvancedImageExtensions.cs
  2. 5
      src/ImageSharp/Image.WrapMemory.cs
  3. 4
      src/ImageSharp/ImageFrameCollection.cs
  4. 8
      src/ImageSharp/ImageFrame{TPixel}.cs
  5. 15
      src/ImageSharp/Image{TPixel}.cs
  6. 4
      src/ImageSharp/Memory/Buffer2DExtensions.cs
  7. 28
      src/ImageSharp/Memory/Buffer2D{T}.cs
  8. 3
      src/ImageSharp/Memory/MemoryAllocatorExtensions.cs
  9. 34
      src/ImageSharp/Memory/MemorySource.cs
  10. 2
      src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs
  11. 4
      src/ImageSharp/Processing/Processors/Transforms/WeightsWindow.cs
  12. 3
      tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs
  13. 2
      tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs
  14. 14
      tests/ImageSharp.Tests/Memory/Buffer2DTests.cs
  15. 69
      tests/ImageSharp.Tests/Memory/MemorySourceTests.cs

2
src/ImageSharp/Advanced/AdvancedImageExtensions.cs

@ -105,7 +105,7 @@ namespace SixLabors.ImageSharp.Advanced
internal static Memory<TPixel> GetPixelMemory<TPixel>(this ImageFrame<TPixel> source)
where TPixel : struct, IPixel<TPixel>
{
return source.PixelBuffer.Buffer.Memory;
return source.PixelBuffer.MemorySource.Memory;
}
/// <summary>

5
src/ImageSharp/Image.WrapMemory.cs

@ -13,8 +13,6 @@ namespace SixLabors.ImageSharp
/// </content>
public static partial class Image
{
// TODO: This is a WIP API, should be public when finished.
/// <summary>
/// Wraps an existing contigous memory area of 'width'x'height' pixels,
/// allowing to view/manipulate it as an ImageSharp <see cref="Image{TPixel}"/> instance.
@ -34,7 +32,8 @@ namespace SixLabors.ImageSharp
ImageMetaData metaData)
where TPixel : struct, IPixel<TPixel>
{
return new Image<TPixel>(config, pixelMemory, width, height, metaData);
var memorySource = new MemorySource<TPixel>(pixelMemory);
return new Image<TPixel>(config, memorySource, width, height, metaData);
}
/// <summary>

4
src/ImageSharp/ImageFrameCollection.cs

@ -30,14 +30,14 @@ namespace SixLabors.ImageSharp
this.frames.Add(new ImageFrame<TPixel>(parent.GetConfiguration(), width, height, backgroundColor));
}
internal ImageFrameCollection(Image<TPixel> parent, int width, int height, Memory<TPixel> consumedMemory)
internal ImageFrameCollection(Image<TPixel> parent, int width, int height, MemorySource<TPixel> memorySource)
{
Guard.NotNull(parent, nameof(parent));
this.parent = parent;
// Frames are already cloned within the caller
this.frames.Add(new ImageFrame<TPixel>(parent.GetConfiguration(), width, height, consumedMemory));
this.frames.Add(new ImageFrame<TPixel>(parent.GetConfiguration(), width, height, memorySource));
}
internal ImageFrameCollection(Image<TPixel> parent, IEnumerable<ImageFrame<TPixel>> frames)

8
src/ImageSharp/ImageFrame{TPixel}.cs

@ -95,8 +95,8 @@ namespace SixLabors.ImageSharp
/// <summary>
/// Initializes a new instance of the <see cref="ImageFrame{TPixel}" /> class wrapping an existing buffer.
/// </summary>
internal ImageFrame(Configuration configuration, int width, int height, Memory<TPixel> consumedMemory)
: this(configuration, width, height, consumedMemory, new ImageFrameMetaData())
internal ImageFrame(Configuration configuration, int width, int height, MemorySource<TPixel> memorySource)
: this(configuration, width, height, memorySource, new ImageFrameMetaData())
{
}
@ -107,7 +107,7 @@ namespace SixLabors.ImageSharp
Configuration configuration,
int width,
int height,
Memory<TPixel> consumedMemory,
MemorySource<TPixel> memorySource,
ImageFrameMetaData metaData)
{
Guard.NotNull(configuration, nameof(configuration));
@ -117,7 +117,7 @@ namespace SixLabors.ImageSharp
this.configuration = configuration;
this.MemoryAllocator = configuration.MemoryAllocator;
this.PixelBuffer = new Buffer2D<TPixel>(consumedMemory, width, height);
this.PixelBuffer = new Buffer2D<TPixel>(memorySource, width, height);
this.MetaData = metaData;
}

15
src/ImageSharp/Image{TPixel}.cs

@ -84,23 +84,14 @@ namespace SixLabors.ImageSharp
/// <summary>
/// Initializes a new instance of the <see cref="Image{TPixel}"/> class
/// consuming an external buffer instance.
/// wrapping an external <see cref="MemorySource{T}"/>
/// </summary>
internal Image(Configuration configuration, Memory<TPixel> consumedMemory, int width, int height)
: this(configuration, consumedMemory, width, height, new ImageMetaData())
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Image{TPixel}"/> class
/// consuming an external buffer instance.
/// </summary>
internal Image(Configuration configuration, Memory<TPixel> consumedMemory, int width, int height, ImageMetaData metadata)
internal Image(Configuration configuration, MemorySource<TPixel> memorySource, int width, int height, ImageMetaData metadata)
{
this.configuration = configuration;
this.PixelType = new PixelTypeInfo(Unsafe.SizeOf<TPixel>() * 8);
this.MetaData = metadata;
this.frames = new ImageFrameCollection<TPixel>(this, width, height, consumedMemory);
this.frames = new ImageFrameCollection<TPixel>(this, width, height, memorySource);
}
/// <summary>

4
src/ImageSharp/Memory/Buffer2DExtensions.cs

@ -18,7 +18,7 @@ namespace SixLabors.Memory
internal static Span<T> GetSpan<T>(this Buffer2D<T> buffer)
where T : struct
{
return buffer.Buffer.GetSpan();
return buffer.MemorySource.GetSpan();
}
/// <summary>
@ -61,7 +61,7 @@ namespace SixLabors.Memory
public static Memory<T> GetRowMemory<T>(this Buffer2D<T> buffer, int y)
where T : struct
{
return buffer.Buffer.Memory.Slice(y * buffer.Width, buffer.Width);
return buffer.MemorySource.Memory.Slice(y * buffer.Width, buffer.Width);
}
/// <summary>

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

@ -16,31 +16,21 @@ namespace SixLabors.Memory
internal sealed class Buffer2D<T> : IDisposable
where T : struct
{
private BufferManager<T> buffer;
private MemorySource<T> memorySource;
/// <summary>
/// Initializes a new instance of the <see cref="Buffer2D{T}"/> class.
/// </summary>
/// <param name="wrappedBuffer">The buffer to wrap</param>
/// <param name="memorySource">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(BufferManager<T> wrappedBuffer, int width, int height)
public Buffer2D(MemorySource<T> memorySource, int width, int height)
{
this.buffer = wrappedBuffer;
this.memorySource = memorySource;
this.Width = width;
this.Height = height;
}
public Buffer2D(IMemoryOwner<T> ownedMemory, int width, int height)
: this(new BufferManager<T>(ownedMemory), width, height)
{
}
public Buffer2D(Memory<T> observedMemory, int width, int height)
: this(new BufferManager<T>(observedMemory), width, height)
{
}
/// <summary>
/// Gets the width.
/// </summary>
@ -52,11 +42,11 @@ namespace SixLabors.Memory
public int Height { get; private set; }
/// <summary>
/// Gets the backing <see cref="BufferManager{T}"/>
/// Gets the backing <see cref="MemorySource{T}"/>
/// </summary>
public BufferManager<T> Buffer => this.buffer;
public MemorySource<T> MemorySource => this.memorySource;
public Memory<T> Memory => this.Buffer.Memory;
public Memory<T> Memory => this.MemorySource.Memory;
public Span<T> Span => this.Memory.Span;
@ -83,7 +73,7 @@ namespace SixLabors.Memory
/// </summary>
public void Dispose()
{
this.Buffer.Dispose();
this.MemorySource.Dispose();
}
/// <summary>
@ -92,7 +82,7 @@ namespace SixLabors.Memory
/// </summary>
public static void SwapOrCopyContent(Buffer2D<T> destination, Buffer2D<T> source)
{
BufferManager<T>.SwapOrCopyContent(ref destination.buffer, ref source.buffer);
MemorySource<T>.SwapOrCopyContent(ref destination.memorySource, ref source.memorySource);
SwapDimensionData(destination, source);
}

3
src/ImageSharp/Memory/MemoryAllocatorExtensions.cs

@ -17,8 +17,9 @@ namespace SixLabors.Memory
where T : struct
{
IMemoryOwner<T> buffer = memoryAllocator.Allocate<T>(width * height, options);
var memorySource = new MemorySource<T>(buffer, true);
return new Buffer2D<T>(buffer, width, height);
return new Buffer2D<T>(memorySource, width, height);
}
public static Buffer2D<T> Allocate2D<T>(

34
src/ImageSharp/Memory/BufferManager.cs → src/ImageSharp/Memory/MemorySource.cs

@ -9,32 +9,50 @@ namespace SixLabors.Memory
/// <summary>
/// Holds a <see cref="System.Memory{T}"/> that is either OWNED or CONSUMED.
/// 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"/>
/// 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 BufferManager<T> : IDisposable
internal struct MemorySource<T> : IDisposable
{
public BufferManager(IMemoryOwner<T> memoryOwner)
/// <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 BufferManager(Memory<T> memory)
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; }
public bool OwnsMemory => this.MemoryOwner != null;
/// <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;
@ -44,9 +62,9 @@ namespace SixLabors.Memory
/// 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 BufferManager<T> destination, ref BufferManager<T> source)
public static void SwapOrCopyContent(ref MemorySource<T> destination, ref MemorySource<T> source)
{
if (source.OwnsMemory && destination.OwnsMemory)
if (source.HasSwappableContents && destination.HasSwappableContents)
{
SwapContents(ref destination, ref source);
}
@ -67,7 +85,7 @@ namespace SixLabors.Memory
this.MemoryOwner?.Dispose();
}
private static void SwapContents(ref BufferManager<T> a, ref BufferManager<T> b)
private static void SwapContents(ref MemorySource<T> a, ref MemorySource<T> b)
{
IMemoryOwner<T> tempOwner = a.MemoryOwner;
Memory<T> tempMemory = a.Memory;

2
src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs

@ -297,7 +297,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
// TODO: Using a transposed variant of 'firstPassPixels' could eliminate the need for the WeightsWindow.ComputeWeightedColumnSum() method, and improve speed!
using (Buffer2D<Vector4> firstPassPixels = source.MemoryAllocator.Allocate2D<Vector4>(width, source.Height))
{
firstPassPixels.Buffer.Clear();
firstPassPixels.MemorySource.Clear();
ParallelFor.WithTemporaryBuffer(
0,

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

@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// <summary>
/// The buffer containing the weights values.
/// </summary>
private readonly BufferManager<float> buffer;
private readonly MemorySource<float> buffer;
/// <summary>
/// Initializes a new instance of the <see cref="WeightsWindow"/> struct.
@ -47,7 +47,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
this.flatStartIndex = (index * buffer.Width) + left;
this.Left = left;
this.buffer = buffer.Buffer;
this.buffer = buffer.MemorySource;
this.Length = length;
}

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

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

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

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

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

@ -71,7 +71,7 @@ namespace SixLabors.ImageSharp.Tests.Memory
// Assert.Equal(width * y, span.Start);
Assert.Equal(width, span.Length);
Assert.SpanPointsTo(span, buffer.Buffer.MemoryOwner, width * y);
Assert.SpanPointsTo(span, buffer.MemorySource.MemoryOwner, width * y);
}
}
@ -87,7 +87,7 @@ namespace SixLabors.ImageSharp.Tests.Memory
// Assert.Equal(width * y + x, span.Start);
Assert.Equal(width - x, span.Length);
Assert.SpanPointsTo(span, buffer.Buffer.MemoryOwner, width * y + x);
Assert.SpanPointsTo(span, buffer.MemorySource.MemoryOwner, width * y + x);
}
}
@ -99,7 +99,7 @@ namespace SixLabors.ImageSharp.Tests.Memory
{
using (Buffer2D<TestStructs.Foo> buffer = this.MemoryAllocator.Allocate2D<TestStructs.Foo>(width, height))
{
Span<TestStructs.Foo> span = buffer.Buffer.GetSpan();
Span<TestStructs.Foo> span = buffer.MemorySource.GetSpan();
ref TestStructs.Foo actual = ref buffer[x, y];
@ -115,13 +115,13 @@ namespace SixLabors.ImageSharp.Tests.Memory
using (Buffer2D<int> a = this.MemoryAllocator.Allocate2D<int>(10, 5))
using (Buffer2D<int> b = this.MemoryAllocator.Allocate2D<int>(3, 7))
{
IMemoryOwner<int> aa = a.Buffer.MemoryOwner;
IMemoryOwner<int> bb = b.Buffer.MemoryOwner;
IMemoryOwner<int> aa = a.MemorySource.MemoryOwner;
IMemoryOwner<int> bb = b.MemorySource.MemoryOwner;
Buffer2D<int>.SwapOrCopyContent(a, b);
Assert.Equal(bb, a.Buffer.MemoryOwner);
Assert.Equal(aa, b.Buffer.MemoryOwner);
Assert.Equal(bb, a.MemorySource.MemoryOwner);
Assert.Equal(aa, b.MemorySource.MemoryOwner);
Assert.Equal(new Size(3, 7), a.Size());
Assert.Equal(new Size(10, 5), b.Size());

69
tests/ImageSharp.Tests/Memory/BufferManagerTests.cs → tests/ImageSharp.Tests/Memory/MemorySourceTests.cs

@ -12,21 +12,23 @@ using Xunit;
namespace SixLabors.ImageSharp.Tests.Memory
{
public class BufferManagerTests
public class MemorySourceTests
{
public class Construction
{
[Fact]
public void InitializeAsOwner_MemoryOwner_IsPresent()
[Theory]
[InlineData(false)]
[InlineData(true)]
public void InitializeAsOwner(bool isInternalMemorySource)
{
var data = new Rgba32[21];
var mmg = new TestMemoryManager<Rgba32>(data);
var a = new BufferManager<Rgba32>(mmg);
var a = new MemorySource<Rgba32>(mmg, isInternalMemorySource);
Assert.Equal(mmg, a.MemoryOwner);
Assert.Equal(mmg.Memory, a.Memory);
Assert.True(a.OwnsMemory);
Assert.Equal(isInternalMemorySource, a.HasSwappableContents);
}
[Fact]
@ -35,21 +37,23 @@ namespace SixLabors.ImageSharp.Tests.Memory
var data = new Rgba32[21];
var mmg = new TestMemoryManager<Rgba32>(data);
var a = new BufferManager<Rgba32>(mmg.Memory);
var a = new MemorySource<Rgba32>(mmg.Memory);
Assert.Null(a.MemoryOwner);
Assert.Equal(mmg.Memory, a.Memory);
Assert.False(a.OwnsMemory);
Assert.False(a.HasSwappableContents);
}
}
public class Dispose
{
[Fact]
public void WhenOwnershipIsTransfered_ShouldDisposeMemoryOwner()
[Theory]
[InlineData(false)]
[InlineData(true)]
public void WhenOwnershipIsTransfered_ShouldDisposeMemoryOwner(bool isInternalMemorySource)
{
var mmg = new TestMemoryManager<int>(new int[10]);
var bmg = new BufferManager<int>(mmg);
var bmg = new MemorySource<int>(mmg, isInternalMemorySource);
bmg.Dispose();
Assert.True(mmg.IsDisposed);
@ -59,7 +63,7 @@ namespace SixLabors.ImageSharp.Tests.Memory
public void WhenMemoryObserver_ShouldNotDisposeAnything()
{
var mmg = new TestMemoryManager<int>(new int[10]);
var bmg = new BufferManager<int>(mmg.Memory);
var bmg = new MemorySource<int>(mmg.Memory);
bmg.Dispose();
Assert.False(mmg.IsDisposed);
@ -70,18 +74,18 @@ namespace SixLabors.ImageSharp.Tests.Memory
{
private MemoryAllocator MemoryAllocator { get; } = new TestMemoryAllocator();
private BufferManager<T> AllocateBufferManager<T>(int length, AllocationOptions options = AllocationOptions.None)
private MemorySource<T> AllocateMemorySource<T>(int length, AllocationOptions options = AllocationOptions.None)
where T : struct
{
var owner = (IMemoryOwner<T>)this.MemoryAllocator.Allocate<T>(length, options);
return new BufferManager<T>(owner);
IMemoryOwner<T> owner = this.MemoryAllocator.Allocate<T>(length, options);
return new MemorySource<T>(owner, true);
}
[Fact]
public void WhenBothAreMemoryOwners_ShouldSwap()
{
BufferManager<int> a = this.AllocateBufferManager<int>(13);
BufferManager<int> b = this.AllocateBufferManager<int>(17);
MemorySource<int> a = this.AllocateMemorySource<int>(13);
MemorySource<int> b = this.AllocateMemorySource<int>(17);
IMemoryOwner<int> aa = a.MemoryOwner;
IMemoryOwner<int> bb = b.MemoryOwner;
@ -89,7 +93,7 @@ namespace SixLabors.ImageSharp.Tests.Memory
Memory<int> aaa = a.Memory;
Memory<int> bbb = b.Memory;
BufferManager<int>.SwapOrCopyContent(ref a, ref b);
MemorySource<int>.SwapOrCopyContent(ref a, ref b);
Assert.Equal(bb, a.MemoryOwner);
Assert.Equal(aa, b.MemoryOwner);
@ -100,26 +104,27 @@ namespace SixLabors.ImageSharp.Tests.Memory
}
[Theory]
[InlineData(false)]
[InlineData(true)]
public void WhenDestIsNotMemoryOwner_SameSize_ShouldCopy(bool sourceIsOwner)
[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 BufferManager<Rgba32>(destOwner.Memory);
var dest = new MemorySource<Rgba32>(destOwner.Memory);
var sourceOwner = (IMemoryOwner<Rgba32>)this.MemoryAllocator.Allocate<Rgba32>(21);
IMemoryOwner<Rgba32> sourceOwner = this.MemoryAllocator.Allocate<Rgba32>(21);
BufferManager<Rgba32> source = sourceIsOwner
? new BufferManager<Rgba32>(sourceOwner)
: new BufferManager<Rgba32>(sourceOwner.Memory);
MemorySource<Rgba32> source = sourceIsOwner
? new MemorySource<Rgba32>(sourceOwner, isInternalMemorySource)
: new MemorySource<Rgba32>(sourceOwner.Memory);
sourceOwner.Memory.Span[10] = color;
// Act:
BufferManager<Rgba32>.SwapOrCopyContent(ref dest, ref source);
MemorySource<Rgba32>.SwapOrCopyContent(ref dest, ref source);
// Assert:
Assert.Equal(color, dest.Memory.Span[10]);
@ -136,18 +141,18 @@ namespace SixLabors.ImageSharp.Tests.Memory
var color = new Rgba32(1, 2, 3, 4);
var destOwner = new TestMemoryManager<Rgba32>(data);
var dest = new BufferManager<Rgba32>(destOwner.Memory);
var dest = new MemorySource<Rgba32>(destOwner.Memory);
var sourceOwner = (IMemoryOwner<Rgba32>)this.MemoryAllocator.Allocate<Rgba32>(22);
IMemoryOwner<Rgba32> sourceOwner = this.MemoryAllocator.Allocate<Rgba32>(22);
BufferManager<Rgba32> source = sourceIsOwner
? new BufferManager<Rgba32>(sourceOwner)
: new BufferManager<Rgba32>(sourceOwner.Memory);
MemorySource<Rgba32> source = sourceIsOwner
? new MemorySource<Rgba32>(sourceOwner, true)
: new MemorySource<Rgba32>(sourceOwner.Memory);
sourceOwner.Memory.Span[10] = color;
// Act:
Assert.ThrowsAny<InvalidOperationException>(
() => BufferManager<Rgba32>.SwapOrCopyContent(ref dest, ref source)
() => MemorySource<Rgba32>.SwapOrCopyContent(ref dest, ref source)
);
Assert.Equal(color, source.Memory.Span[10]);
Loading…
Cancel
Save