Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 59 additions & 8 deletions sparse_strips/vello_bench/src/fine/strip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,44 +16,95 @@ use vello_cpu::fine::{Fine, FineKernel};
use vello_dev_macros::vello_bench;

pub fn strip(c: &mut Criterion) {
solid_single(c);
solid_short(c);
solid_medium(c);
solid_long(c);
solid_opaque_single(c);
solid_transparent_single(c);

solid_opaque_short(c);
solid_transparent_short(c);

solid_opaque_medium(c);
solid_transparent_medium(c);

solid_opaque_long(c);
solid_transparent_long(c);
}

#[vello_bench]
pub fn solid_single<S: Simd, N: FineKernel<S>>(b: &mut Bencher<'_>, fine: &mut Fine<S, N>) {
pub fn solid_opaque_single<S: Simd, N: FineKernel<S>>(b: &mut Bencher<'_>, fine: &mut Fine<S, N>) {
let paint = Paint::Solid(PremulColor::from_alpha_color(ROYAL_BLUE));
let width = Tile::WIDTH;

strip_single(&paint, &[], width as usize, b, fine);
}

#[vello_bench]
pub fn solid_short<S: Simd, N: FineKernel<S>>(b: &mut Bencher<'_>, fine: &mut Fine<S, N>) {
pub fn solid_opaque_short<S: Simd, N: FineKernel<S>>(b: &mut Bencher<'_>, fine: &mut Fine<S, N>) {
let paint = Paint::Solid(PremulColor::from_alpha_color(ROYAL_BLUE));
let width = 8;

strip_single(&paint, &[], width, b, fine);
}

#[vello_bench]
pub fn solid_medium<S: Simd, N: FineKernel<S>>(b: &mut Bencher<'_>, fine: &mut Fine<S, N>) {
pub fn solid_opaque_medium<S: Simd, N: FineKernel<S>>(b: &mut Bencher<'_>, fine: &mut Fine<S, N>) {
let paint = Paint::Solid(PremulColor::from_alpha_color(ROYAL_BLUE));
let width = 16;

strip_single(&paint, &[], width, b, fine);
}

#[vello_bench]
pub fn solid_long<S: Simd, N: FineKernel<S>>(b: &mut Bencher<'_>, fine: &mut Fine<S, N>) {
pub fn solid_opaque_long<S: Simd, N: FineKernel<S>>(b: &mut Bencher<'_>, fine: &mut Fine<S, N>) {
let paint = Paint::Solid(PremulColor::from_alpha_color(ROYAL_BLUE));
let width = 64;

strip_single(&paint, &[], width, b, fine);
}

#[vello_bench]
pub fn solid_transparent_single<S: Simd, N: FineKernel<S>>(
b: &mut Bencher<'_>,
fine: &mut Fine<S, N>,
) {
let paint = Paint::Solid(PremulColor::from_alpha_color(ROYAL_BLUE.with_alpha(0.3)));
let width = Tile::WIDTH;

strip_single(&paint, &[], width as usize, b, fine);
}

#[vello_bench]
pub fn solid_transparent_short<S: Simd, N: FineKernel<S>>(
b: &mut Bencher<'_>,
fine: &mut Fine<S, N>,
) {
let paint = Paint::Solid(PremulColor::from_alpha_color(ROYAL_BLUE.with_alpha(0.3)));
let width = 8;

strip_single(&paint, &[], width, b, fine);
}

#[vello_bench]
pub fn solid_transparent_medium<S: Simd, N: FineKernel<S>>(
b: &mut Bencher<'_>,
fine: &mut Fine<S, N>,
) {
let paint = Paint::Solid(PremulColor::from_alpha_color(ROYAL_BLUE.with_alpha(0.3)));
let width = 16;

strip_single(&paint, &[], width, b, fine);
}

#[vello_bench]
pub fn solid_transparent_long<S: Simd, N: FineKernel<S>>(
b: &mut Bencher<'_>,
fine: &mut Fine<S, N>,
) {
let paint = Paint::Solid(PremulColor::from_alpha_color(ROYAL_BLUE.with_alpha(0.3)));
let width = 64;

strip_single(&paint, &[], width, b, fine);
}

fn strip_single<S: Simd, N: FineKernel<S>>(
paint: &Paint,
encoded_paints: &[EncodedPaint],
Expand Down
41 changes: 41 additions & 0 deletions sparse_strips/vello_cpu/src/fine/highp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,21 @@ impl<S: Simd> FineKernel<S> for F32Kernel {
}
}

#[inline(always)]
fn alpha_composite_opaque_solid(
simd: S,
dest: &mut [Self::Numeric],
src: [Self::Numeric; 4],
alphas: &[u8],
) {
alpha_fill::alpha_composite_opaque_solid(
simd,
dest,
src,
bytemuck::cast_slice::<u8, [u8; 4]>(alphas).iter().copied(),
);
}

/// Composites a source buffer onto a destination buffer using alpha blending.
///
/// Dispatches to either the masked or unmasked implementation based on the
Expand Down Expand Up @@ -446,6 +461,32 @@ mod alpha_fill {
);
}

/// Composites an opaque solid color with alpha masks.
#[inline(always)]
pub(super) fn alpha_composite_opaque_solid<S: Simd>(
s: S,
dest: &mut [f32],
src: [f32; 4],
alphas: impl Iterator<Item = [u8; 4]>,
) {
s.vectorize(
#[inline(always)]
|| {
let src_c = f32x16::block_splat(src.simd_into(s));
let one = f32x16::splat(s, 1.0);

for (next_dest, next_mask) in dest.chunks_exact_mut(16).zip(alphas) {
let bg_c = f32x16::from_slice(s, next_dest);
let mask_a = extract_masks(s, &next_mask);
let inv_mask_a = one - mask_a;

let res = bg_c.mul_add(inv_mask_a, src_c * mask_a);
res.store_slice(next_dest);
}
},
);
}

/// Composites a buffer of colors with per-pixel alpha masks.
///
/// Each pixel's source alpha is modulated by its corresponding mask value.
Expand Down
44 changes: 44 additions & 0 deletions sparse_strips/vello_cpu/src/fine/lowp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,21 @@ impl<S: Simd> FineKernel<S> for U8Kernel {
}
}

#[inline(always)]
fn alpha_composite_opaque_solid(
simd: S,
dest: &mut [Self::Numeric],
src: [Self::Numeric; 4],
alphas: &[u8],
) {
alpha_fill::alpha_composite_opaque_solid(
simd,
dest,
src,
cast_slice::<u8, [u8; 8]>(alphas).iter().copied(),
);
}

/// Composites a source buffer onto a destination buffer using alpha blending.
///
/// Dispatches to either the masked or unmasked implementation based on the
Expand Down Expand Up @@ -509,6 +524,35 @@ mod alpha_fill {
);
}

/// Composites an opaque solid color with alpha masks.
#[inline(always)]
pub(super) fn alpha_composite_opaque_solid<S: Simd>(
s: S,
dest: &mut [u8],
src: [u8; 4],
alphas: impl Iterator<Item = [u8; 8]>,
) {
s.vectorize(
#[inline(always)]
|| {
let src_c = u32x8::splat(s, u32::from_ne_bytes(src)).to_bytes();
let one = u8x32::splat(s, 255);

for (next_bg, next_mask) in dest.chunks_exact_mut(32).zip(alphas) {
let bg_v = u8x32::from_slice(s, next_bg);
let mask_v = extract_masks(s, &next_mask);
let inv_mask = one - mask_v;

let p1 = s.widen_u8x32(bg_v) * s.widen_u8x32(inv_mask);
let p2 = s.widen_u8x32(src_c) * s.widen_u8x32(mask_v);
let res = s.narrow_u16x32((p1 + p2).div_255());

res.store_slice(next_bg);
}
},
);
}

/// Composites a buffer of colors with per-pixel alpha masks.
///
/// Each pixel's source alpha is modulated by its corresponding mask value.
Expand Down
27 changes: 19 additions & 8 deletions sparse_strips/vello_cpu/src/fine/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,17 @@ pub trait FineKernel<S: Simd>: Send + Sync + 'static {
alphas: Option<&[u8]>,
);

/// Perform alpha compositing with an opaque solid color and per-pixel alpha values.
///
/// Blends an opaque RGBA color over the destination while modulating the source
/// contribution by the provided per-pixel alpha values.
fn alpha_composite_opaque_solid(
simd: S,
target: &mut [Self::Numeric],
src: [Self::Numeric; 4],
alphas: &[u8],
);

/// Perform alpha compositing with a source buffer over the destination buffer.
///
/// Blends the source buffer contents over the destination using standard alpha compositing.
Expand Down Expand Up @@ -687,14 +698,14 @@ impl<S: Simd, T: FineKernel<S>> Fine<S, T> {
Paint::Solid(color) => {
let color = T::extract_color(*color);

// If color is completely opaque, we can just directly override
// the blend buffer.
if color[3] == T::Numeric::ONE
&& default_blend
&& alphas.is_none()
&& mask.is_none()
{
T::copy_solid(self.simd, blend_buf, color);
// If color is completely opaque and we have the default blend
// mode, we can use the solid-color fast path.
if color[3] == T::Numeric::ONE && default_blend && mask.is_none() {
if let Some(alphas) = alphas {
T::alpha_composite_opaque_solid(self.simd, blend_buf, color, alphas);
} else {
T::copy_solid(self.simd, blend_buf, color);
}

return;
}
Expand Down
Loading