kahlo 0.0.2

Optimized software rendering library.
Documentation
use criterion::{measurement::WallTime, BenchmarkGroup, BenchmarkId};
use rand::Fill;

use crate::bitmap::{BitmapAccess, BitmapAccessMut, BitmapRawData, BitmapRawDataMut};
use crate::colour::BlendMode;
use crate::formats::{self, PixelFormatSpec};
use crate::math::{PixelBox, PixelPoint};
use crate::op::generate::{Benchmark, ContextValueTag, DstSize, SrcSize};
use crate::{math, Bitmap};

use super::composite::CompositeOp;
use super::copy_from::CopyFromOp;
use super::fill::FillOp;
use super::fill_region::FillRegionOp;
use super::fill_region_masked::FillRegionMaskedOp;
use super::generate::{self, BinaryInputSpec, UnaryInputSpec, ValueGeneratorList};
use super::{BinaryOpSpec, GenericBinary, GenericUnary, UnaryOpSpec};

struct Signifier;
impl ContextValueTag for Signifier {
    type Value = String;
}

type CriGroup<'a> = BenchmarkGroup<'a, WallTime>;

fn bench_unary_format<UIS: UnaryInputSpec, Format: PixelFormatSpec>(
    group: &mut CriGroup,
    ctx: &mut generate::Context,
    n: usize,
) {
    let gen = UIS::gen();

    let op = UIS::build();
    if op.is_generic::<Format>() {
        // println!("Skipping generic implementation {}", Format::NAME);
        return;
    } else {
        println!("Testing specific implementation {}", Format::NAME);
    }

    let mut rng = rand::rngs::ThreadRng::default();

    let mut original_img = Bitmap::<Format>::new(n, n);
    u8::fill_slice(original_img.data_mut(), &mut rng);

    let mut out_img = original_img.clone();

    ctx.insert::<DstSize>(original_img.size());

    let signifier = ctx
        .lookup::<Signifier>()
        .map(ToOwned::to_owned)
        .unwrap_or(String::new());

    let params = gen.generate(ctx).expect("could not generate parameters");

    group.bench_function(
        BenchmarkId::new("generic", format!("{}_{signifier}_{n}", Format::NAME)),
        |bencher| {
            bencher.iter(|| {
                <UIS as GenericUnary<UIS::Params<'static>>>::perform::<Format>(
                    &mut out_img,
                    &params,
                );
            });
        },
    );

    group.bench_function(
        BenchmarkId::new("specific", format!("{}_{signifier}_{n}", Format::NAME)),
        |bencher| {
            bencher.iter(|| {
                op.select::<Format>()(&mut out_img, &params);
            });
        },
    );
}

fn bench_unary<UIS: UnaryInputSpec>(group: &mut CriGroup, ctx: &mut generate::Context, n: usize) {
    bench_unary_format::<UIS, formats::A8>(group, ctx, n);
    bench_unary_format::<UIS, formats::Abgr32>(group, ctx, n);
    bench_unary_format::<UIS, formats::Rgba32>(group, ctx, n);
    // test_unary_format::<UIS, formats::Rgba32P>(n);
}

fn bench_binary_format<
    BIS: BinaryInputSpec,
    DstFormat: PixelFormatSpec,
    SrcFormat: PixelFormatSpec,
>(
    group: &mut CriGroup,
    ctx: &mut generate::Context,
    n: usize,
) {
    let gen = BIS::gen();

    let op = BIS::build();
    if op.is_generic::<DstFormat, SrcFormat>() {
        println!(
            "Skipping generic implementation ({},{})",
            DstFormat::NAME,
            SrcFormat::NAME
        );
        return;
    } else {
        println!(
            "Benchmarking specific implementation ({},{})",
            DstFormat::NAME,
            SrcFormat::NAME
        );
    }

    let mut rng = rand::rngs::ThreadRng::default();

    let mut original_dst_img = Bitmap::<DstFormat>::new(n, n);
    u8::fill_slice(original_dst_img.data_mut(), &mut rng);

    let mut src_img = Bitmap::<SrcFormat>::new(n, n);
    u8::fill_slice(src_img.data_mut(), &mut rng);

    let mut out_img = original_dst_img.clone();

    ctx.insert::<DstSize>(src_img.size());
    ctx.insert::<SrcSize>(src_img.size());

    let signifier = ctx
        .lookup::<Signifier>()
        .map(ToOwned::to_owned)
        .unwrap_or(String::new());

    let params = gen.generate(ctx).expect("could not generate parameters");

    group.bench_function(
        BenchmarkId::new(
            "generic",
            format!("{}_{}_{signifier}_{n}", DstFormat::NAME, SrcFormat::NAME),
        ),
        |bencher| {
            bencher.iter(|| {
                <BIS as GenericBinary<BIS::Params<'static>>>::perform::<DstFormat, SrcFormat>(
                    &mut out_img,
                    &src_img,
                    &params,
                );
            });
        },
    );

    group.bench_function(
        BenchmarkId::new(
            "specific",
            format!("{}_{}_{signifier}_{n}", DstFormat::NAME, SrcFormat::NAME),
        ),
        |bencher| {
            bencher.iter(|| {
                op.select::<DstFormat, SrcFormat>()(&mut out_img, &src_img, &params);
            });
        },
    );
}

fn bench_binary<BIS: BinaryInputSpec>(
    group: &mut criterion::BenchmarkGroup<'_, criterion::measurement::WallTime>,
    ctx: &mut generate::Context,
    n: usize,
) {
    bench_binary_format::<BIS, formats::A8, formats::A8>(group, ctx, n);
    bench_binary_format::<BIS, formats::A8, formats::Abgr32>(group, ctx, n);
    bench_binary_format::<BIS, formats::A8, formats::Rgba32>(group, ctx, n);
    // bench_binary_format::<BIS, formats::A8, formats::Rgba32P>(group, ctx, n);

    bench_binary_format::<BIS, formats::Abgr32, formats::A8>(group, ctx, n);
    bench_binary_format::<BIS, formats::Abgr32, formats::Abgr32>(group, ctx, n);
    bench_binary_format::<BIS, formats::Abgr32, formats::Rgba32>(group, ctx, n);
    // bench_binary_format::<BIS, formats::Abgr32, formats::Rgba32P>(group, ctx, n);

    bench_binary_format::<BIS, formats::Rgba32, formats::A8>(group, ctx, n);
    bench_binary_format::<BIS, formats::Rgba32, formats::Abgr32>(group, ctx, n);
    bench_binary_format::<BIS, formats::Rgba32, formats::Rgba32>(group, ctx, n);
    // bench_binary_format::<BIS, formats::Rgba32, formats::Rgba32P>(group, ctx, n);

    /*bench_binary_format::<BIS, formats::Rgba32P, formats::A8>(group, ctx, n);
    bench_binary_format::<BIS, formats::Rgba32P, formats::Abgr32>(group, ctx, n);
    bench_binary_format::<BIS, formats::Rgba32P, formats::Rgba32>(group, ctx, n);
    bench_binary_format::<BIS, formats::Rgba32P, formats::Rgba32P>(group, ctx, n);*/
}

pub fn op_benchmarks(cri: &mut criterion::Criterion) {
    for n in [16, 64, 1024].into_iter() {
        {
            let mut composite = cri.benchmark_group("composite");
            for blend in [
                BlendMode::Simple,
                BlendMode::SourceAlpha,
                BlendMode::Multiply,
            ] {
                let mut ctx = generate::Context::new(n);
                ctx.insert::<BlendMode>(blend);
                ctx.insert::<Benchmark>(true);
                ctx.insert::<Signifier>(format!("{blend:?}"));
                bench_binary::<CompositeOp>(&mut composite, &mut ctx, n);
            }
        }
        {
            let mut fill_region_masked = cri.benchmark_group("fill_region_masked");
            {
                for blend in [
                    BlendMode::Simple,
                    BlendMode::SourceAlpha,
                    BlendMode::Multiply,
                ] {
                    let mut ctx = generate::Context::new(n);
                    ctx.insert::<BlendMode>(blend);
                    ctx.insert::<Benchmark>(true);
                    ctx.insert::<Signifier>(format!("{blend:?}"));
                    bench_unary::<FillRegionMaskedOp>(&mut fill_region_masked, &mut ctx, n);
                }
            }
        }
    }
}

/*
#[test]
fn fill_op() {
    bench_unary::<FillOp>(100);
}

#[test]
fn fill_region_op() {
    bench_unary::<FillRegionOp>(100);
}

#[test]
fn fill_region_masked_op() {
    bench_unary::<FillRegionMaskedOp>(40);
}

#[test]
fn copy_from_op() {
    bench_binary::<CopyFromOp>(20);
}

#[test]
fn composite_op() {
    bench_binary::<CompositeOp>(40);
}
*/