dont support aliasing threshold for now
diff --git a/sparse_strips/vello_common/src/strip.rs b/sparse_strips/vello_common/src/strip.rs index 5849f4f..e146e73 100644 --- a/sparse_strips/vello_common/src/strip.rs +++ b/sparse_strips/vello_common/src/strip.rs
@@ -91,6 +91,7 @@ } /// Render the tiles stored in `tiles` into the strip and alpha buffer. +#[inline(never)] pub fn render( level: Level, tiles: &Tiles,
diff --git a/sparse_strips/vello_common/src/strip_generator.rs b/sparse_strips/vello_common/src/strip_generator.rs index b4d9a70..0b35a3c 100644 --- a/sparse_strips/vello_common/src/strip_generator.rs +++ b/sparse_strips/vello_common/src/strip_generator.rs
@@ -208,6 +208,55 @@ ); } + /// Flatten a filled path and generate sorted tiles, without computing strips or alpha. + /// + /// After this call, [`tiles()`](Self::tiles) and [`lines()`](Self::lines) contain the + /// tile and line data for the path. + pub fn prepare_tiles_for_fill( + &mut self, + path: impl IntoIterator<Item = PathEl>, + transform: Affine, + ) { + flatten::fill( + self.level, + path, + transform, + &mut self.line_buf, + &mut self.flatten_ctx, + self.width, + self.height, + ); + self.tiles + .make_tiles_analytic_aa(&self.line_buf, self.width, self.height); + self.tiles.sort_tiles(); + } + + /// Flatten a stroked path and generate sorted tiles, without computing strips or alpha. + /// + /// After this call, [`tiles()`](Self::tiles) and [`lines()`](Self::lines) contain the + /// tile and line data for the path. + pub fn prepare_tiles_for_stroke( + &mut self, + path: impl IntoIterator<Item = PathEl>, + stroke: &Stroke, + transform: Affine, + ) { + flatten::stroke( + self.level, + path, + stroke, + transform, + &mut self.line_buf, + &mut self.flatten_ctx, + &mut self.stroke_ctx, + self.width, + self.height, + ); + self.tiles + .make_tiles_analytic_aa(&self.line_buf, self.width, self.height); + self.tiles.sort_tiles(); + } + /// Access the tiles from the last generation call. pub fn tiles(&self) -> &Tiles { &self.tiles
diff --git a/sparse_strips/vello_example_scenes/src/lib.rs b/sparse_strips/vello_example_scenes/src/lib.rs index d4756b2..7b68b09 100644 --- a/sparse_strips/vello_example_scenes/src/lib.rs +++ b/sparse_strips/vello_example_scenes/src/lib.rs
@@ -444,6 +444,7 @@ scenes.push(AnyScene::new(path::TrickyStrokesScene::new())); scenes.push(AnyScene::new(path::FunkyPathsScene::new())); scenes.push(AnyScene::new(path::RobustPathsScene::new())); + scenes.push(AnyScene::new(path::RandomPolylineScene::new())); scenes.into_boxed_slice() } @@ -472,6 +473,7 @@ AnyScene::new(path::TrickyStrokesScene::new()), AnyScene::new(path::FunkyPathsScene::new()), AnyScene::new(path::RobustPathsScene::new()), + AnyScene::new(path::RandomPolylineScene::new()), ]; scenes.into_boxed_slice() }
diff --git a/sparse_strips/vello_example_scenes/src/path.rs b/sparse_strips/vello_example_scenes/src/path.rs index 54e124d..afe71b3 100644 --- a/sparse_strips/vello_example_scenes/src/path.rs +++ b/sparse_strips/vello_example_scenes/src/path.rs
@@ -11,7 +11,7 @@ //! - `robust_paths` method use crate::{ExampleScene, RenderingContext}; -use vello_common::color::palette::css::{AQUA, BLUE, GRAY, LIME, YELLOW}; +use vello_common::color::palette::css::{AQUA, BLUE, GRAY, LIME, REBECCA_PURPLE, YELLOW}; use vello_common::kurbo::{Affine, BezPath, Cap, Join, Point, Rect, Shape, Stroke}; use vello_common::peniko::{Color, Fill}; @@ -547,3 +547,29 @@ ctx.fill_path(&path); } } + +/// Scene that draws a filled polyline with pseudo-random vertices. +#[derive(Debug, Default)] +pub struct RandomPolylineScene; + +impl RandomPolylineScene { + /// Create a new random polyline scene. + pub fn new() -> Self { + Self + } +} + +impl ExampleScene for RandomPolylineScene { + fn render(&mut self, ctx: &mut impl RenderingContext, root_transform: Affine) { + ctx.set_transform(root_transform); + + let mut path = BezPath::new(); + path.move_to((100.0, 50.0)); + path.line_to((300.0, 250.0)); + path.line_to((50.0, 200.0)); + path.close_path(); + + ctx.set_paint(REBECCA_PURPLE); + ctx.fill_path(&path); + } +}
diff --git a/sparse_strips/vello_example_scenes/src/svg.rs b/sparse_strips/vello_example_scenes/src/svg.rs index 9165ebb..ba957aa 100644 --- a/sparse_strips/vello_example_scenes/src/svg.rs +++ b/sparse_strips/vello_example_scenes/src/svg.rs
@@ -182,7 +182,7 @@ Self { transform: Affine::scale(3.0), svg, - recording_enabled: true, + recording_enabled: false, recording: CachedRecording::new(), } } @@ -197,7 +197,7 @@ transform: Affine::scale(3.0), svg, recording: CachedRecording::new(), - recording_enabled: true, + recording_enabled: false, }) }
diff --git a/sparse_strips/vello_example_scenes/src/text.rs b/sparse_strips/vello_example_scenes/src/text.rs index 3c31cc8..7326e14 100644 --- a/sparse_strips/vello_example_scenes/src/text.rs +++ b/sparse_strips/vello_example_scenes/src/text.rs
@@ -118,7 +118,7 @@ Self { layout, - recording_enabled: true, + recording_enabled: false, recording: CachedRecording::new(), } }
diff --git a/sparse_strips/vello_hybrid/src/render/wgpu.rs b/sparse_strips/vello_hybrid/src/render/wgpu.rs index f1305c8..bf8ed82 100644 --- a/sparse_strips/vello_hybrid/src/render/wgpu.rs +++ b/sparse_strips/vello_hybrid/src/render/wgpu.rs
@@ -1504,11 +1504,12 @@ max_texture_dimension_2d: u32, winding_value_count: usize, ) { - // R16Float: 1 pixel per winding value. - let required_height = u32::try_from(winding_value_count) - .unwrap() - .div_ceil(max_texture_dimension_2d) - .max(u32::from(Tile::HEIGHT)); // at least one band + // R16Float: 1 pixel per winding value. Columns are TILE_HEIGHT pixels tall + // and wrap into bands of `max_texture_dimension_2d` columns each. + let tile_height = u32::from(Tile::HEIGHT); + let num_columns = u32::try_from(winding_value_count).unwrap().div_ceil(tile_height); + let num_bands = num_columns.div_ceil(max_texture_dimension_2d).max(1); + let required_height = num_bands * tile_height; let current_height = self.resources.winding_texture.height(); if required_height > current_height { assert!(
diff --git a/sparse_strips/vello_hybrid/src/scene.rs b/sparse_strips/vello_hybrid/src/scene.rs index 3c45c90..c5fff72 100644 --- a/sparse_strips/vello_hybrid/src/scene.rs +++ b/sparse_strips/vello_hybrid/src/scene.rs
@@ -371,9 +371,28 @@ ) { let strip_storage = &mut self.strip_storage.borrow_mut(); let strip_start = strip_storage.strips.len(); - #[cfg(feature = "wgpu")] - let alpha_offset = strip_storage.alphas.len() as u32; + #[cfg(feature = "wgpu")] + { + // GPU path: flatten + tile, then produce strips and tile-lines together. + // This replaces the CPU per-pixel alpha computation. + let alpha_offset = strip_storage.alphas.len() as u32; + self.strip_generator + .prepare_tiles_for_fill(path, transform); + let tiles = self.strip_generator.tiles(); + let lines = self.strip_generator.lines(); + let output = + gpu_winding::render_strips_and_tile_lines(tiles, fill_rule, lines, alpha_offset); + strip_storage.strips.extend_from_slice(&output.strips); + // Alphas buffer must match in length for winding texture sizing, but + // values are unused — the GPU winding pass fills the winding texture. + strip_storage + .alphas + .resize(output.winding_value_count as usize, 0); + self.tile_lines.extend(output.tile_lines); + } + + #[cfg(not(feature = "wgpu"))] self.strip_generator.generate_filled_path( path, fill_rule, @@ -383,24 +402,6 @@ self.clip_context.get(), ); - // Generate GPU tile-line instances from the tiles that are still available - // in the strip generator after the generate call. - #[cfg(feature = "wgpu")] - { - let tiles = self.strip_generator.tiles(); - let lines = self.strip_generator.lines(); - let output = - gpu_winding::render_strips_and_tile_lines(tiles, fill_rule, lines, alpha_offset); - debug_assert_eq!( - output.winding_value_count, - strip_storage.alphas.len() as u32, - "winding_value_count ({}) != alphas.len() ({})", - output.winding_value_count, - strip_storage.alphas.len(), - ); - self.tile_lines.extend(output.tile_lines); - } - submit_strips!(self, strip_storage, strip_start, paint); } @@ -456,9 +457,27 @@ ) { let strip_storage = &mut self.strip_storage.borrow_mut(); let strip_start = strip_storage.strips.len(); - #[cfg(feature = "wgpu")] - let alpha_offset = strip_storage.alphas.len() as u32; + #[cfg(feature = "wgpu")] + { + let alpha_offset = strip_storage.alphas.len() as u32; + self.strip_generator.prepare_tiles_for_stroke( + path, + &self.render_state.stroke, + transform, + ); + let tiles = self.strip_generator.tiles(); + let lines = self.strip_generator.lines(); + let output = + gpu_winding::render_strips_and_tile_lines(tiles, Fill::NonZero, lines, alpha_offset); + strip_storage.strips.extend_from_slice(&output.strips); + strip_storage + .alphas + .resize(output.winding_value_count as usize, 0); + self.tile_lines.extend(output.tile_lines); + } + + #[cfg(not(feature = "wgpu"))] self.strip_generator.generate_stroked_path( path, &self.render_state.stroke, @@ -468,22 +487,6 @@ self.clip_context.get(), ); - #[cfg(feature = "wgpu")] - { - let tiles = self.strip_generator.tiles(); - let lines = self.strip_generator.lines(); - let output = - gpu_winding::render_strips_and_tile_lines(tiles, Fill::NonZero, lines, alpha_offset); - debug_assert_eq!( - output.winding_value_count, - strip_storage.alphas.len() as u32, - "winding_value_count ({}) != alphas.len() ({})", - output.winding_value_count, - strip_storage.alphas.len(), - ); - self.tile_lines.extend(output.tile_lines); - } - submit_strips!(self, strip_storage, strip_start, paint); } @@ -842,7 +845,9 @@ /// Set the fill rule for subsequent fill operations. pub fn set_fill_rule(&mut self, fill_rule: Fill) { - unimplemented!(); + if fill_rule != Fill::NonZero { + unimplemented!(); + } self.render_state.fill_rule = fill_rule; }