Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unmanaged pooling MemoryAllocator #1730

Merged
merged 96 commits into from
Dec 9, 2021
Merged
Show file tree
Hide file tree
Changes from 93 commits
Commits
Show all changes
96 commits
Select commit Hold shift + click to select a range
7c1a1af
UniformUnmanagedMemoryPoolMemoryAllocator
antonfirsov Aug 7, 2021
ca17d33
use WeakReference with timer, configure trim period for sandbox
antonfirsov Aug 7, 2021
fd94dbf
fix trimming
antonfirsov Aug 7, 2021
1a41aaa
LoadResizeSaveParallelMemoryStress help text
antonfirsov Aug 8, 2021
aa26b63
Set_MaximumPoolSizeMegabytes_CreateImage_MaximumPoolSizeMegabytes
antonfirsov Aug 8, 2021
172b0a0
MinimumContiguousBlockBytes -> MinimumContiguousBlockSizeBytes
antonfirsov Aug 8, 2021
4251eac
disable CA2015
antonfirsov Aug 8, 2021
c9d1396
comments and docs
antonfirsov Aug 8, 2021
e13edba
fix .NET Framework build error + a few warnings
antonfirsov Aug 8, 2021
0cdbf72
disable MemoryGroupFinalizer_ReturnsToPool on MacOS
antonfirsov Aug 8, 2021
20d6228
make MemoryGroupFinalizer_ReturnsToPool Windows-only
antonfirsov Aug 8, 2021
1a86d6d
try to mitigate 32 bit failures
antonfirsov Aug 8, 2021
32ae7cf
API chunks
antonfirsov Aug 14, 2021
5dd8594
retry allocation on OOM
antonfirsov Aug 16, 2021
f7d9d37
cleanup naming in UniformUnmanagedMemoryPool
antonfirsov Aug 16, 2021
017ee40
Merge branch 'master' into af/UniformUnmanagedMemoryPoolMemoryAllocat…
antonfirsov Oct 16, 2021
a67cb01
fix build after merge
antonfirsov Oct 16, 2021
e233b81
undo unsafe optimizations in ErrorDither
antonfirsov Oct 16, 2021
5839a5a
PreferContiguousImageBuffers implemented
antonfirsov Oct 28, 2021
2472c42
MemoryAllocatorOptions -> MemoryAllocatorSettings,
antonfirsov Oct 28, 2021
3740471
MemoryAllocator.Create & tests
antonfirsov Oct 28, 2021
003e51e
MemoryAllocator.Default
antonfirsov Oct 28, 2021
f81008e
ProcessPixelRows tests
antonfirsov Oct 28, 2021
ce1ac2c
pass pinnable correctly
antonfirsov Oct 28, 2021
1df9e25
ProcessPixelRows
antonfirsov Oct 30, 2021
409cfb1
DangerousGetPixelRowMemory
antonfirsov Oct 30, 2021
e1f15bc
TryGetSinglePixelSpan -> DangerousTryGetSinglePixelMemory
antonfirsov Oct 30, 2021
f178416
test ImageFrame.DangerousGetPixelRowMemory
antonfirsov Oct 30, 2021
ff383c9
Buffer2D.GetRowSpan -> DangerousGetRowSpan
antonfirsov Oct 30, 2021
fb57101
Merge remote-tracking branch 'origin/master' into af/UniformUnmanaged…
antonfirsov Oct 30, 2021
64a9d25
update webp code
antonfirsov Oct 30, 2021
9047fc9
remove ImageFrame.GetPixelRowSpan
antonfirsov Oct 30, 2021
ebe7b9c
Remove Image.DangerousGetRowSpan
antonfirsov Oct 30, 2021
f40fc3a
test exception safety of ProcessPixelRows
antonfirsov Oct 30, 2021
251af06
Merge branch 'master' into af/UniformUnmanagedMemoryPoolMemoryAllocat…
antonfirsov Nov 7, 2021
58a23cd
remove ArrayPoolMemoryAllocator from tests
antonfirsov Nov 7, 2021
74d8be6
fix warning & simplify ClearTransparentPixels
antonfirsov Nov 7, 2021
412ebfb
Remove ArrayPoolMemoryAllocator
antonfirsov Nov 7, 2021
2d6596f
CopyPixelDataTo
antonfirsov Nov 7, 2021
8dda1d5
make GetNetCoreVersion work with preview
antonfirsov Nov 7, 2021
7186767
Disable MemoryAllocator_Create_LimitPoolSize on OSX
antonfirsov Nov 7, 2021
14eef74
Use [ConditionalFact] instead of [PlatformSpecific]
antonfirsov Nov 8, 2021
a9786b8
Merge branch 'master' into af/UniformUnmanagedMemoryPoolMemoryAllocat…
antonfirsov Nov 8, 2021
d09108a
fix test bug in WrapSystemDrawingBitmap_FromBytes_WhenObserved
antonfirsov Nov 8, 2021
2b6d940
update LoadResizeSaveParallelMemoryStress
antonfirsov Nov 9, 2021
7818ae0
temporarily remove try-catch in ImageProcessor<TPixel>.Apply()
antonfirsov Nov 13, 2021
f7b5807
(temporarily?) add RunTestsInLoop.ps1
antonfirsov Nov 13, 2021
bc70d10
Merge branch 'master' into af/UniformUnmanagedMemoryPoolMemoryAllocat…
antonfirsov Nov 13, 2021
bfcb1be
Logging made optional in TestMemoryAllocator
antonfirsov Nov 13, 2021
34a39e5
Relax criteria for Shuffle3
antonfirsov Nov 13, 2021
5c9be36
attempt to fix MemoryAllocator_Create_LimitPoolSize
antonfirsov Nov 13, 2021
ab0480f
memory clearing should not be UniformUnmanagedMemoryPool concern
antonfirsov Nov 19, 2021
ad982c4
emulate leaks in LoadResizeSaveParallelMemoryStress
antonfirsov Nov 19, 2021
5797197
MemoryOwnerFinalizer_ReturnsToPool
antonfirsov Nov 19, 2021
aaacc41
Add missing GC.SuppressFinalize(this)
antonfirsov Nov 19, 2021
27ede23
fixes
antonfirsov Nov 20, 2021
432c03a
remove outdated AllocationOptions.Contiguous
antonfirsov Nov 20, 2021
2b209f0
Merge remote-tracking branch 'origin/master' into af/UniformUnmanaged…
antonfirsov Nov 24, 2021
77e7700
Reimplement buffer ownership management
antonfirsov Nov 24, 2021
b43e963
stress testing improvements
antonfirsov Nov 24, 2021
b685d37
re-enable tests on Unix
antonfirsov Nov 24, 2021
b0b56df
wait a bit more in trim tests
antonfirsov Nov 24, 2021
13a3522
fix tests
antonfirsov Nov 24, 2021
916b31c
Disable MultiplePoolInstances_TrimPeriodElapsed_AllAreTrimmed on MacO…
antonfirsov Nov 25, 2021
d045df2
implement pool finalization & cleanup
antonfirsov Nov 25, 2021
a827390
Docs and null check on Configuration.MemoryAllocator.
antonfirsov Nov 25, 2021
452f31c
Disable RentReturnRelease_SubsequentRentReturnsDifferentHandles on Mac.
antonfirsov Nov 25, 2021
0de983b
Merge branch 'master' into af/UniformUnmanagedMemoryPoolMemoryAllocat…
antonfirsov Nov 25, 2021
308676e
promote Debug-InnerLoop hack
antonfirsov Nov 25, 2021
62943a1
Merge branch 'master' into af/UniformUnmanagedMemoryPoolMemoryAllocat…
antonfirsov Nov 25, 2021
4865ada
cleanup & comments
antonfirsov Nov 25, 2021
8c39628
make MemoryAllocatorSettings a struct
antonfirsov Nov 25, 2021
42546d0
nits
antonfirsov Nov 25, 2021
a02d88a
address some unrelated coverage issues reported by CodeCov for some r…
antonfirsov Nov 25, 2021
a3a6d1d
Always disable MultiplePoolInstances_TrimPeriodElapsed_AllAreTrimmed …
antonfirsov Nov 25, 2021
7ec7447
fix DetectEncoder tests
antonfirsov Nov 25, 2021
9ec8dc7
remove processor exception wrapping forever, fixes #1827
antonfirsov Nov 26, 2021
e10126e
use standard NETCOREAPP3_1_OR_GREATER directive
antonfirsov Nov 26, 2021
ee3265c
add back unsafe optimizations in ErrorDither
antonfirsov Nov 26, 2021
a57250e
oops
antonfirsov Nov 26, 2021
9f55e3f
fix comment
antonfirsov Nov 26, 2021
adaa67e
Merge branch 'master' into af/UniformUnmanagedMemoryPoolMemoryAllocat…
JimBobSquarePants Nov 29, 2021
f10da94
MemoryAllocatorSettings -> MemoryAllocatorOptions
antonfirsov Dec 4, 2021
751fc06
update shared-infrastructure
antonfirsov Dec 4, 2021
66967ab
Merge branch 'master' into af/UniformUnmanagedMemoryPoolMemoryAllocat…
antonfirsov Dec 4, 2021
d35b239
automatic consequence of shared-infrastructure update?
antonfirsov Dec 4, 2021
90316a1
serialize MultiplePoolInstances_TrimPeriodElapsed_AllAreTrimmed
antonfirsov Dec 4, 2021
7eaa5ee
attempt to re-enable MultiplePoolInstances_TrimPeriodElapsed_AllAreTr…
antonfirsov Dec 4, 2021
c90993b
Comments on [Theory] data
antonfirsov Dec 7, 2021
01f055a
Revert "attempt to re-enable MultiplePoolInstances_TrimPeriodElapsed_…
antonfirsov Dec 7, 2021
4986c52
Address local failures caused by high memory load
antonfirsov Dec 7, 2021
5b8be24
Merge branch 'master' into af/UniformUnmanagedMemoryPoolMemoryAllocat…
antonfirsov Dec 7, 2021
64d11ee
Merge branch 'af/UniformUnmanagedMemoryPoolMemoryAllocator-02' of htt…
antonfirsov Dec 7, 2021
53726f5
Merge branch 'master' into af/UniformUnmanagedMemoryPoolMemoryAllocat…
antonfirsov Dec 8, 2021
1e24da4
improve test naming
antonfirsov Dec 9, 2021
5eaa632
Merge branch 'af/UniformUnmanagedMemoryPoolMemoryAllocator-02' of htt…
antonfirsov Dec 9, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -87,15 +87,13 @@
*.eot binary
*.exe binary
*.otf binary
*.pbm binary
*.pdf binary
*.ppt binary
*.pptx binary
*.pvr binary
*.snk binary
*.ttc binary
*.ttf binary
*.wbmp binary
*.woff binary
*.woff2 binary
*.xls binary
Expand Down Expand Up @@ -126,3 +124,9 @@
*.dds filter=lfs diff=lfs merge=lfs -text
*.ktx filter=lfs diff=lfs merge=lfs -text
*.ktx2 filter=lfs diff=lfs merge=lfs -text
*.pam filter=lfs diff=lfs merge=lfs -text
*.pbm filter=lfs diff=lfs merge=lfs -text
*.pgm filter=lfs diff=lfs merge=lfs -text
*.ppm filter=lfs diff=lfs merge=lfs -text
*.pnm filter=lfs diff=lfs merge=lfs -text
*.wbmp filter=lfs diff=lfs merge=lfs -text
4 changes: 3 additions & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
<PropertyGroup>
<!-- This MUST be defined before importing props. -->
<SixLaborsSolutionDirectory>$(MSBuildThisFileDirectory)</SixLaborsSolutionDirectory>

<!-- For some reason Debug-InnerLoop doesn't define DEBUG by default. -->
<DefineConstants Condition="'$(Configuration)' == 'Debug-InnerLoop'">$(DefineConstants);DEBUG</DefineConstants>
</PropertyGroup>

<!-- Import the shared global .props file -->
Expand All @@ -30,5 +33,4 @@
<PropertyGroup Condition="$(Configuration.StartsWith('Release')) == true">
<Optimize>true</Optimize>
</PropertyGroup>

</Project>
4 changes: 2 additions & 2 deletions src/ImageSharp/Advanced/AdvancedImageExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ public static IMemoryGroup<TPixel> GetPixelMemoryGroup<TPixel>(this Image<TPixel
/// <param name="source">The source.</param>
/// <param name="rowIndex">The row.</param>
/// <returns>The <see cref="Span{TPixel}"/></returns>
public static Memory<TPixel> GetPixelRowMemory<TPixel>(this ImageFrame<TPixel> source, int rowIndex)
public static Memory<TPixel> DangerousGetPixelRowMemory<TPixel>(this ImageFrame<TPixel> source, int rowIndex)
where TPixel : unmanaged, IPixel<TPixel>
{
Guard.NotNull(source, nameof(source));
Expand All @@ -161,7 +161,7 @@ public static Memory<TPixel> GetPixelRowMemory<TPixel>(this ImageFrame<TPixel> s
/// <param name="source">The source.</param>
/// <param name="rowIndex">The row.</param>
/// <returns>The <see cref="Span{TPixel}"/></returns>
public static Memory<TPixel> GetPixelRowMemory<TPixel>(this Image<TPixel> source, int rowIndex)
public static Memory<TPixel> DangerousGetPixelRowMemory<TPixel>(this Image<TPixel> source, int rowIndex)
where TPixel : unmanaged, IPixel<TPixel>
{
Guard.NotNull(source, nameof(source));
Expand Down
2 changes: 1 addition & 1 deletion src/ImageSharp/Advanced/AotCompilerTools.cs
Original file line number Diff line number Diff line change
Expand Up @@ -529,7 +529,7 @@ private static void AotCompileDither<TPixel, TDither>()
private static void AotCompileMemoryManagers<TPixel>()
where TPixel : unmanaged, IPixel<TPixel>
{
AotCompileMemoryManager<TPixel, ArrayPoolMemoryAllocator>();
AotCompileMemoryManager<TPixel, UniformUnmanagedMemoryPoolMemoryAllocator>();
AotCompileMemoryManager<TPixel, SimpleGcMemoryAllocator>();
}

Expand Down
14 changes: 14 additions & 0 deletions src/ImageSharp/Common/Helpers/DebugGuard.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,20 @@ public static void IsTrue(bool target, string message)
}
}

/// <summary>
/// Verifies whether a condition (indicating disposed state) is met, throwing an ObjectDisposedException if it's true.
/// </summary>
/// <param name="isDisposed">Whether the object is disposed.</param>
/// <param name="objectName">The name of the object.</param>
[Conditional("DEBUG")]
public static void NotDisposed(bool isDisposed, string objectName)
{
if (isDisposed)
{
throw new ObjectDisposedException(objectName);
}
}

/// <summary>
/// Verifies, that the target span is of same size than the 'other' span.
/// </summary>
Expand Down
4 changes: 4 additions & 0 deletions src/ImageSharp/Common/Helpers/Shuffle/IComponentShuffle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ internal interface IComponentShuffle
/// </summary>
/// <param name="source">The source span of bytes.</param>
/// <param name="dest">The destination span of bytes.</param>
/// <remarks>
/// Implementation can assume that source.Length is less or equal than dest.Length.
/// Loops should iterate using source.Length.
/// </remarks>
void RunFallbackShuffle(ReadOnlySpan<byte> source, Span<byte> dest);
}

Expand Down
5 changes: 3 additions & 2 deletions src/ImageSharp/Common/Helpers/SimdUtils.Shuffle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ public static void Shuffle3<TShuffle>(
TShuffle shuffle)
where TShuffle : struct, IShuffle3
{
// Source length should be smaller than dest length, and divisible by 3.
VerifyShuffle3SpanInput(source, dest);

#if SUPPORTS_RUNTIME_INTRINSICS
Expand Down Expand Up @@ -182,9 +183,9 @@ private static void VerifyShuffle3SpanInput<T>(ReadOnlySpan<T> source, Span<T> d
where T : struct
{
DebugGuard.IsTrue(
source.Length == dest.Length,
source.Length <= dest.Length,
nameof(source),
"Input spans must be of same length!");
"Source should fit into dest!");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did we have a scenario where they weren't the same length? When I wrote this IShuffle3 and IShuffle4 were mirrored implementations designed for working against specific pixel formats. VerifyShuffle4SpanInput still checks for equality.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original version fails EncodeBaseline_WorksWithDiscontiguousBuffers<Bgr24> on current master, we just did not notice it since CI doesn't run tests in Debug (maybe it should?).

this.rgbSpan is longer than this.pixelSpan here:

PixelOperations<TPixel>.Instance.ToRgb24(this.config, this.pixelSpan, this.rgbSpan);

because of the padding added by @br3aker:

this.rgbSpan = MemoryMarshal.Cast<byte, Rgb24>(new byte[RgbSpanByteSize + RgbToYCbCrConverterVectorized.AvxCompatibilityPadding].AsSpan());

I believe we should follow Span.CopyTo(source, dest) semantics in this family of methods, which allows dest to be longer and processes source.Length items.

Copy link
Member

@JimBobSquarePants JimBobSquarePants Nov 26, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CI doesn't run tests in Debug (maybe it should?).

Oh please no! They take long enough!! 🤣

I believe we should follow Span.CopyTo(source, dest) semantics in this family of methods, which allows dest to be longer and processes source.Length items.

I agree then and we should update VerifyShuffle4SpanInput to match.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can introduce a new Checked build configuration, like many C/C++ projects do. It's basically Release build but with all the checks/guards enabled.


DebugGuard.IsTrue(
source.Length % 3 == 0,
Expand Down
39 changes: 35 additions & 4 deletions src/ImageSharp/Configuration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,11 @@ public sealed class Configuration
/// <summary>
/// A lazily initialized configuration default instance.
/// </summary>
private static readonly Lazy<Configuration> Lazy = new Lazy<Configuration>(CreateDefaultInstance);
private static readonly Lazy<Configuration> Lazy = new(CreateDefaultInstance);
private const int DefaultStreamProcessingBufferSize = 8096;
private int streamProcessingBufferSize = DefaultStreamProcessingBufferSize;
private int maxDegreeOfParallelism = Environment.ProcessorCount;
private MemoryAllocator memoryAllocator = MemoryAllocator.Default;

/// <summary>
/// Initializes a new instance of the <see cref="Configuration" /> class.
Expand Down Expand Up @@ -95,6 +96,14 @@ public int StreamProcessingBufferSize
}
}

/// <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>
Expand All @@ -117,9 +126,31 @@ public int StreamProcessingBufferSize
public ImageFormatManager ImageFormatsManager { get; set; } = new ImageFormatManager();

/// <summary>
/// Gets or sets the <see cref="MemoryAllocator"/> that is currently in use.
/// Gets or sets the <see cref="ImageSharp.Memory.MemoryAllocator"/> that is currently in use.
/// Defaults to <see cref="ImageSharp.Memory.MemoryAllocator.Default"/>.
/// <para />
/// Allocators are expensive, so it is strongly recommended to use only one busy instance per process.
/// In case you need to customize it, you can ensure this by changing
/// </summary>
public MemoryAllocator MemoryAllocator { get; set; } = ArrayPoolMemoryAllocator.CreateDefault();
/// <remarks>
/// It's possible to reduce allocator footprint by assigning a custom instance created with
/// <see cref="Memory.MemoryAllocator.Create(MemoryAllocatorOptions)"/>, but note that since the default pooling
/// allocators are expensive, it is strictly recommended to use a single process-wide allocator.
/// You can ensure this by altering the allocator of <see cref="Default"/>, or by implementing custom application logic that
/// manages allocator lifetime.
/// <para />
/// If an allocator has to be dropped for some reason, <see cref="Memory.MemoryAllocator.ReleaseRetainedResources"/>
/// shall be invoked after disposing all associated <see cref="Image"/> instances.
/// </remarks>
public MemoryAllocator MemoryAllocator
{
get => this.memoryAllocator;
set
{
Guard.NotNull(value, nameof(this.MemoryAllocator));
this.memoryAllocator = value;
}
}

/// <summary>
/// Gets the maximum header size of all the formats.
Expand Down Expand Up @@ -165,7 +196,7 @@ public void Configure(IConfigurationModule configuration)
MaxDegreeOfParallelism = this.MaxDegreeOfParallelism,
StreamProcessingBufferSize = this.StreamProcessingBufferSize,
ImageFormatsManager = this.ImageFormatsManager,
MemoryAllocator = this.MemoryAllocator,
memoryAllocator = this.memoryAllocator,
ImageOperationsProvider = this.ImageOperationsProvider,
ReadOrigin = this.ReadOrigin,
FileSystem = this.FileSystem,
Expand Down
18 changes: 9 additions & 9 deletions src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ private void ReadRle<TPixel>(BmpCompression compression, Buffer2D<TPixel> pixels
int newY = Invert(y, height, inverted);
int rowStartIdx = y * width;
Span<byte> bufferRow = bufferSpan.Slice(rowStartIdx, width);
Span<TPixel> pixelRow = pixels.GetRowSpan(newY);
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(newY);

bool rowHasUndefinedPixels = rowsWithUndefinedPixelsSpan[y];
if (rowHasUndefinedPixels)
Expand Down Expand Up @@ -377,7 +377,7 @@ private void ReadRle24<TPixel>(Buffer2D<TPixel> pixels, int width, int height, b
for (int y = 0; y < height; y++)
{
int newY = Invert(y, height, inverted);
Span<TPixel> pixelRow = pixels.GetRowSpan(newY);
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(newY);
bool rowHasUndefinedPixels = rowsWithUndefinedPixelsSpan[y];
if (rowHasUndefinedPixels)
{
Expand Down Expand Up @@ -826,7 +826,7 @@ private void ReadRgbPalette<TPixel>(Buffer2D<TPixel> pixels, byte[] colors, int
int newY = Invert(y, height, inverted);
this.stream.Read(rowSpan);
int offset = 0;
Span<TPixel> pixelRow = pixels.GetRowSpan(newY);
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(newY);

for (int x = 0; x < arrayWidth; x++)
{
Expand Down Expand Up @@ -878,7 +878,7 @@ private void ReadRgb16<TPixel>(Buffer2D<TPixel> pixels, int width, int height, b
{
this.stream.Read(bufferSpan);
int newY = Invert(y, height, inverted);
Span<TPixel> pixelRow = pixels.GetRowSpan(newY);
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(newY);

int offset = 0;
for (int x = 0; x < width; x++)
Expand Down Expand Up @@ -933,7 +933,7 @@ private void ReadRgb24<TPixel>(Buffer2D<TPixel> pixels, int width, int height, b
{
this.stream.Read(rowSpan);
int newY = Invert(y, height, inverted);
Span<TPixel> pixelSpan = pixels.GetRowSpan(newY);
Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(newY);
PixelOperations<TPixel>.Instance.FromBgr24Bytes(
this.Configuration,
rowSpan,
Expand Down Expand Up @@ -961,7 +961,7 @@ private void ReadRgb32Fast<TPixel>(Buffer2D<TPixel> pixels, int width, int heigh
{
this.stream.Read(rowSpan);
int newY = Invert(y, height, inverted);
Span<TPixel> pixelSpan = pixels.GetRowSpan(newY);
Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(newY);
PixelOperations<TPixel>.Instance.FromBgra32Bytes(
this.Configuration,
rowSpan,
Expand Down Expand Up @@ -1031,7 +1031,7 @@ private void ReadRgb32Slow<TPixel>(Buffer2D<TPixel> pixels, int width, int heigh
this.stream.Read(rowSpan);

int newY = Invert(y, height, inverted);
Span<TPixel> pixelSpan = pixels.GetRowSpan(newY);
Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(newY);

PixelOperations<TPixel>.Instance.FromBgra32Bytes(
this.Configuration,
Expand All @@ -1054,7 +1054,7 @@ private void ReadRgb32Slow<TPixel>(Buffer2D<TPixel> pixels, int width, int heigh
width);

int newY = Invert(y, height, inverted);
Span<TPixel> pixelSpan = pixels.GetRowSpan(newY);
Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(newY);

for (int x = 0; x < width; x++)
{
Expand Down Expand Up @@ -1109,7 +1109,7 @@ private void ReadRgb32BitFields<TPixel>(Buffer2D<TPixel> pixels, int width, int
{
this.stream.Read(bufferSpan);
int newY = Invert(y, height, inverted);
Span<TPixel> pixelRow = pixels.GetRowSpan(newY);
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(newY);

int offset = 0;
for (int x = 0; x < width; x++)
Expand Down
20 changes: 10 additions & 10 deletions src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ private void Write32Bit<TPixel>(Stream stream, Buffer2D<TPixel> pixels)

for (int y = pixels.Height - 1; y >= 0; y--)
{
Span<TPixel> pixelSpan = pixels.GetRowSpan(y);
Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.ToBgra32Bytes(
this.configuration,
pixelSpan,
Expand All @@ -300,7 +300,7 @@ private void Write24Bit<TPixel>(Stream stream, Buffer2D<TPixel> pixels)

for (int y = pixels.Height - 1; y >= 0; y--)
{
Span<TPixel> pixelSpan = pixels.GetRowSpan(y);
Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.ToBgr24Bytes(
this.configuration,
pixelSpan,
Expand All @@ -326,7 +326,7 @@ private void Write16Bit<TPixel>(Stream stream, Buffer2D<TPixel> pixels)

for (int y = pixels.Height - 1; y >= 0; y--)
{
Span<TPixel> pixelSpan = pixels.GetRowSpan(y);
Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(y);

PixelOperations<TPixel>.Instance.ToBgra5551Bytes(
this.configuration,
Expand Down Expand Up @@ -379,7 +379,7 @@ private void Write8BitColor<TPixel>(Stream stream, ImageFrame<TPixel> image, Spa

for (int y = image.Height - 1; y >= 0; y--)
{
ReadOnlySpan<byte> pixelSpan = quantized.GetPixelRowSpan(y);
ReadOnlySpan<byte> pixelSpan = quantized.DangerousGetRowSpan(y);
stream.Write(pixelSpan);

for (int i = 0; i < this.padding; i++)
Expand Down Expand Up @@ -413,10 +413,10 @@ private void Write8BitGray<TPixel>(Stream stream, ImageFrame<TPixel> image, Span
}

stream.Write(colorPalette);

Buffer2D<TPixel> imageBuffer = image.PixelBuffer;
for (int y = image.Height - 1; y >= 0; y--)
{
ReadOnlySpan<TPixel> inputPixelRow = image.GetPixelRowSpan(y);
ReadOnlySpan<TPixel> inputPixelRow = imageBuffer.DangerousGetRowSpan(y);
ReadOnlySpan<byte> outputPixelRow = MemoryMarshal.AsBytes(inputPixelRow);
stream.Write(outputPixelRow);

Expand Down Expand Up @@ -447,11 +447,11 @@ private void Write4BitColor<TPixel>(Stream stream, ImageFrame<TPixel> image)
ReadOnlySpan<TPixel> quantizedColorPalette = quantized.Palette.Span;
this.WriteColorPalette(stream, quantizedColorPalette, colorPalette);

ReadOnlySpan<byte> pixelRowSpan = quantized.GetPixelRowSpan(0);
ReadOnlySpan<byte> pixelRowSpan = quantized.DangerousGetRowSpan(0);
int rowPadding = pixelRowSpan.Length % 2 != 0 ? this.padding - 1 : this.padding;
for (int y = image.Height - 1; y >= 0; y--)
{
pixelRowSpan = quantized.GetPixelRowSpan(y);
pixelRowSpan = quantized.DangerousGetRowSpan(y);

int endIdx = pixelRowSpan.Length % 2 == 0 ? pixelRowSpan.Length : pixelRowSpan.Length - 1;
for (int i = 0; i < endIdx; i += 2)
Expand Down Expand Up @@ -491,11 +491,11 @@ private void Write1BitColor<TPixel>(Stream stream, ImageFrame<TPixel> image)
ReadOnlySpan<TPixel> quantizedColorPalette = quantized.Palette.Span;
this.WriteColorPalette(stream, quantizedColorPalette, colorPalette);

ReadOnlySpan<byte> quantizedPixelRow = quantized.GetPixelRowSpan(0);
ReadOnlySpan<byte> quantizedPixelRow = quantized.DangerousGetRowSpan(0);
int rowPadding = quantizedPixelRow.Length % 8 != 0 ? this.padding - 1 : this.padding;
for (int y = image.Height - 1; y >= 0; y--)
{
quantizedPixelRow = quantized.GetPixelRowSpan(y);
quantizedPixelRow = quantized.DangerousGetRowSpan(y);

int endIdx = quantizedPixelRow.Length % 8 == 0 ? quantizedPixelRow.Length : quantizedPixelRow.Length - 8;
for (int i = 0; i < endIdx; i += 8)
Expand Down
4 changes: 2 additions & 2 deletions src/ImageSharp/Formats/Gif/GifDecoderCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,7 @@ private void ReadFrameColors<TPixel>(ref Image<TPixel> image, ref ImageFrame<TPi

for (int y = descriptorTop; y < descriptorBottom && y < imageHeight; y++)
{
ref byte indicesRowRef = ref MemoryMarshal.GetReference(indices.GetRowSpan(y - descriptorTop));
ref byte indicesRowRef = ref MemoryMarshal.GetReference(indices.DangerousGetRowSpan(y - descriptorTop));

// Check if this image is interlaced.
int writeY; // the target y offset to write to
Expand Down Expand Up @@ -481,7 +481,7 @@ private void ReadFrameColors<TPixel>(ref Image<TPixel> image, ref ImageFrame<TPi
writeY = y;
}

ref TPixel rowRef = ref MemoryMarshal.GetReference(imageFrame.GetPixelRowSpan(writeY));
ref TPixel rowRef = ref MemoryMarshal.GetReference(imageFrame.PixelBuffer.DangerousGetRowSpan(writeY));

if (!transFlag)
{
Expand Down
4 changes: 2 additions & 2 deletions src/ImageSharp/Formats/Gif/LzwDecoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,14 +115,14 @@ public void DecodePixels(int dataSize, Buffer2D<byte> pixels)
int y = 0;
int x = 0;
int rowMax = width;
ref byte pixelsRowRef = ref MemoryMarshal.GetReference(pixels.GetRowSpan(y));
ref byte pixelsRowRef = ref MemoryMarshal.GetReference(pixels.DangerousGetRowSpan(y));
while (xyz < length)
{
// Reset row reference.
if (xyz == rowMax)
{
x = 0;
pixelsRowRef = ref MemoryMarshal.GetReference(pixels.GetRowSpan(++y));
pixelsRowRef = ref MemoryMarshal.GetReference(pixels.DangerousGetRowSpan(++y));
rowMax = (y * width) + width;
}

Expand Down
2 changes: 1 addition & 1 deletion src/ImageSharp/Formats/Gif/LzwEncoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ private void Compress(Buffer2D<byte> indexedPixels, int initialBits, Stream stre

for (int y = 0; y < indexedPixels.Height; y++)
{
ref byte rowSpanRef = ref MemoryMarshal.GetReference(indexedPixels.GetRowSpan(y));
ref byte rowSpanRef = ref MemoryMarshal.GetReference(indexedPixels.DangerousGetRowSpan(y));
int offsetX = y == 0 ? 1 : 0;

for (int x = offsetX; x < indexedPixels.Width; x++)
Expand Down
Loading