// 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.Prediction;
using SixLabors.ImageSharp.Formats.Heif.Av1.Prediction.ChromaFromLuma;
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Heif;
///
/// Measures frame-wide AV1 chroma-from-luma prediction at each available intrinsic tier.
///
[Config(typeof(Configuration))]
[MemoryDiagnoser(displayGenColumns: false)]
[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)]
[CategoriesColumn]
public class Av1ChromaFromLumaBenchmarks
{
///
/// The coded frame width, which is an exact multiple of the maximum CfL block side.
///
private const int Width = 1920;
///
/// The coded frame height including the final padded coding-tree row for a 1080-line presentation.
///
private const int Height = 1088;
///
/// The maximum CfL block side in chroma samples.
///
private const int BlockSize = 32;
///
/// The fixed-stride Q3 luma residual surface reused by each benchmark block.
///
private readonly short[] lumaQ3 = new short[BlockSize * BlockSize];
///
/// The frame-wide 8-bit DC prediction surface.
///
private readonly byte[] destination8 = new byte[Width * Height];
///
/// The frame-wide 12-bit DC prediction surface.
///
private readonly short[] destination12 = new short[Width * Height];
///
/// Populates deterministic Q3 residuals and DC predictions outside the measured traversal.
///
[GlobalSetup]
public void Setup()
{
for (int index = 0; index < this.lumaQ3.Length; index++)
{
this.lumaQ3[index] = (short)(((index * 4051) % 65521) - 32760);
}
this.destination8.AsSpan().Fill(137);
this.destination12.AsSpan().Fill(2101);
}
///
/// Measures frame-wide 8-bit chroma-from-luma prediction.
///
/// The final reconstructed sample, keeping the frame output observable.
[Benchmark]
[BenchmarkCategory("8Bit")]
public byte Predict8BitFrame()
{
for (int row = 0; row < Height; row += BlockSize)
{
for (int column = 0; column < Width; column += BlockSize)
{
Av1ChromaFromLumaPredictor.Predict(this.lumaQ3, this.destination8.AsSpan((row * Width) + column), Width, 11, BlockSize, BlockSize);
}
}
return this.destination8[^1];
}
///
/// Measures frame-wide 12-bit chroma-from-luma prediction.
///
/// The final reconstructed sample, keeping the frame output observable.
[Benchmark]
[BenchmarkCategory("12Bit")]
public short Predict12BitFrame()
{
for (int row = 0; row < Height; row += BlockSize)
{
for (int column = 0; column < Width; column += BlockSize)
{
Av1ChromaFromLumaPredictor.Predict(this.lumaQ3, this.destination12.AsSpan((row * Width) + column), Width, 11, 12, BlockSize, BlockSize);
}
}
return this.destination12[^1];
}
///
/// Configures production-process measurements for the default, AVX2, Vector128, and scalar paths.
///
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("Avx2")
.WithEnvironmentVariable("DOTNET_EnableAVX512F", "0"));
this.AddJob(
Job.ShortRun
.WithId("Vector128")
.WithEnvironmentVariable("DOTNET_EnableAVX512F", "0")
.WithEnvironmentVariable("DOTNET_EnableAVX2", "0"));
this.AddJob(
Job.ShortRun
.WithId("Scalar")
.WithEnvironmentVariable("DOTNET_EnableHWIntrinsic", "0"));
}
}
}