diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs index 6d1d7c23c2..38b6cab3f8 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs @@ -123,15 +123,10 @@ internal class Convolution2PassProcessor : ImageProcessor this.Configuration, this.PreserveAlpha); - using (IMemoryOwner hBuffer = allocator.Allocate(horizontalOperation.GetRequiredBufferLength(interest))) - { - Span hSpan = hBuffer.Memory.Span; - - for (int y = interest.Top; y < interest.Bottom; y++) - { - horizontalOperation.Invoke(y, hSpan); - } - } + ParallelRowIterator.IterateRows( + this.Configuration, + interest, + in horizontalOperation); // Vertical convolution VerticalConvolutionRowOperation verticalOperation = new( @@ -143,15 +138,10 @@ internal class Convolution2PassProcessor : ImageProcessor this.Configuration, this.PreserveAlpha); - using (IMemoryOwner vBuffer = allocator.Allocate(verticalOperation.GetRequiredBufferLength(interest))) - { - Span vSpan = vBuffer.Memory.Span; - - for (int y = interest.Top; y < interest.Bottom; y++) - { - verticalOperation.Invoke(y, vSpan); - } - } + ParallelRowIterator.IterateRows( + this.Configuration, + interest, + in verticalOperation); } /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs index b704f556fa..69d72b3cc0 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs @@ -97,17 +97,10 @@ internal class ConvolutionProcessor : ImageProcessor map.BuildSamplingOffsetMap(this.KernelXY.Rows, this.KernelXY.Columns, interest, this.BorderWrapModeX, this.BorderWrapModeY); RowOperation operation = new(interest, targetPixels, source.PixelBuffer, map, this.KernelXY, this.Configuration, this.PreserveAlpha); - - // Convolution is memory-bandwidth-bound with low arithmetic intensity. - // Parallelization degrades performance due to cache line contention from - // overlapping source row reads. See #3111. - using IMemoryOwner buffer = allocator.Allocate(operation.GetRequiredBufferLength(interest)); - Span span = buffer.Memory.Span; - - for (int y = interest.Top; y < interest.Bottom; y++) - { - operation.Invoke(y, span); - } + ParallelRowIterator.IterateRows( + this.Configuration, + interest, + in operation); } Buffer2D.SwapOrCopyContent(source.PixelBuffer, targetPixels); diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs index f1edb75a20..eae7481661 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs @@ -83,15 +83,11 @@ internal class EdgeDetectorCompassProcessor : ImageProcessor processor.Apply(pass); } - // Convolution is memory-bandwidth-bound with low arithmetic intensity. - // Parallelization degrades performance due to cache line contention from - // overlapping source row reads. See #3111. RowOperation operation = new(source.PixelBuffer, pass.PixelBuffer, interest); - - for (int y = interest.Top; y < interest.Bottom; y++) - { - operation.Invoke(y); - } + ParallelRowIterator.IterateRows( + this.Configuration, + interest, + in operation); } }