From 0220108d86f7c0986257abb91029f182541bcf55 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 10 Apr 2026 13:48:58 +1000 Subject: [PATCH] Use ParallelRowIterator for BokehBlur --- .../Convolution/BokehBlurProcessor{TPixel}.cs | 70 +++++++------------ 1 file changed, 24 insertions(+), 46 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs index 426544d692..663d54afc9 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.Buffers; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -79,36 +78,22 @@ internal class BokehBlurProcessor : ImageProcessor { Rectangle sourceRectangle = Rectangle.Intersect(this.SourceRectangle, source.Bounds); - MemoryAllocator allocator = this.Configuration.MemoryAllocator; - - // Convolution is memory-bandwidth-bound with low arithmetic intensity. - // Parallelization degrades performance due to cache line contention from - // overlapping source row reads. See #3111. - // Preliminary gamma highlight pass if (this.gamma == 3F) { ApplyGamma3ExposureRowOperation gammaOperation = new(sourceRectangle, source.PixelBuffer, this.Configuration); - - using IMemoryOwner gammaBuffer = allocator.Allocate(gammaOperation.GetRequiredBufferLength(sourceRectangle)); - Span gammaSpan = gammaBuffer.Memory.Span; - - for (int y = sourceRectangle.Top; y < sourceRectangle.Bottom; y++) - { - gammaOperation.Invoke(y, gammaSpan); - } + ParallelRowIterator.IterateRows( + this.Configuration, + sourceRectangle, + in gammaOperation); } else { ApplyGammaExposureRowOperation gammaOperation = new(sourceRectangle, source.PixelBuffer, this.Configuration, this.gamma); - - using IMemoryOwner gammaBuffer = allocator.Allocate(gammaOperation.GetRequiredBufferLength(sourceRectangle)); - Span gammaSpan = gammaBuffer.Memory.Span; - - for (int y = sourceRectangle.Top; y < sourceRectangle.Bottom; y++) - { - gammaOperation.Invoke(y, gammaSpan); - } + ParallelRowIterator.IterateRows( + this.Configuration, + sourceRectangle, + in gammaOperation); } // Create a 0-filled buffer to use to store the result of the component convolutions @@ -121,20 +106,18 @@ internal class BokehBlurProcessor : ImageProcessor if (this.gamma == 3F) { ApplyInverseGamma3ExposureRowOperation operation = new(sourceRectangle, source.PixelBuffer, processingBuffer, this.Configuration); - - for (int y = sourceRectangle.Top; y < sourceRectangle.Bottom; y++) - { - operation.Invoke(y); - } + ParallelRowIterator.IterateRows( + this.Configuration, + sourceRectangle, + in operation); } else { ApplyInverseGammaExposureRowOperation operation = new(sourceRectangle, source.PixelBuffer, processingBuffer, this.Configuration, this.gamma); - - for (int y = sourceRectangle.Top; y < sourceRectangle.Bottom; y++) - { - operation.Invoke(y); - } + ParallelRowIterator.IterateRows( + this.Configuration, + sourceRectangle, + in operation); } } @@ -187,15 +170,10 @@ internal class BokehBlurProcessor : ImageProcessor kernel, configuration); - using (IMemoryOwner hBuffer = configuration.MemoryAllocator.Allocate(horizontalOperation.GetRequiredBufferLength(sourceRectangle))) - { - Span hSpan = hBuffer.Memory.Span; - - for (int y = sourceRectangle.Top; y < sourceRectangle.Bottom; y++) - { - horizontalOperation.Invoke(y, hSpan); - } - } + ParallelRowIterator.IterateRows( + configuration, + sourceRectangle, + in horizontalOperation); // Vertical 1D convolutions to accumulate the partial results on the target buffer BokehBlurProcessor.SecondPassConvolutionRowOperation verticalOperation = new( @@ -207,10 +185,10 @@ internal class BokehBlurProcessor : ImageProcessor parameters.Z, parameters.W); - for (int y = sourceRectangle.Top; y < sourceRectangle.Bottom; y++) - { - verticalOperation.Invoke(y); - } + ParallelRowIterator.IterateRows( + configuration, + sourceRectangle, + in verticalOperation); } }