// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.Formats.Heif.Hevc;
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Heif;
///
/// Measures frame-wide preparation of complete and partially substituted HEVC intra references.
///
[MemoryDiagnoser(displayGenColumns: false)]
public class HevcIntraReferencePreparationBenchmarks
{
///
/// The coded frame width.
///
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 base-two logarithm of the benchmark prediction-block side.
///
private const int BlockLog2 = 5;
///
/// The prediction-block side in samples.
///
private const int BlockSize = 1 << BlockLog2;
///
/// The availability-unit side in samples.
///
private const int UnitSize = 4;
///
/// The number of availability units surrounding one benchmark block.
///
private const int AvailabilityUnitCount = ((BlockSize * 2) / UnitSize * 2) + 1;
///
/// The complete reference availability pattern.
///
private readonly bool[] completeAvailability = new bool[AvailabilityUnitCount];
///
/// The partial availability pattern exercising forward substitution.
///
private readonly bool[] partialAvailability = new bool[AvailabilityUnitCount];
///
/// The reusable top reference destination.
///
private readonly ushort[] top = new ushort[(BlockSize * 2) + 1];
///
/// The reusable left reference destination.
///
private readonly ushort[] left = new ushort[(BlockSize * 2) + 1];
///
/// The reusable reference-line scratch storage.
///
private readonly ushort[] scratch = new ushort[HevcIntraPredictor.GetReferenceScratchLength(BlockLog2, UnitSize)];
///
/// The reconstructed picture providing benchmark reference samples.
///
private HevcPictureBuffer picture;
///
/// Allocates and initializes the reconstructed benchmark picture outside measured operations.
///
[GlobalSetup]
public void Setup()
{
this.picture = new HevcPictureBuffer(
Configuration.Default,
Width + (BlockSize * 2),
Height + (BlockSize * 2),
12,
12,
1,
false);
for (int y = 0; y < this.picture.Height; y++)
{
Span row = this.picture.GetRowSpan(HevcPlane.Y, y);
for (int x = 0; x < row.Length; x++)
{
row[x] = (ushort)(((37 * x) + (53 * y)) & 4095);
}
}
this.completeAvailability.AsSpan().Fill(true);
for (int i = 0; i < this.partialAvailability.Length; i++)
{
this.partialAvailability[i] = (i & 3) != 0;
}
}
///
/// Releases the reconstructed benchmark picture.
///
[GlobalCleanup]
public void Cleanup() => this.picture.Dispose();
///
/// Measures the complete-border fast path across a padded 1080p coded frame.
///
/// A prepared reference sample, keeping the result observable.
[Benchmark(Baseline = true)]
public ushort PrepareCompleteFrame() => this.PrepareFrame(this.completeAvailability);
///
/// Measures partial-unit collection and substitution across a padded 1080p coded frame.
///
/// A prepared reference sample, keeping the result observable.
[Benchmark]
public ushort PreparePartialFrame() => this.PrepareFrame(this.partialAvailability);
///
/// Prepares references for every maximum-size block in the benchmark frame.
///
/// The repeated availability pattern.
/// The final prepared reference sample.
private ushort PrepareFrame(ReadOnlySpan availability)
{
for (int y = 0; y < Height; y += BlockSize)
{
for (int x = 0; x < Width; x += BlockSize)
{
HevcIntraPredictor.PrepareReferenceSamples(
this.picture,
HevcPlane.Y,
x + BlockSize,
y + BlockSize,
BlockLog2,
UnitSize,
UnitSize,
availability,
this.top,
this.left,
this.scratch);
}
}
return this.left[^1];
}
}