// 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"));
}
}
}