blob: 5f5f41df71498ddc6b7d91f2c3cece5c2355e252 [file]
// Copyright 2025 the Vello Authors
// SPDX-License-Identifier: Apache-2.0 OR MIT
#![allow(rustdoc::private_intra_doc_links, reason = "not a public-facing crate")]
#![allow(rustdoc::broken_intra_doc_links, reason = "not a public-facing crate")]
//! Proc-macros for testing `vello_cpu` and `vello_hybrid`.
mod bench;
mod test;
// How much renderers are allowed to deviate from the reference images
// per color component. A value of 0 means that it must be an exact match, i.e.
// each pixel must have the exact same value as the reference pixel. A value of 1
// means that the absolute difference of each component of a pixel must not be higher
// than 1. For example, if the target pixel is (233, 43, 64, 100), then permissible
// values are (232, 43, 65, 101) or (233, 42, 64, 100), but not (231, 43, 64, 100).
// If we set this to 1 for CPU_U8, we would need to adjust it manually for nearly all
// bilinear image test cases.
const DEFAULT_CPU_U8_TOLERANCE: u8 = 2;
const DEFAULT_SIMD_TOLERANCE: u8 = 1;
const DEFAULT_CPU_F32_TOLERANCE: u8 = 0;
const DEFAULT_HYBRID_TOLERANCE: u8 = 1;
use crate::bench::vello_bench_inner;
use crate::test::vello_test_inner;
use proc_macro::TokenStream;
use syn::parse::{Parse, ParseStream};
use syn::punctuated::Punctuated;
use syn::{Expr, Ident, Token};
#[derive(Debug)]
enum Attribute {
/// A key-value-like argument.
KeyValue { key: Ident, expr: Expr },
/// A flag-like argument.
Flag(Ident),
}
impl Parse for Attribute {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let key = input.parse()?;
let is_flag = !input.peek(Token![=]);
if is_flag {
Ok(Self::Flag(key))
} else {
// Skip equality token.
let _: Token![=] = input.parse()?;
let expr = input.parse()?;
Ok(Self::KeyValue { key, expr })
}
}
}
/// Create a new Vello snapshot test.
/// See [`test::Arguments`] for documentation of the arguments, which are comma-separated.
/// Boolean flags are set on their own, and others are in the form of key-value pairs.
///
/// ## Example
/// ```ignore
/// use vello_dev_macros::vello_test;
/// use vello_api::kurbo::Rect;
/// use vello_api::color::palette::css::REBECCA_PURPLE;
///
/// #[vello_test(width = 10, height = 10)]
/// fn rectangle_above_viewport(ctx: &mut impl Renderer) {
/// let rect = Rect::new(2.0, -5.0, 8.0, 8.0);
///
/// ctx.set_paint(REBECCA_PURPLE.with_alpha(0.5));
/// ctx.fill_rect(&rect);
/// }
/// ```
#[proc_macro_attribute]
pub fn vello_test(attr: TokenStream, item: TokenStream) -> TokenStream {
vello_test_inner(attr, item)
}
/// Create a new Vello benchmark for fine rasterization.
/// This macro expects a function hat takes a `Bencher` and `Fine` as input, and will generate one benchmark
/// for each possible instantiation of `Fine`.
///
/// ## Example
/// ```ignore
/// #[vello_bench]
/// pub fn transparent_short<F: FineType>(b: &mut Bencher<'_>, fine: &mut Fine<F>) {
/// let paint = Paint::Solid(PremulColor::from_alpha_color(ROYAL_BLUE.with_alpha(0.3)));
/// let width = 32;
///
/// fill_single(&paint, &[], width, b, default_blend(), fine);
/// }
/// ```
#[proc_macro_attribute]
pub fn vello_bench(attr: TokenStream, item: TokenStream) -> TokenStream {
vello_bench_inner(attr, item)
}
#[derive(Debug)]
struct AttributeInput {
args: Punctuated<Attribute, Token![,]>,
}
impl Parse for AttributeInput {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
Ok(Self {
args: input.parse_terminated(Attribute::parse, Token![,])?,
})
}
}
fn parse_int_lit(expr: &Expr, name: &str) -> u16 {
if let Expr::Lit(syn::ExprLit {
lit: syn::Lit::Int(lit_int),
..
}) = expr
{
lit_int.base10_parse::<u16>().unwrap()
} else {
panic!("invalid expression supplied to `{name}`")
}
}
fn parse_string_lit(expr: &Expr, name: &str) -> String {
if let Expr::Lit(syn::ExprLit {
lit: syn::Lit::Str(lit_str),
..
}) = expr
{
lit_str.value()
} else {
panic!("invalid expression supplied to `{name}`")
}
}