Skip to content

Commit dd4285d

Browse files
committed
Add single row value delegate with buffer
1 parent d6fb30e commit dd4285d

File tree

2 files changed

+154
-0
lines changed

2 files changed

+154
-0
lines changed
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// Copyright (c) Six Labors and contributors.
2+
// Licensed under the Apache License, Version 2.0.
3+
4+
using System;
5+
using System.Buffers;
6+
using System.Runtime.CompilerServices;
7+
using SixLabors.ImageSharp.Memory;
8+
9+
namespace SixLabors.ImageSharp.Advanced
10+
{
11+
/// <summary>
12+
/// Defines the contract for an action that operates on a row with a temporary buffer.
13+
/// </summary>
14+
/// <typeparam name="TBuffer">The type of buffer elements.</typeparam>
15+
public interface IRowAction<TBuffer>
16+
where TBuffer : unmanaged
17+
{
18+
/// <summary>
19+
/// Invokes the method passing the row and a buffer.
20+
/// </summary>
21+
/// <param name="y">The row y coordinate.</param>
22+
/// <param name="span">The contiguous region of memory.</param>
23+
void Invoke(int y, Span<TBuffer> span);
24+
}
25+
26+
internal readonly struct WrappingRowAction<T, TBuffer>
27+
where T : struct, IRowAction<TBuffer>
28+
where TBuffer : unmanaged
29+
{
30+
public readonly int MinY;
31+
public readonly int MaxY;
32+
public readonly int StepY;
33+
public readonly int MaxX;
34+
35+
private readonly MemoryAllocator allocator;
36+
private readonly T action;
37+
38+
[MethodImpl(InliningOptions.ShortMethod)]
39+
public WrappingRowAction(
40+
int minY,
41+
int maxY,
42+
int stepY,
43+
MemoryAllocator allocator,
44+
in T action)
45+
: this(minY, maxY, stepY, 0, allocator, action)
46+
{
47+
}
48+
49+
[MethodImpl(InliningOptions.ShortMethod)]
50+
public WrappingRowAction(
51+
int minY,
52+
int maxY,
53+
int stepY,
54+
int maxX,
55+
MemoryAllocator allocator,
56+
in T action)
57+
{
58+
this.MinY = minY;
59+
this.MaxY = maxY;
60+
this.StepY = stepY;
61+
this.MaxX = maxX;
62+
this.allocator = allocator;
63+
this.action = action;
64+
}
65+
66+
[MethodImpl(InliningOptions.ShortMethod)]
67+
public void Invoke(int i)
68+
{
69+
int yMin = this.MinY + (i * this.StepY);
70+
71+
if (yMin >= this.MaxY)
72+
{
73+
return;
74+
}
75+
76+
int yMax = Math.Min(yMin + this.StepY, this.MaxY);
77+
78+
using IMemoryOwner<TBuffer> buffer = this.allocator.Allocate<TBuffer>(this.MaxX);
79+
80+
Span<TBuffer> span = buffer.Memory.Span;
81+
82+
for (int y = yMin; y < yMax; y++)
83+
{
84+
Unsafe.AsRef(this.action).Invoke(y, span);
85+
}
86+
}
87+
}
88+
}

src/ImageSharp/Advanced/ParallelRowIterator.cs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,72 @@ internal static void IterateRows<T, TBuffer>(
141141
rowAction.Invoke);
142142
}
143143

144+
/// <summary>
145+
/// Iterate through the rows of a rectangle in optimized batches defined by <see cref="RowInterval"/>-s
146+
/// instantiating a temporary buffer for each <paramref name="body"/> invocation.
147+
/// </summary>
148+
/// <typeparam name="T">The type of row action to perform.</typeparam>
149+
/// <typeparam name="TBuffer">The type of buffer elements.</typeparam>
150+
/// <param name="rectangle">The <see cref="Rectangle"/>.</param>
151+
/// <param name="configuration">The <see cref="Configuration"/> to get the parallel settings from.</param>
152+
/// <param name="body">The method body defining the iteration logic on a single <see cref="RowInterval"/>.</param>
153+
public static void IterateRows2<T, TBuffer>(Rectangle rectangle, Configuration configuration, in T body)
154+
where T : struct, IRowAction<TBuffer>
155+
where TBuffer : unmanaged
156+
{
157+
var parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration);
158+
IterateRows2<T, TBuffer>(rectangle, in parallelSettings, in body);
159+
}
160+
161+
/// <summary>
162+
/// Iterate through the rows of a rectangle in optimized batches defined by <see cref="RowInterval"/>-s
163+
/// instantiating a temporary buffer for each <paramref name="body"/> invocation.
164+
/// </summary>
165+
internal static void IterateRows2<T, TBuffer>(
166+
Rectangle rectangle,
167+
in ParallelExecutionSettings parallelSettings,
168+
in T body)
169+
where T : struct, IRowAction<TBuffer>
170+
where TBuffer : unmanaged
171+
{
172+
ValidateRectangle(rectangle);
173+
174+
int top = rectangle.Top;
175+
int bottom = rectangle.Bottom;
176+
int width = rectangle.Width;
177+
int height = rectangle.Height;
178+
179+
int maxSteps = DivideCeil(width * height, parallelSettings.MinimumPixelsProcessedPerTask);
180+
int numOfSteps = Math.Min(parallelSettings.MaxDegreeOfParallelism, maxSteps);
181+
MemoryAllocator allocator = parallelSettings.MemoryAllocator;
182+
183+
// Avoid TPL overhead in this trivial case:
184+
if (numOfSteps == 1)
185+
{
186+
using (IMemoryOwner<TBuffer> buffer = allocator.Allocate<TBuffer>(width))
187+
{
188+
Span<TBuffer> span = buffer.Memory.Span;
189+
190+
for (int y = top; y < bottom; y++)
191+
{
192+
Unsafe.AsRef(body).Invoke(y, span);
193+
}
194+
}
195+
196+
return;
197+
}
198+
199+
int verticalStep = DivideCeil(height, numOfSteps);
200+
var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = numOfSteps };
201+
var rowAction = new WrappingRowAction<T, TBuffer>(top, bottom, verticalStep, width, allocator, in body);
202+
203+
Parallel.For(
204+
0,
205+
numOfSteps,
206+
parallelOptions,
207+
rowAction.Invoke);
208+
}
209+
144210
/// <summary>
145211
/// Iterate through the rows of a rectangle in optimized batches defined by <see cref="RowInterval"/>-s.
146212
/// </summary>

0 commit comments

Comments
 (0)