Skip to content

Commit ee83e99

Browse files
authored
Merge pull request #1730 from SixLabors/af/UniformUnmanagedMemoryPoolMemoryAllocator-02
Unmanaged pooling MemoryAllocator
2 parents 4587649 + 5eaa632 commit ee83e99

File tree

189 files changed

+5120
-1753
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

189 files changed

+5120
-1753
lines changed

.gitattributes

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,15 +87,13 @@
8787
*.eot binary
8888
*.exe binary
8989
*.otf binary
90-
*.pbm binary
9190
*.pdf binary
9291
*.ppt binary
9392
*.pptx binary
9493
*.pvr binary
9594
*.snk binary
9695
*.ttc binary
9796
*.ttf binary
98-
*.wbmp binary
9997
*.woff binary
10098
*.woff2 binary
10199
*.xls binary
@@ -126,3 +124,9 @@
126124
*.dds filter=lfs diff=lfs merge=lfs -text
127125
*.ktx filter=lfs diff=lfs merge=lfs -text
128126
*.ktx2 filter=lfs diff=lfs merge=lfs -text
127+
*.pam filter=lfs diff=lfs merge=lfs -text
128+
*.pbm filter=lfs diff=lfs merge=lfs -text
129+
*.pgm filter=lfs diff=lfs merge=lfs -text
130+
*.ppm filter=lfs diff=lfs merge=lfs -text
131+
*.pnm filter=lfs diff=lfs merge=lfs -text
132+
*.wbmp filter=lfs diff=lfs merge=lfs -text

Directory.Build.props

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
<PropertyGroup>
1414
<!-- This MUST be defined before importing props. -->
1515
<SixLaborsSolutionDirectory>$(MSBuildThisFileDirectory)</SixLaborsSolutionDirectory>
16+
17+
<!-- For some reason Debug-InnerLoop doesn't define DEBUG by default. -->
18+
<DefineConstants Condition="'$(Configuration)' == 'Debug-InnerLoop'">$(DefineConstants);DEBUG</DefineConstants>
1619
</PropertyGroup>
1720

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

src/ImageSharp/Advanced/AdvancedImageExtensions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ public static IMemoryGroup<TPixel> GetPixelMemoryGroup<TPixel>(this Image<TPixel
143143
/// <param name="source">The source.</param>
144144
/// <param name="rowIndex">The row.</param>
145145
/// <returns>The <see cref="Span{TPixel}"/></returns>
146-
public static Memory<TPixel> GetPixelRowMemory<TPixel>(this ImageFrame<TPixel> source, int rowIndex)
146+
public static Memory<TPixel> DangerousGetPixelRowMemory<TPixel>(this ImageFrame<TPixel> source, int rowIndex)
147147
where TPixel : unmanaged, IPixel<TPixel>
148148
{
149149
Guard.NotNull(source, nameof(source));
@@ -161,7 +161,7 @@ public static Memory<TPixel> GetPixelRowMemory<TPixel>(this ImageFrame<TPixel> s
161161
/// <param name="source">The source.</param>
162162
/// <param name="rowIndex">The row.</param>
163163
/// <returns>The <see cref="Span{TPixel}"/></returns>
164-
public static Memory<TPixel> GetPixelRowMemory<TPixel>(this Image<TPixel> source, int rowIndex)
164+
public static Memory<TPixel> DangerousGetPixelRowMemory<TPixel>(this Image<TPixel> source, int rowIndex)
165165
where TPixel : unmanaged, IPixel<TPixel>
166166
{
167167
Guard.NotNull(source, nameof(source));

src/ImageSharp/Advanced/AotCompilerTools.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -529,7 +529,7 @@ private static void AotCompileDither<TPixel, TDither>()
529529
private static void AotCompileMemoryManagers<TPixel>()
530530
where TPixel : unmanaged, IPixel<TPixel>
531531
{
532-
AotCompileMemoryManager<TPixel, ArrayPoolMemoryAllocator>();
532+
AotCompileMemoryManager<TPixel, UniformUnmanagedMemoryPoolMemoryAllocator>();
533533
AotCompileMemoryManager<TPixel, SimpleGcMemoryAllocator>();
534534
}
535535

src/ImageSharp/Common/Helpers/DebugGuard.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,20 @@ public static void IsTrue(bool target, string message)
2626
}
2727
}
2828

29+
/// <summary>
30+
/// Verifies whether a condition (indicating disposed state) is met, throwing an ObjectDisposedException if it's true.
31+
/// </summary>
32+
/// <param name="isDisposed">Whether the object is disposed.</param>
33+
/// <param name="objectName">The name of the object.</param>
34+
[Conditional("DEBUG")]
35+
public static void NotDisposed(bool isDisposed, string objectName)
36+
{
37+
if (isDisposed)
38+
{
39+
throw new ObjectDisposedException(objectName);
40+
}
41+
}
42+
2943
/// <summary>
3044
/// Verifies, that the target span is of same size than the 'other' span.
3145
/// </summary>

src/ImageSharp/Common/Helpers/Shuffle/IComponentShuffle.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ internal interface IComponentShuffle
2828
/// </summary>
2929
/// <param name="source">The source span of bytes.</param>
3030
/// <param name="dest">The destination span of bytes.</param>
31+
/// <remarks>
32+
/// Implementation can assume that source.Length is less or equal than dest.Length.
33+
/// Loops should iterate using source.Length.
34+
/// </remarks>
3135
void RunFallbackShuffle(ReadOnlySpan<byte> source, Span<byte> dest);
3236
}
3337

src/ImageSharp/Common/Helpers/SimdUtils.Shuffle.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ public static void Shuffle3<TShuffle>(
7777
TShuffle shuffle)
7878
where TShuffle : struct, IShuffle3
7979
{
80+
// Source length should be smaller than dest length, and divisible by 3.
8081
VerifyShuffle3SpanInput(source, dest);
8182

8283
#if SUPPORTS_RUNTIME_INTRINSICS
@@ -182,9 +183,9 @@ private static void VerifyShuffle3SpanInput<T>(ReadOnlySpan<T> source, Span<T> d
182183
where T : struct
183184
{
184185
DebugGuard.IsTrue(
185-
source.Length == dest.Length,
186+
source.Length <= dest.Length,
186187
nameof(source),
187-
"Input spans must be of same length!");
188+
"Source should fit into dest!");
188189

189190
DebugGuard.IsTrue(
190191
source.Length % 3 == 0,

src/ImageSharp/Configuration.cs

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,11 @@ public sealed class Configuration
2626
/// <summary>
2727
/// A lazily initialized configuration default instance.
2828
/// </summary>
29-
private static readonly Lazy<Configuration> Lazy = new Lazy<Configuration>(CreateDefaultInstance);
29+
private static readonly Lazy<Configuration> Lazy = new(CreateDefaultInstance);
3030
private const int DefaultStreamProcessingBufferSize = 8096;
3131
private int streamProcessingBufferSize = DefaultStreamProcessingBufferSize;
3232
private int maxDegreeOfParallelism = Environment.ProcessorCount;
33+
private MemoryAllocator memoryAllocator = MemoryAllocator.Default;
3334

3435
/// <summary>
3536
/// Initializes a new instance of the <see cref="Configuration" /> class.
@@ -95,6 +96,14 @@ public int StreamProcessingBufferSize
9596
}
9697
}
9798

99+
/// <summary>
100+
/// Gets or sets a value indicating whether to force image buffers to be contiguous whenever possible.
101+
/// </summary>
102+
/// <remarks>
103+
/// Contiguous allocations are not possible, if the image needs a buffer larger than <see cref="int.MaxValue"/>.
104+
/// </remarks>
105+
public bool PreferContiguousImageBuffers { get; set; }
106+
98107
/// <summary>
99108
/// Gets a set of properties for the Configuration.
100109
/// </summary>
@@ -117,9 +126,31 @@ public int StreamProcessingBufferSize
117126
public ImageFormatManager ImageFormatsManager { get; set; } = new ImageFormatManager();
118127

119128
/// <summary>
120-
/// Gets or sets the <see cref="MemoryAllocator"/> that is currently in use.
129+
/// Gets or sets the <see cref="ImageSharp.Memory.MemoryAllocator"/> that is currently in use.
130+
/// Defaults to <see cref="ImageSharp.Memory.MemoryAllocator.Default"/>.
131+
/// <para />
132+
/// Allocators are expensive, so it is strongly recommended to use only one busy instance per process.
133+
/// In case you need to customize it, you can ensure this by changing
121134
/// </summary>
122-
public MemoryAllocator MemoryAllocator { get; set; } = ArrayPoolMemoryAllocator.CreateDefault();
135+
/// <remarks>
136+
/// It's possible to reduce allocator footprint by assigning a custom instance created with
137+
/// <see cref="Memory.MemoryAllocator.Create(MemoryAllocatorOptions)"/>, but note that since the default pooling
138+
/// allocators are expensive, it is strictly recommended to use a single process-wide allocator.
139+
/// You can ensure this by altering the allocator of <see cref="Default"/>, or by implementing custom application logic that
140+
/// manages allocator lifetime.
141+
/// <para />
142+
/// If an allocator has to be dropped for some reason, <see cref="Memory.MemoryAllocator.ReleaseRetainedResources"/>
143+
/// shall be invoked after disposing all associated <see cref="Image"/> instances.
144+
/// </remarks>
145+
public MemoryAllocator MemoryAllocator
146+
{
147+
get => this.memoryAllocator;
148+
set
149+
{
150+
Guard.NotNull(value, nameof(this.MemoryAllocator));
151+
this.memoryAllocator = value;
152+
}
153+
}
123154

124155
/// <summary>
125156
/// Gets the maximum header size of all the formats.
@@ -165,7 +196,7 @@ public void Configure(IConfigurationModule configuration)
165196
MaxDegreeOfParallelism = this.MaxDegreeOfParallelism,
166197
StreamProcessingBufferSize = this.StreamProcessingBufferSize,
167198
ImageFormatsManager = this.ImageFormatsManager,
168-
MemoryAllocator = this.MemoryAllocator,
199+
memoryAllocator = this.memoryAllocator,
169200
ImageOperationsProvider = this.ImageOperationsProvider,
170201
ReadOrigin = this.ReadOrigin,
171202
FileSystem = this.FileSystem,

src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ private void ReadRle<TPixel>(BmpCompression compression, Buffer2D<TPixel> pixels
306306
int newY = Invert(y, height, inverted);
307307
int rowStartIdx = y * width;
308308
Span<byte> bufferRow = bufferSpan.Slice(rowStartIdx, width);
309-
Span<TPixel> pixelRow = pixels.GetRowSpan(newY);
309+
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(newY);
310310

311311
bool rowHasUndefinedPixels = rowsWithUndefinedPixelsSpan[y];
312312
if (rowHasUndefinedPixels)
@@ -377,7 +377,7 @@ private void ReadRle24<TPixel>(Buffer2D<TPixel> pixels, int width, int height, b
377377
for (int y = 0; y < height; y++)
378378
{
379379
int newY = Invert(y, height, inverted);
380-
Span<TPixel> pixelRow = pixels.GetRowSpan(newY);
380+
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(newY);
381381
bool rowHasUndefinedPixels = rowsWithUndefinedPixelsSpan[y];
382382
if (rowHasUndefinedPixels)
383383
{
@@ -826,7 +826,7 @@ private void ReadRgbPalette<TPixel>(Buffer2D<TPixel> pixels, byte[] colors, int
826826
int newY = Invert(y, height, inverted);
827827
this.stream.Read(rowSpan);
828828
int offset = 0;
829-
Span<TPixel> pixelRow = pixels.GetRowSpan(newY);
829+
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(newY);
830830

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

883883
int offset = 0;
884884
for (int x = 0; x < width; x++)
@@ -933,7 +933,7 @@ private void ReadRgb24<TPixel>(Buffer2D<TPixel> pixels, int width, int height, b
933933
{
934934
this.stream.Read(rowSpan);
935935
int newY = Invert(y, height, inverted);
936-
Span<TPixel> pixelSpan = pixels.GetRowSpan(newY);
936+
Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(newY);
937937
PixelOperations<TPixel>.Instance.FromBgr24Bytes(
938938
this.Configuration,
939939
rowSpan,
@@ -961,7 +961,7 @@ private void ReadRgb32Fast<TPixel>(Buffer2D<TPixel> pixels, int width, int heigh
961961
{
962962
this.stream.Read(rowSpan);
963963
int newY = Invert(y, height, inverted);
964-
Span<TPixel> pixelSpan = pixels.GetRowSpan(newY);
964+
Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(newY);
965965
PixelOperations<TPixel>.Instance.FromBgra32Bytes(
966966
this.Configuration,
967967
rowSpan,
@@ -1031,7 +1031,7 @@ private void ReadRgb32Slow<TPixel>(Buffer2D<TPixel> pixels, int width, int heigh
10311031
this.stream.Read(rowSpan);
10321032

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

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

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

10591059
for (int x = 0; x < width; x++)
10601060
{
@@ -1109,7 +1109,7 @@ private void ReadRgb32BitFields<TPixel>(Buffer2D<TPixel> pixels, int width, int
11091109
{
11101110
this.stream.Read(bufferSpan);
11111111
int newY = Invert(y, height, inverted);
1112-
Span<TPixel> pixelRow = pixels.GetRowSpan(newY);
1112+
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(newY);
11131113

11141114
int offset = 0;
11151115
for (int x = 0; x < width; x++)

0 commit comments

Comments
 (0)