[encoding] Replace linewidth stream with the new style encoding * The linewidth stream is now the "style" stream. This has been wired up all the way up to the `flatten` pipeline which uses the new encoding to extract the fill rule. * Pipelines downstream of `flatten` still use the old linewidth scheme. They will be fixed up in a follow-up change.
diff --git a/crates/encoding/src/encoding.rs b/crates/encoding/src/encoding.rs index 997fada..e569646 100644 --- a/crates/encoding/src/encoding.rs +++ b/crates/encoding/src/encoding.rs
@@ -1,7 +1,7 @@ // Copyright 2022 The Vello authors // SPDX-License-Identifier: Apache-2.0 OR MIT -use super::{DrawColor, DrawTag, PathEncoder, PathTag, Transform}; +use super::{DrawColor, DrawTag, PathEncoder, PathTag, Style, Transform}; use peniko::{kurbo::Shape, BlendMode, BrushRef, Color, Fill}; @@ -25,8 +25,8 @@ pub draw_data: Vec<u8>, /// The transform stream. pub transforms: Vec<Transform>, - /// The line width stream. - pub linewidths: Vec<f32>, + /// The style stream + pub styles: Vec<Style>, /// Late bound resource data. #[cfg(feature = "full")] pub resources: Resources, @@ -56,7 +56,7 @@ self.transforms.clear(); self.path_tags.clear(); self.path_data.clear(); - self.linewidths.clear(); + self.styles.clear(); self.draw_data.clear(); self.draw_tags.clear(); self.n_paths = 0; @@ -67,7 +67,7 @@ self.resources.reset(); if !is_fragment { self.transforms.push(Transform::IDENTITY); - self.linewidths.push(-1.0); + self.styles.push(Style::from_fill(&Fill::NonZero)); } } @@ -96,7 +96,7 @@ run.stream_offsets.draw_tags += offsets.draw_tags; run.stream_offsets.draw_data += offsets.draw_data; run.stream_offsets.transforms += offsets.transforms; - run.stream_offsets.linewidths += offsets.linewidths; + run.stream_offsets.styles += offsets.styles; run })); self.resources @@ -148,7 +148,7 @@ } else { self.transforms.extend_from_slice(&other.transforms); } - self.linewidths.extend_from_slice(&other.linewidths); + self.styles.extend_from_slice(&other.styles); } /// Returns a snapshot of the current stream offsets. @@ -159,19 +159,16 @@ draw_tags: self.draw_tags.len(), draw_data: self.draw_data.len(), transforms: self.transforms.len(), - linewidths: self.linewidths.len(), + styles: self.styles.len(), } } /// Encodes a fill style. pub fn encode_fill_style(&mut self, fill: Fill) { - let linewidth = match fill { - Fill::NonZero => -1.0, - Fill::EvenOdd => -2.0, - }; - if self.linewidths.last() != Some(&linewidth) { - self.path_tags.push(PathTag::LINEWIDTH); - self.linewidths.push(linewidth); + let style = Style::from_fill(&fill); + if self.styles.last() != Some(&style) { + self.path_tags.push(PathTag::STYLE); + self.styles.push(style); } } @@ -449,8 +446,8 @@ pub draw_data: usize, /// Current length of transform stream. pub transforms: usize, - /// Current length of linewidth stream. - pub linewidths: usize, + /// Current length of style stream. + pub styles: usize, } impl StreamOffsets { @@ -461,6 +458,6 @@ self.draw_tags += other.draw_tags; self.draw_data += other.draw_data; self.transforms += other.transforms; - self.linewidths += other.linewidths; + self.styles += other.styles; } }
diff --git a/crates/encoding/src/glyph_cache.rs b/crates/encoding/src/glyph_cache.rs index 3469c13..6ed8bb3 100644 --- a/crates/encoding/src/glyph_cache.rs +++ b/crates/encoding/src/glyph_cache.rs
@@ -105,7 +105,7 @@ draw_tags: self.end.draw_tags - self.start.draw_tags, draw_data: self.end.draw_data - self.start.draw_data, transforms: self.end.transforms - self.start.transforms, - linewidths: self.end.linewidths - self.start.linewidths, + styles: self.end.styles - self.start.styles, } } }
diff --git a/crates/encoding/src/lib.rs b/crates/encoding/src/lib.rs index 3d24c19..798bf98 100644 --- a/crates/encoding/src/lib.rs +++ b/crates/encoding/src/lib.rs
@@ -36,7 +36,7 @@ pub use monoid::Monoid; pub use path::{ Cubic, LineSoup, Path, PathBbox, PathEncoder, PathMonoid, PathSegment, PathSegmentType, - PathTag, SegmentCount, Tile, + PathTag, SegmentCount, Style, Tile, }; pub use resolve::{resolve_solid_paths_only, Layout};
diff --git a/crates/encoding/src/path.rs b/crates/encoding/src/path.rs index 9cd10c8..fb70e59 100644 --- a/crates/encoding/src/path.rs +++ b/crates/encoding/src/path.rs
@@ -267,8 +267,8 @@ /// Path marker. pub const PATH: Self = Self(0x10); - /// Line width setting. - pub const LINEWIDTH: Self = Self(0x40); + /// Style setting. + pub const STYLE: Self = Self(0x40); /// Bit for path segments that are represented as f32 values. If unset /// they are represented as i16. @@ -316,8 +316,8 @@ pub pathseg_ix: u32, /// Offset into path segment stream. pub pathseg_offset: u32, - /// Index into linewidth stream. - pub linewidth_ix: u32, + /// Index into style stream. + pub style_ix: u32, /// Index of containing path. pub path_ix: u32, } @@ -337,7 +337,8 @@ a += a >> 16; c.pathseg_offset = a & 0xff; c.path_ix = (tag_word & (PathTag::PATH.0 as u32 * 0x1010101)).count_ones(); - c.linewidth_ix = (tag_word & (PathTag::LINEWIDTH.0 as u32 * 0x1010101)).count_ones(); + let style_size = (std::mem::size_of::<Style>() / std::mem::size_of::<u32>()) as u32; + c.style_ix = (tag_word & (PathTag::STYLE.0 as u32 * 0x1010101)).count_ones() * style_size; c } @@ -347,7 +348,7 @@ trans_ix: self.trans_ix + other.trans_ix, pathseg_ix: self.pathseg_ix + other.pathseg_ix, pathseg_offset: self.pathseg_offset + other.pathseg_offset, - linewidth_ix: self.linewidth_ix + other.linewidth_ix, + style_ix: self.style_ix + other.style_ix, path_ix: self.path_ix + other.path_ix, } }
diff --git a/crates/encoding/src/resolve.rs b/crates/encoding/src/resolve.rs index dfd89bc..367f400 100644 --- a/crates/encoding/src/resolve.rs +++ b/crates/encoding/src/resolve.rs
@@ -3,7 +3,7 @@ use bytemuck::{Pod, Zeroable}; -use super::{DrawTag, Encoding, PathTag, StreamOffsets, Transform}; +use super::{DrawTag, Encoding, PathTag, StreamOffsets, Style, Transform}; #[cfg(feature = "full")] use { @@ -38,8 +38,8 @@ pub draw_data_base: u32, /// Start of transform stream. pub transform_base: u32, - /// Start of linewidth stream. - pub linewidth_base: u32, + /// Start of style stream. + pub style_base: u32, } impl Layout { @@ -92,13 +92,13 @@ /// Returns the transform stream. pub fn transforms<'a>(&self, data: &'a [u8]) -> &'a [Transform] { let start = self.transform_base as usize * 4; - let end = self.linewidth_base as usize * 4; + let end = self.style_base as usize * 4; bytemuck::cast_slice(&data[start..end]) } - /// Returns the linewidth stream. - pub fn linewidths<'a>(&self, data: &'a [u8]) -> &'a [f32] { - let start = self.linewidth_base as usize * 4; + /// Returns the style stream. + pub fn styles<'a>(&self, data: &'a [u8]) -> &'a [Style] { + let start = self.style_base as usize * 4; bytemuck::cast_slice(&data[start..]) } } @@ -150,9 +150,9 @@ // Transform stream layout.transform_base = size_to_words(data.len()); data.extend_from_slice(bytemuck::cast_slice(&encoding.transforms)); - // Linewidth stream - layout.linewidth_base = size_to_words(data.len()); - data.extend_from_slice(bytemuck::cast_slice(&encoding.linewidths)); + // Style stream + layout.style_base = size_to_words(data.len()); + data.extend_from_slice(bytemuck::cast_slice(&encoding.styles)); layout.n_draw_objects = layout.n_paths; assert_eq!(buffer_size, data.len()); layout @@ -359,21 +359,21 @@ data.extend_from_slice(bytemuck::cast_slice(&stream[pos..])); } } - // Linewidth stream - layout.linewidth_base = size_to_words(data.len()); + // Style stream + layout.style_base = size_to_words(data.len()); { let mut pos = 0; - let stream = &encoding.linewidths; + let stream = &encoding.styles; for patch in &self.patches { if let ResolvedPatch::GlyphRun { index, glyphs, .. } = patch { - let stream_offset = resources.glyph_runs[*index].stream_offsets.linewidths; + let stream_offset = resources.glyph_runs[*index].stream_offsets.styles; if pos < stream_offset { data.extend_from_slice(bytemuck::cast_slice(&stream[pos..stream_offset])); pos = stream_offset; } for glyph in &self.glyph_ranges[glyphs.clone()] { - let glyph_data = &self.glyph_cache.encoding.linewidths - [glyph.start.linewidths..glyph.end.linewidths]; + let glyph_data = + &self.glyph_cache.encoding.styles[glyph.start.styles..glyph.end.styles]; data.extend_from_slice(bytemuck::cast_slice(glyph_data)); } } @@ -612,7 +612,7 @@ ) + slice_size_in_bytes(&encoding.draw_data, patch_sizes.draw_data) + slice_size_in_bytes(&encoding.transforms, patch_sizes.transforms) - + slice_size_in_bytes(&encoding.linewidths, patch_sizes.linewidths); + + slice_size_in_bytes(&encoding.styles, patch_sizes.styles); Self { buffer_size, path_tag_padded,
diff --git a/shader/flatten.wgsl b/shader/flatten.wgsl index e27bb6e..a5d328f 100644 --- a/shader/flatten.wgsl +++ b/shader/flatten.wgsl
@@ -218,7 +218,13 @@ var tag_byte = (tag_word >> shift) & 0xffu; let out = &path_bboxes[tm.path_ix]; - let linewidth = bitcast<f32>(scene[config.linewidth_base + tm.linewidth_ix]); + let style_flags = scene[config.style_base + tm.style_ix]; + // TODO: We assume all paths are fills at the moment. This is where we will extract the stroke + // vs fill state using STYLE_FLAGS_STYLE_BIT. + // TODO: The downstream pipelines still use the old floating-point linewidth/fill encoding scheme + // This will change to represent the fill rule as a single bit inside the bounding box and draw + // info data structures. + let linewidth = select(-2.0, -1.0, (style_flags & STYLE_FLAGS_FILL_BIT) == 0u); if (tag_byte & PATH_TAG_PATH) != 0u { (*out).linewidth = linewidth; (*out).trans_ix = tm.trans_ix;
diff --git a/shader/shared/config.wgsl b/shader/shared/config.wgsl index 1e553f9..fcae33c 100644 --- a/shader/shared/config.wgsl +++ b/shader/shared/config.wgsl
@@ -29,7 +29,7 @@ drawdata_base: u32, transform_base: u32, - linewidth_base: u32, + style_base: u32, // Sizes of bump allocated buffers (in element size units) binning_size: u32,
diff --git a/shader/shared/pathtag.wgsl b/shader/shared/pathtag.wgsl index 9777f47..0aa942d 100644 --- a/shader/shared/pathtag.wgsl +++ b/shader/shared/pathtag.wgsl
@@ -6,7 +6,7 @@ pathseg_ix: u32, pathseg_offset: u32, #ifdef full - linewidth_ix: u32, + style_ix: u32, path_ix: u32, #endif } @@ -19,9 +19,16 @@ let PATH_TAG_TRANSFORM = 0x20u; #ifdef full let PATH_TAG_PATH = 0x10u; -let PATH_TAG_LINEWIDTH = 0x40u; +let PATH_TAG_STYLE = 0x40u; #endif +// Size of the `Style` data structure in words +let STYLE_SIZE_IN_WORDS: u32 = 2u; +let STYLE_FLAGS_STYLE_BIT: u32 = 0x80000000u; +let STYLE_FLAGS_FILL_BIT: u32 = 0x40000000u; + +// TODO: Declare the remaining STYLE flags here. + fn tag_monoid_identity() -> TagMonoid { return TagMonoid(); } @@ -32,7 +39,7 @@ c.pathseg_ix = a.pathseg_ix + b.pathseg_ix; c.pathseg_offset = a.pathseg_offset + b.pathseg_offset; #ifdef full - c.linewidth_ix = a.linewidth_ix + b.linewidth_ix; + c.style_ix = a.style_ix + b.style_ix; c.path_ix = a.path_ix + b.path_ix; #endif return c; @@ -50,7 +57,7 @@ c.pathseg_offset = a & 0xffu; #ifdef full c.path_ix = countOneBits(tag_word & (PATH_TAG_PATH * 0x1010101u)); - c.linewidth_ix = countOneBits(tag_word & (PATH_TAG_LINEWIDTH * 0x1010101u)); + c.style_ix = countOneBits(tag_word & (PATH_TAG_STYLE * 0x1010101u)) * STYLE_SIZE_IN_WORDS; #endif return c; }
diff --git a/src/cpu_shader/flatten.rs b/src/cpu_shader/flatten.rs index 2cdf725..150aec1 100644 --- a/src/cpu_shader/flatten.rs +++ b/src/cpu_shader/flatten.rs
@@ -4,7 +4,9 @@ use crate::cpu_dispatch::CpuBinding; use super::util::{Transform, Vec2}; -use vello_encoding::{BumpAllocators, ConfigUniform, LineSoup, Monoid, PathBbox, PathMonoid}; +use vello_encoding::{ + BumpAllocators, ConfigUniform, LineSoup, Monoid, PathBbox, PathMonoid, Style, +}; fn to_minus_one_quarter(x: f32) -> f32 { // could also be written x.powf(-0.25) @@ -211,11 +213,19 @@ if tag_byte != 0 { tm = tag_monoids[ix >> 2].combine(&tm); } - let linewidth = - f32::from_bits(scene[(config.layout.linewidth_base + tm.linewidth_ix) as usize]); + let style_flags = scene[(config.layout.style_base + tm.style_ix) as usize]; if (tag_byte & PATH_TAG_PATH) != 0 { let out = &mut path_bboxes[tm.path_ix as usize]; - out.linewidth = linewidth; + // TODO: We assume all paths are fills at the moment. This is where we will extract the + // stroke vs fill state using STYLE_FLAGS_STYLE_BIT. + // TODO: The downstream pipelines still use the old floating-point linewidth/fill + // encoding scheme. This will change to represent the fill rule as a single bit inside + // the bounding box and draw info data structures. + out.linewidth = if (style_flags & Style::FLAGS_FILL_BIT) == 0 { + -1.0 + } else { + -2.0 + }; out.trans_ix = tm.trans_ix; } let seg_type = tag_byte & PATH_TAG_SEG_TYPE;