// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Columns; using BenchmarkDotNet.Configs; using BenchmarkDotNet.Jobs; using SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline.LoopRestoration; namespace SixLabors.ImageSharp.Benchmarks.Codecs.Heif; /// /// Measures normative AV1 self-guided restoration across a full-HD-equivalent luma workload. /// [Config(typeof(Configuration))] [MemoryDiagnoser(displayGenColumns: false)] [GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)] [CategoriesColumn] public class Av1LoopRestorationBenchmarks { /// /// The width of one normative self-guided processing unit. /// private const int Width = 64; /// /// The height of one normative self-guided processing unit. /// private const int Height = 64; /// /// The three source samples required on each side of a processing unit. /// private const int Border = 3; /// /// The number of processing units covering a 1920 by 1080 luma plane. /// private const int ProcessingUnitCount = 30 * 17; /// /// The bordered source-row stride. /// private const int SourceStride = Width + (Border * 2); /// /// The self-guided parameter set activating both radius-two and radius-one filtering. /// private const int ParameterSetIndex = 0; /// /// The deterministic bordered eight-bit source block. /// private readonly ushort[] source8 = new ushort[SourceStride * (Height + (Border * 2))]; /// /// The deterministic bordered twelve-bit source block. /// private readonly ushort[] source12 = new ushort[SourceStride * (Height + (Border * 2))]; /// /// The restored processing-unit destination. /// private readonly ushort[] destination = new ushort[Width * Height]; /// /// The caller-owned self-guided work storage. /// private readonly int[] scratch = new int[Av1SelfGuidedFilter.GetScratchLength(Width, Height)]; /// /// Gets the two transmitted projection coefficients used by the measured parameter set. /// private static ReadOnlySpan ProjectionCoefficients => [31, -7]; /// /// Populates deterministic bordered source blocks outside the measured traversal. /// [GlobalSetup] public void Setup() { for (int row = 0; row < Height + (Border * 2); row++) { for (int column = 0; column < SourceStride; column++) { int sample = ((row * 4051) + (column * 7919) + 127) & byte.MaxValue; int offset = (row * SourceStride) + column; this.source8[offset] = (ushort)sample; this.source12[offset] = (ushort)(sample << 4); } } } /// /// Measures eight-bit self-guided restoration for a full-HD-equivalent luma plane. /// /// The final restored sample, keeping the output observable. [Benchmark] [BenchmarkCategory("8Bit")] public ushort Restore8BitPlane() { for (int unit = 0; unit < ProcessingUnitCount; unit++) { Av1SelfGuidedFilter.FilterBlock( this.source8, SourceStride, this.destination, Width, Width, Height, 8, ParameterSetIndex, ProjectionCoefficients, this.scratch); } return this.destination[^1]; } /// /// Measures twelve-bit self-guided restoration for a full-HD-equivalent luma plane. /// /// The final restored sample, keeping the output observable. [Benchmark] [BenchmarkCategory("12Bit")] public ushort Restore12BitPlane() { for (int unit = 0; unit < ProcessingUnitCount; unit++) { Av1SelfGuidedFilter.FilterBlock( this.source12, SourceStride, this.destination, Width, Width, Height, 12, ParameterSetIndex, ProjectionCoefficients, this.scratch); } return this.destination[^1]; } /// /// Configures production-process measurements for hardware, 128-bit, and scalar filtering. /// public sealed class Configuration : ManualConfig { /// /// Initializes a new instance of the class. /// public Configuration() { this.AddJob(Job.ShortRun.WithId("Hardware").AsBaseline()); this.AddJob( Job.ShortRun .WithId("Vector128") .WithEnvironmentVariable("DOTNET_EnableAVX", "0")); this.AddJob( Job.ShortRun .WithId("Scalar") .WithEnvironmentVariable("DOTNET_EnableHWIntrinsic", "0")); } } }