Merge branch 'main' into precompute # Conflicts: # sparse_strips/vello_bench/src/cpu_fine.rs
diff --git a/sparse_strips/vello_api/src/paint.rs b/sparse_strips/vello_api/src/paint.rs index 13fb370..595d8f8 100644 --- a/sparse_strips/vello_api/src/paint.rs +++ b/sparse_strips/vello_api/src/paint.rs
@@ -36,10 +36,10 @@ /// 2) Indexed paints, which can represent any arbitrary, more complex paint that is /// determined by the frontend. The intended way of using this is to store a vector /// of paints and store its index inside `IndexedPaint`. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq)] pub enum Paint { /// A premultiplied RGBA8 color. - Solid(PremulRgba8), + Solid(PremulColor), /// A paint that needs to be resolved via an index. Indexed(IndexedPaint), } @@ -50,7 +50,7 @@ // Since we only do that conversion once per path it might not be critical, but should // still be measured. This also applies to all other usages of `to_rgba8` in the current // code. - Self::Solid(value.premultiply().to_rgba8()) + Self::Solid(PremulColor::new(value)) } } @@ -127,6 +127,40 @@ pub transform: Affine, } +/// A premultiplied color. +#[derive(Debug, Clone, PartialEq, Copy)] +pub struct PremulColor { + premul_u8: PremulRgba8, + premul_f32: peniko::color::PremulColor<Srgb>, +} + +impl PremulColor { + /// Create a new premultiplied color. + pub fn new(color: AlphaColor<Srgb>) -> Self { + let premul = color.premultiply(); + + Self { + premul_u8: premul.to_rgba8(), + premul_f32: premul, + } + } + + /// Return the color as a premultiplied RGBA8 color. + pub fn as_premul_rgba8(&self) -> PremulRgba8 { + self.premul_u8 + } + + /// Return the color as a premultiplied RGBAF32 color. + pub fn as_premul_f32(&self) -> peniko::color::PremulColor<Srgb> { + self.premul_f32 + } + + /// Return whether the color is opaque (i.e. has no transparency). + pub fn is_opaque(&self) -> bool { + self.premul_f32.components[3] == 1.0 + } +} + /// A kind of paint that can be used for filling and stroking shapes. #[derive(Debug, Clone)] pub enum PaintType {
diff --git a/sparse_strips/vello_bench/src/cpu_fine.rs b/sparse_strips/vello_bench/src/cpu_fine.rs index 62e6bb3..013a420 100644 --- a/sparse_strips/vello_bench/src/cpu_fine.rs +++ b/sparse_strips/vello_bench/src/cpu_fine.rs
@@ -9,9 +9,9 @@ use vello_common::coarse::WideTile; use vello_common::color::DynamicColor; use vello_common::color::palette::css::{BLUE, GREEN, RED, ROYAL_BLUE, YELLOW}; -use vello_common::encode::EncodeExt; -use vello_common::kurbo::Point; -use vello_common::paint::{Gradient, Paint}; +use vello_common::encode::{EncodeExt, EncodedPaint}; +use vello_common::kurbo::{Affine, Point}; +use vello_common::paint::{Gradient, Paint, PremulColor}; use vello_common::peniko; use vello_common::peniko::{ColorStop, ColorStops, GradientKind}; use vello_common::tile::Tile; @@ -21,12 +21,15 @@ let mut g = c.benchmark_group("fine/fill"); macro_rules! fill_single { - ($name:ident, $paint:expr, $paints:expr) => { + ($name:ident, $paint:expr, $paints:expr, $width:expr) => { g.bench_function(stringify!($name), |b| { let mut fine = Fine::new(WideTile::WIDTH, Tile::HEIGHT); + let paint = $paint; + let paints: &[EncodedPaint] = $paints; + b.iter(|| { - fine.fill(0, WideTile::WIDTH as usize, $paint, $paints); + fine.fill(0, $width, paint, paints); std::hint::black_box(&fine); }) @@ -36,13 +39,21 @@ fill_single!( solid_opaque, - &Paint::Solid(ROYAL_BLUE.premultiply().to_rgba8()), - &[] + &Paint::Solid(PremulColor::new(ROYAL_BLUE)), + &[], + WideTile::WIDTH as usize ); fill_single!( - sold_transparent, - &Paint::Solid(ROYAL_BLUE.with_alpha(0.2).premultiply().to_rgba8()), - &[] + solid_opaque_short, + &Paint::Solid(PremulColor::new(ROYAL_BLUE)), + &[], + 16 + ); + fill_single!( + solid_transparent, + &Paint::Solid(PremulColor::new(ROYAL_BLUE.with_alpha(0.2))), + &[], + WideTile::WIDTH as usize ); macro_rules! fill_single_linear { @@ -60,7 +71,7 @@ let paint = grad.encode_into(&mut paints); - fill_single!($name, &paint, &paints); + fill_single!($name, &paint, &paints, WideTile::WIDTH as usize); }; } @@ -101,7 +112,7 @@ let paint = grad.encode_into(&mut paints); - fill_single!($name, &paint, &paints); + fill_single!($name, &paint, &paints, WideTile::WIDTH as usize); }; } @@ -146,7 +157,7 @@ let paint = grad.encode_into(&mut paints); - fill_single!($name, &paint, &paints); + fill_single!($name, &paint, &paints, WideTile::WIDTH as usize); }; } @@ -183,12 +194,15 @@ } macro_rules! strip_single { - ($name:ident, $paint:expr, $paints:expr) => { + ($name:ident, $paint:expr, $paints:expr, $width:expr) => { g.bench_function(stringify!($name), |b| { let mut fine = Fine::new(WideTile::WIDTH, Tile::HEIGHT); + let paint = $paint; + let paints: &[EncodedPaint] = $paints; + b.iter(|| { - fine.strip(0, WideTile::WIDTH as usize, &alphas, $paint, $paints); + fine.strip(0, $width, &alphas, paint, paints); std::hint::black_box(&fine); }) @@ -198,8 +212,16 @@ strip_single!( basic, - &Paint::Solid(ROYAL_BLUE.premultiply().to_rgba8()), - &[] + &Paint::Solid(PremulColor::new(ROYAL_BLUE)), + &[], + WideTile::WIDTH as usize + ); + + strip_single!( + basic_short, + &Paint::Solid(PremulColor::new(ROYAL_BLUE)), + &[], + 8 ); // There is not really a need to measure performance of complex paint types
diff --git a/sparse_strips/vello_common/src/coarse.rs b/sparse_strips/vello_common/src/coarse.rs index 3245aff..53fc368 100644 --- a/sparse_strips/vello_common/src/coarse.rs +++ b/sparse_strips/vello_common/src/coarse.rs
@@ -3,14 +3,11 @@ //! Generating and processing wide tiles. -use crate::{ - color::{AlphaColor, Srgb}, - strip::Strip, - tile::Tile, -}; +use crate::color::palette::css::TRANSPARENT; +use crate::{strip::Strip, tile::Tile}; use alloc::vec; use alloc::{boxed::Box, vec::Vec}; -use vello_api::color::PremulRgba8; +use vello_api::paint::PremulColor; use vello_api::{paint::Paint, peniko::Fill}; /// A container for wide tiles. @@ -130,7 +127,7 @@ /// Reset all tiles in the container. pub fn reset(&mut self) { for tile in &mut self.tiles { - tile.bg = AlphaColor::<Srgb>::TRANSPARENT.premultiply().to_rgba8(); + tile.bg = PremulColor::new(TRANSPARENT); tile.cmds.clear(); } } @@ -616,7 +613,7 @@ /// The y coordinate of the wide tile. pub y: u16, /// The background of the tile. - pub bg: PremulRgba8, + pub bg: PremulColor, /// The draw commands of the tile. pub cmds: Vec<Cmd>, @@ -635,7 +632,7 @@ Self { x, y, - bg: AlphaColor::<Srgb>::TRANSPARENT.premultiply().to_rgba8(), + bg: PremulColor::new(TRANSPARENT), cmds: vec![], n_zero_clip: 0, @@ -656,7 +653,8 @@ // // However, the extra cost of tracking such optimizations may outweigh the // benefit, especially in hybrid mode with GPU painting. - let can_override = x == 0 && width == Self::WIDTH && s.a == 255 && self.n_clip == 0; + let can_override = + x == 0 && width == Self::WIDTH && s.is_opaque() && self.n_clip == 0; can_override.then_some(*s) } else { // TODO: Implement for indexed paints.
diff --git a/sparse_strips/vello_cpu/src/fine/mod.rs b/sparse_strips/vello_cpu/src/fine/mod.rs index e0717cb..7569f19 100644 --- a/sparse_strips/vello_cpu/src/fine/mod.rs +++ b/sparse_strips/vello_cpu/src/fine/mod.rs
@@ -145,18 +145,18 @@ match fill { Paint::Solid(color) => { - let color = &color.to_u8_array(); + let color = color.as_premul_rgba8().to_u8_array(); // If color is completely opaque we can just memcopy the colors. if color[3] == 255 { for t in blend_buf.chunks_exact_mut(COLOR_COMPONENTS) { - t.copy_from_slice(color); + t.copy_from_slice(&color); } return; } - fill::src_over(blend_buf, iter::repeat(*color)); + fill::src_over(blend_buf, iter::repeat(color)); } Paint::Indexed(paint) => { let encoded_paint = &encoded_paints[paint.index()]; @@ -223,7 +223,11 @@ match fill { Paint::Solid(color) => { - strip::src_over(blend_buf, iter::repeat(color.to_u8_array()), alphas); + strip::src_over( + blend_buf, + iter::repeat(color.as_premul_rgba8().to_u8_array()), + alphas, + ); } Paint::Indexed(paint) => { let encoded_paint = &paints[paint.index()];
diff --git a/sparse_strips/vello_cpu/src/render.rs b/sparse_strips/vello_cpu/src/render.rs index 8a4b9a1..2dd7b00 100644 --- a/sparse_strips/vello_cpu/src/render.rs +++ b/sparse_strips/vello_cpu/src/render.rs
@@ -198,7 +198,7 @@ let wtile = self.wide.get(x, y); fine.set_coords(x, y); - fine.clear(wtile.bg.to_u8_array()); + fine.clear(wtile.bg.as_premul_rgba8().to_u8_array()); for cmd in &wtile.cmds { fine.run_cmd(cmd, &self.alphas, &self.encoded_paints); }
diff --git a/sparse_strips/vello_hybrid/src/scene.rs b/sparse_strips/vello_hybrid/src/scene.rs index 952abf4..c7e382b 100644 --- a/sparse_strips/vello_hybrid/src/scene.rs +++ b/sparse_strips/vello_hybrid/src/scene.rs
@@ -7,7 +7,6 @@ use alloc::vec; use alloc::vec::Vec; use vello_common::coarse::{Wide, WideTile}; -use vello_common::color::PremulRgba8; use vello_common::flatten::Line; use vello_common::glyph::{GlyphRenderer, GlyphRunBuilder, PreparedGlyph}; use vello_common::kurbo::{Affine, BezPath, Cap, Join, Rect, Shape, Stroke}; @@ -212,7 +211,7 @@ let wide_tile = &self.wide.tiles[wide_tile_idx]; let wide_tile_x = wide_tile_col * WideTile::WIDTH; let wide_tile_y = wide_tile_row * Tile::HEIGHT; - let bg = wide_tile.bg.to_u32(); + let bg = wide_tile.bg.as_premul_rgba8().to_u32(); if bg != 0 { strips.push(GpuStrip { x: wide_tile_x, @@ -226,8 +225,8 @@ for cmd in &wide_tile.cmds { match cmd { vello_common::coarse::Cmd::Fill(fill) => { - let color: PremulRgba8 = match fill.paint { - Paint::Solid(color) => color, + let rgba = match &fill.paint { + Paint::Solid(color) => color.as_premul_rgba8().to_u32(), Paint::Indexed(_) => unimplemented!(), }; strips.push(GpuStrip { @@ -236,12 +235,12 @@ width: fill.width, dense_width: 0, col: 0, - rgba: color.to_u32(), + rgba, }); } vello_common::coarse::Cmd::AlphaFill(cmd_strip) => { - let color: PremulRgba8 = match cmd_strip.paint { - Paint::Solid(color) => color, + let rgba = match &cmd_strip.paint { + Paint::Solid(color) => color.as_premul_rgba8().to_u32(), Paint::Indexed(_) => unimplemented!(), }; @@ -255,7 +254,7 @@ col: (cmd_strip.alpha_idx / usize::from(Tile::HEIGHT)) .try_into() .expect(msg), - rgba: color.to_u32(), + rgba, }); } _ => {