From 3dfb3171cfc628a953ced05d9efaee46130845fe Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Wed, 27 Nov 2019 13:04:05 +0100 Subject: [PATCH] Memory usage reduction, sequential mode disabled --- .../Common/Helpers/Buffer2DUtils.cs | 15 ++- .../Convolution/BokehBlurProcessor{TPixel}.cs | 101 ++++-------------- 2 files changed, 28 insertions(+), 88 deletions(-) diff --git a/src/ImageSharp/Common/Helpers/Buffer2DUtils.cs b/src/ImageSharp/Common/Helpers/Buffer2DUtils.cs index 0c22aa68f..b678e798f 100644 --- a/src/ImageSharp/Common/Helpers/Buffer2DUtils.cs +++ b/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 } /// - /// Computes the sum of vectors in weighted by the kernel weight values. + /// Computes the sum of vectors in weighted by the kernel weight values and accumulates the partial results. /// /// The 1D convolution kernel. /// The source frame. @@ -73,16 +74,20 @@ namespace SixLabors.ImageSharp /// The maximum working area row. /// The minimum working area column. /// The maximum working area column. - public static void Convolve4( + /// The weight factor for the real component of the complex pixel values. + /// The weight factor for the imaginary component of the complex pixel values. + public static void Convolve4AndAccumulatePartials( Span kernel, Buffer2D sourceValues, - Span targetRow, + Span 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); } } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs index f8fb3f796..9c784099c 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs +++ b/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 processing = this.Configuration.MemoryAllocator.Allocate2D(source.Size(), AllocationOptions.Clean)) + using (Buffer2D firstPassValues = this.Configuration.MemoryAllocator.Allocate2D(source.Size())) { - if (this.executionMode == BokehBlurExecutionMode.PreferLowMemoryUsage) - { - // Memory usage priority: allocate a shared buffer and execute the second convolution in sequential mode - using (Buffer2D buffer = this.Configuration.MemoryAllocator.Allocate2D(source.Width, source.Height + this.radius)) - using (Buffer2D firstPassBuffer = buffer.Slice(this.radius, source.Height)) - using (Buffer2D 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 firstPassValues = this.Configuration.MemoryAllocator.Allocate2D(source.Size())) - using (Buffer2D secondPassBuffer = this.Configuration.MemoryAllocator.Allocate2D(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 /// The configuration. /// The buffer with the raw pixel data to use to aggregate the results of each convolution. /// The complex buffer to use for the first 1D convolution pass for each kernel. - /// The complex buffer to use for the second 1D convolution pass for each kernel. private void OnFrameApplyCore( ImageFrame source, Rectangle sourceRectangle, Configuration configuration, Buffer2D processingBuffer, - Buffer2D firstPassBuffer, - Buffer2D secondPassBuffer) + Buffer2D 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 buffer at the specified location /// and with the specified size. /// - /// The target values to use to store the results. + /// The target values to use to store the results. /// The source complex values. Cannot be null. - /// - /// The structure that specifies the portion of the image object to draw. - /// + /// The structure that specifies the portion of the image object to draw. /// The 1D kernel. /// The + /// The weight factor for the real component of the complex pixel values. + /// The weight factor for the imaginary component of the complex pixel values. private void ApplyConvolution( - Buffer2D targetValues, + Buffer2D targetValues, Buffer2D 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 targetRowSpan = targetValues.GetRowSpan(y).Slice(startX); + Span 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 } }); } - - /// - /// Applies the process to the specified portion of the specified at the specified location - /// and with the specified size. - /// - /// The target instance to use to store the results. - /// The source complex pixels. Cannot be null. - /// - /// The structure that specifies the portion of the image object to draw. - /// - /// The - /// The weight factor for the real component of the complex pixel values. - /// The weight factor for the imaginary component of the complex pixel values. - private void SumProcessingPartials( - Buffer2D targetValues, - Buffer2D 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 targetRowSpan = targetValues.GetRowSpan(y).Slice(startX); - Span 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); - } - } - }); - } } }