Browse Source

Add single row value delegate with buffer

pull/1108/head
Sergio Pedri 6 years ago
parent
commit
dd4285ded4
  1. 88
      src/ImageSharp/Advanced/IRowAction{TBuffer}.cs
  2. 66
      src/ImageSharp/Advanced/ParallelRowIterator.cs

88
src/ImageSharp/Advanced/IRowAction{TBuffer}.cs

@ -0,0 +1,88 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Advanced
{
/// <summary>
/// Defines the contract for an action that operates on a row with a temporary buffer.
/// </summary>
/// <typeparam name="TBuffer">The type of buffer elements.</typeparam>
public interface IRowAction<TBuffer>
where TBuffer : unmanaged
{
/// <summary>
/// Invokes the method passing the row and a buffer.
/// </summary>
/// <param name="y">The row y coordinate.</param>
/// <param name="span">The contiguous region of memory.</param>
void Invoke(int y, Span<TBuffer> span);
}
internal readonly struct WrappingRowAction<T, TBuffer>
where T : struct, IRowAction<TBuffer>
where TBuffer : unmanaged
{
public readonly int MinY;
public readonly int MaxY;
public readonly int StepY;
public readonly int MaxX;
private readonly MemoryAllocator allocator;
private readonly T action;
[MethodImpl(InliningOptions.ShortMethod)]
public WrappingRowAction(
int minY,
int maxY,
int stepY,
MemoryAllocator allocator,
in T action)
: this(minY, maxY, stepY, 0, allocator, action)
{
}
[MethodImpl(InliningOptions.ShortMethod)]
public WrappingRowAction(
int minY,
int maxY,
int stepY,
int maxX,
MemoryAllocator allocator,
in T action)
{
this.MinY = minY;
this.MaxY = maxY;
this.StepY = stepY;
this.MaxX = maxX;
this.allocator = allocator;
this.action = action;
}
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int i)
{
int yMin = this.MinY + (i * this.StepY);
if (yMin >= this.MaxY)
{
return;
}
int yMax = Math.Min(yMin + this.StepY, this.MaxY);
using IMemoryOwner<TBuffer> buffer = this.allocator.Allocate<TBuffer>(this.MaxX);
Span<TBuffer> span = buffer.Memory.Span;
for (int y = yMin; y < yMax; y++)
{
Unsafe.AsRef(this.action).Invoke(y, span);
}
}
}
}

66
src/ImageSharp/Advanced/ParallelRowIterator.cs

@ -141,6 +141,72 @@ namespace SixLabors.ImageSharp.Advanced
rowAction.Invoke);
}
/// <summary>
/// Iterate through the rows of a rectangle in optimized batches defined by <see cref="RowInterval"/>-s
/// instantiating a temporary buffer for each <paramref name="body"/> invocation.
/// </summary>
/// <typeparam name="T">The type of row action to perform.</typeparam>
/// <typeparam name="TBuffer">The type of buffer elements.</typeparam>
/// <param name="rectangle">The <see cref="Rectangle"/>.</param>
/// <param name="configuration">The <see cref="Configuration"/> to get the parallel settings from.</param>
/// <param name="body">The method body defining the iteration logic on a single <see cref="RowInterval"/>.</param>
public static void IterateRows2<T, TBuffer>(Rectangle rectangle, Configuration configuration, in T body)
where T : struct, IRowAction<TBuffer>
where TBuffer : unmanaged
{
var parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration);
IterateRows2<T, TBuffer>(rectangle, in parallelSettings, in body);
}
/// <summary>
/// Iterate through the rows of a rectangle in optimized batches defined by <see cref="RowInterval"/>-s
/// instantiating a temporary buffer for each <paramref name="body"/> invocation.
/// </summary>
internal static void IterateRows2<T, TBuffer>(
Rectangle rectangle,
in ParallelExecutionSettings parallelSettings,
in T body)
where T : struct, IRowAction<TBuffer>
where TBuffer : unmanaged
{
ValidateRectangle(rectangle);
int top = rectangle.Top;
int bottom = rectangle.Bottom;
int width = rectangle.Width;
int height = rectangle.Height;
int maxSteps = DivideCeil(width * height, parallelSettings.MinimumPixelsProcessedPerTask);
int numOfSteps = Math.Min(parallelSettings.MaxDegreeOfParallelism, maxSteps);
MemoryAllocator allocator = parallelSettings.MemoryAllocator;
// Avoid TPL overhead in this trivial case:
if (numOfSteps == 1)
{
using (IMemoryOwner<TBuffer> buffer = allocator.Allocate<TBuffer>(width))
{
Span<TBuffer> span = buffer.Memory.Span;
for (int y = top; y < bottom; y++)
{
Unsafe.AsRef(body).Invoke(y, span);
}
}
return;
}
int verticalStep = DivideCeil(height, numOfSteps);
var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = numOfSteps };
var rowAction = new WrappingRowAction<T, TBuffer>(top, bottom, verticalStep, width, allocator, in body);
Parallel.For(
0,
numOfSteps,
parallelOptions,
rowAction.Invoke);
}
/// <summary>
/// Iterate through the rows of a rectangle in optimized batches defined by <see cref="RowInterval"/>-s.
/// </summary>

Loading…
Cancel
Save