Browse Source

Memory usage reduction, sequential mode disabled

af/merge-core
Sergio Pedri 6 years ago
parent
commit
3dfb3171cf
  1. 15
      src/ImageSharp/Common/Helpers/Buffer2DUtils.cs
  2. 101
      src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs

15
src/ImageSharp/Common/Helpers/Buffer2DUtils.cs

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@ -62,7 +63,7 @@ namespace SixLabors.ImageSharp
}
/// <summary>
/// Computes the sum of vectors in <paramref name="targetRow"/> weighted by the kernel weight values.
/// Computes the sum of vectors in <paramref name="targetRow"/> weighted by the kernel weight values and accumulates the partial results.
/// </summary>
/// <param name="kernel">The 1D convolution kernel.</param>
/// <param name="sourceValues">The source frame.</param>
@ -73,16 +74,20 @@ namespace SixLabors.ImageSharp
/// <param name="maxRow">The maximum working area row.</param>
/// <param name="minColumn">The minimum working area column.</param>
/// <param name="maxColumn">The maximum working area column.</param>
public static void Convolve4(
/// <param name="z">The weight factor for the real component of the complex pixel values.</param>
/// <param name="w">The weight factor for the imaginary component of the complex pixel values.</param>
public static void Convolve4AndAccumulatePartials(
Span<Complex64> kernel,
Buffer2D<ComplexVector4> sourceValues,
Span<ComplexVector4> targetRow,
Span<Vector4> targetRow,
int row,
int column,
int minRow,
int maxRow,
int minColumn,
int maxColumn)
int maxColumn,
float z,
float w)
{
ComplexVector4 vector = default;
int kernelLength = kernel.Length;
@ -99,7 +104,7 @@ namespace SixLabors.ImageSharp
vector.Sum(Unsafe.Add(ref baseRef, x) * Unsafe.Add(ref sourceRef, offsetX));
}
targetRow[column] = vector;
targetRow[column] += vector.WeightedSum(z, w);
}
}
}

101
src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs

@ -281,26 +281,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
// Create a 0-filled buffer to use to store the result of the component convolutions
using (Buffer2D<Vector4> processing = this.Configuration.MemoryAllocator.Allocate2D<Vector4>(source.Size(), AllocationOptions.Clean))
using (Buffer2D<ComplexVector4> firstPassValues = this.Configuration.MemoryAllocator.Allocate2D<ComplexVector4>(source.Size()))
{
if (this.executionMode == BokehBlurExecutionMode.PreferLowMemoryUsage)
{
// Memory usage priority: allocate a shared buffer and execute the second convolution in sequential mode
using (Buffer2D<ComplexVector4> buffer = this.Configuration.MemoryAllocator.Allocate2D<ComplexVector4>(source.Width, source.Height + this.radius))
using (Buffer2D<ComplexVector4> firstPassBuffer = buffer.Slice(this.radius, source.Height))
using (Buffer2D<ComplexVector4> secondPassBuffer = buffer.Slice(0, source.Height))
{
this.OnFrameApplyCore(source, this.SourceRectangle, this.Configuration, processing, firstPassBuffer, secondPassBuffer);
}
}
else
{
// Performance priority: allocate two independent buffers and execute both convolutions in parallel mode
using (Buffer2D<ComplexVector4> firstPassValues = this.Configuration.MemoryAllocator.Allocate2D<ComplexVector4>(source.Size()))
using (Buffer2D<ComplexVector4> secondPassBuffer = this.Configuration.MemoryAllocator.Allocate2D<ComplexVector4>(source.Size()))
{
this.OnFrameApplyCore(source, this.SourceRectangle, this.Configuration, processing, firstPassValues, secondPassBuffer);
}
}
// Perform the 1D convolutions on all the kernel components and accumulate the results
this.OnFrameApplyCore(source, this.SourceRectangle, this.Configuration, processing, firstPassValues);
// Apply the inverse gamma exposure pass, and write the final pixel data
this.ApplyInverseGammaExposure(source.PixelBuffer, processing, this.SourceRectangle, this.Configuration);
@ -315,14 +299,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <param name="configuration">The configuration.</param>
/// <param name="processingBuffer">The buffer with the raw pixel data to use to aggregate the results of each convolution.</param>
/// <param name="firstPassBuffer">The complex buffer to use for the first 1D convolution pass for each kernel.</param>
/// <param name="secondPassBuffer">The complex buffer to use for the second 1D convolution pass for each kernel.</param>
private void OnFrameApplyCore(
ImageFrame<TPixel> source,
Rectangle sourceRectangle,
Configuration configuration,
Buffer2D<Vector4> processingBuffer,
Buffer2D<ComplexVector4> firstPassBuffer,
Buffer2D<ComplexVector4> secondPassBuffer)
Buffer2D<ComplexVector4> firstPassBuffer)
{
// Perform two 1D convolutions for each component in the current instance
ref Complex64[] baseRef = ref MemoryMarshal.GetReference(this.kernels.AsSpan());
@ -331,12 +313,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
// Compute the resulting complex buffer for the current component
var interest = Rectangle.Intersect(sourceRectangle, source.Bounds());
Complex64[] kernel = Unsafe.Add(ref baseRef, i);
this.ApplyConvolution(firstPassBuffer, source.PixelBuffer, interest, kernel, configuration);
this.ApplyConvolution(secondPassBuffer, firstPassBuffer, interest, kernel, configuration);
// Add the results of the convolution with the current kernel
Vector4 parameters = this.kernelParameters[i];
this.SumProcessingPartials(processingBuffer, secondPassBuffer, sourceRectangle, configuration, parameters.Z, parameters.W);
// Compute the two 1D convolutions and accumulate the partial results on the target buffer
this.ApplyConvolution(firstPassBuffer, source.PixelBuffer, interest, kernel, configuration);
this.ApplyConvolution(processingBuffer, firstPassBuffer, interest, kernel, configuration, parameters.Z, parameters.W);
}
}
@ -389,19 +370,21 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// Applies the process to the specified portion of the specified <see cref="Buffer2D{T}"/> buffer at the specified location
/// and with the specified size.
/// </summary>
/// <param name="targetValues">The target <see cref="ComplexVector4"/> values to use to store the results.</param>
/// <param name="targetValues">The target <see cref="Vector4"/> values to use to store the results.</param>
/// <param name="sourceValues">The source complex values. Cannot be null.</param>
/// <param name="sourceRectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
/// </param>
/// <param name="sourceRectangle">The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.</param>
/// <param name="kernel">The 1D kernel.</param>
/// <param name="configuration">The <see cref="Configuration"/></param>
/// <param name="z">The weight factor for the real component of the complex pixel values.</param>
/// <param name="w">The weight factor for the imaginary component of the complex pixel values.</param>
private void ApplyConvolution(
Buffer2D<ComplexVector4> targetValues,
Buffer2D<Vector4> targetValues,
Buffer2D<ComplexVector4> sourceValues,
Rectangle sourceRectangle,
Complex64[] kernel,
Configuration configuration)
Configuration configuration,
float z,
float w)
{
int startY = sourceRectangle.Y;
int endY = sourceRectangle.Bottom;
@ -426,11 +409,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
{
for (int y = rows.Min; y < rows.Max; y++)
{
Span<ComplexVector4> targetRowSpan = targetValues.GetRowSpan(y).Slice(startX);
Span<Vector4> targetRowSpan = targetValues.GetRowSpan(y).Slice(startX);
for (int x = 0; x < width; x++)
{
Buffer2DUtils.Convolve4(kernel, sourceValues, targetRowSpan, y, x, startY, maxY, startX, maxX);
Buffer2DUtils.Convolve4AndAccumulatePartials(kernel, sourceValues, targetRowSpan, y, x, startY, maxY, startX, maxX, z, w);
}
}
});
@ -536,53 +519,5 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
}
});
}
/// <summary>
/// Applies the process to the specified portion of the specified <see cref="ImageFrame{TPixel}"/> at the specified location
/// and with the specified size.
/// </summary>
/// <param name="targetValues">The target <see cref="Buffer2D{T}"/> instance to use to store the results.</param>
/// <param name="sourceValues">The source complex pixels. Cannot be null.</param>
/// <param name="sourceRectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
/// </param>
/// <param name="configuration">The <see cref="Configuration"/></param>
/// <param name="z">The weight factor for the real component of the complex pixel values.</param>
/// <param name="w">The weight factor for the imaginary component of the complex pixel values.</param>
private void SumProcessingPartials(
Buffer2D<Vector4> targetValues,
Buffer2D<ComplexVector4> sourceValues,
Rectangle sourceRectangle,
Configuration configuration,
float z,
float w)
{
int startY = sourceRectangle.Y;
int endY = sourceRectangle.Bottom;
int startX = sourceRectangle.X;
int endX = sourceRectangle.Right;
var workingRectangle = Rectangle.FromLTRB(startX, startY, endX, endY);
int width = workingRectangle.Width;
ParallelHelper.IterateRows(
workingRectangle,
configuration,
rows =>
{
for (int y = rows.Min; y < rows.Max; y++)
{
Span<Vector4> targetRowSpan = targetValues.GetRowSpan(y).Slice(startX);
Span<ComplexVector4> sourceRowSpan = sourceValues.GetRowSpan(y).Slice(startX);
ref Vector4 baseTargetRef = ref MemoryMarshal.GetReference(targetRowSpan);
ref ComplexVector4 baseSourceRef = ref MemoryMarshal.GetReference(sourceRowSpan);
for (int x = 0; x < width; x++)
{
Unsafe.Add(ref baseTargetRef, x) += Unsafe.Add(ref baseSourceRef, x).WeightedSum(z, w);
}
}
});
}
}
}

Loading…
Cancel
Save