diff --git a/crates/encoding/src/draw.rs b/crates/encoding/src/draw.rs index 8e8b1ba07..0b9864a63 100644 --- a/crates/encoding/src/draw.rs +++ b/crates/encoding/src/draw.rs @@ -41,6 +41,11 @@ impl DrawTag { } } +/// The first word of each draw info stream entry contains the flags. This is not a part of the +/// draw object stream but get used after the draw objects have been reduced on the GPU. +/// 0 represents a non-zero fill. 1 represents an even-odd fill. +pub const DRAW_INFO_FLAGS_FILL_RULE_BIT: u32 = 1; + /// Draw object bounding box. #[derive(Copy, Clone, Pod, Zeroable, Debug, Default)] #[repr(C)] diff --git a/crates/encoding/src/lib.rs b/crates/encoding/src/lib.rs index 798bf9890..9b61cc5bf 100644 --- a/crates/encoding/src/lib.rs +++ b/crates/encoding/src/lib.rs @@ -29,7 +29,7 @@ pub use config::{ }; pub use draw::{ DrawBbox, DrawBeginClip, DrawColor, DrawImage, DrawLinearGradient, DrawMonoid, - DrawRadialGradient, DrawTag, + DrawRadialGradient, DrawTag, DRAW_INFO_FLAGS_FILL_RULE_BIT, }; pub use encoding::{Encoding, StreamOffsets}; pub use math::Transform; diff --git a/crates/encoding/src/path.rs b/crates/encoding/src/path.rs index f586f8773..83e537042 100644 --- a/crates/encoding/src/path.rs +++ b/crates/encoding/src/path.rs @@ -380,8 +380,8 @@ pub struct PathBbox { pub x1: i32, /// Maximum y value. pub y1: i32, - /// Line width. - pub linewidth: f32, + /// Style flags + pub draw_flags: u32, /// Index into the transform stream. pub trans_ix: u32, } diff --git a/shader/bbox_clear.wgsl b/shader/bbox_clear.wgsl index fe8ccebbd..b2b9f93b3 100644 --- a/shader/bbox_clear.wgsl +++ b/shader/bbox_clear.wgsl @@ -1,19 +1,11 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT OR Unlicense #import config +#import bbox @group(0) @binding(0) var config: Config; -struct PathBbox { - x0: i32, - y0: i32, - x1: i32, - y1: i32, - linewidth: f32, - trans_ix: u32, -} - @group(0) @binding(1) var path_bboxes: array; diff --git a/shader/coarse.wgsl b/shader/coarse.wgsl index 0635443fe..44f30ae81 100644 --- a/shader/coarse.wgsl +++ b/shader/coarse.wgsl @@ -82,8 +82,8 @@ fn alloc_cmd(size: u32) { } } -fn write_path(tile: Tile, tile_ix: u32, linewidth: f32) -> bool { - let even_odd = linewidth < -1.0; +fn write_path(tile: Tile, tile_ix: u32, draw_flags: u32) -> bool { + let even_odd = (draw_flags & DRAW_INFO_FLAGS_FILL_RULE_BIT) != 0u; // We overload the "segments" field to store both count (written by // path_count stage) and segment allocation (used by path_tiling and // fine). @@ -350,16 +350,16 @@ fn main( switch drawtag { // DRAWTAG_FILL_COLOR case 0x44u: { - let linewidth = bitcast(info_bin_data[di]); - if write_path(tile, tile_ix, linewidth) { + let draw_flags = info_bin_data[di]; + if write_path(tile, tile_ix, draw_flags) { let rgba_color = scene[dd]; write_color(CmdColor(rgba_color)); } } // DRAWTAG_FILL_LIN_GRADIENT case 0x114u: { - let linewidth = bitcast(info_bin_data[di]); - if write_path(tile, tile_ix, linewidth) { + let draw_flags = info_bin_data[di]; + if write_path(tile, tile_ix, draw_flags) { let index = scene[dd]; let info_offset = di + 1u; write_grad(CMD_LIN_GRAD, index, info_offset); @@ -367,8 +367,8 @@ fn main( } // DRAWTAG_FILL_RAD_GRADIENT case 0x29cu: { - let linewidth = bitcast(info_bin_data[di]); - if write_path(tile, tile_ix, linewidth) { + let draw_flags = info_bin_data[di]; + if write_path(tile, tile_ix, draw_flags) { let index = scene[dd]; let info_offset = di + 1u; write_grad(CMD_RAD_GRAD, index, info_offset); @@ -376,8 +376,8 @@ fn main( } // DRAWTAG_FILL_IMAGE case 0x248u: { - let linewidth = bitcast(info_bin_data[di]); - if write_path(tile, tile_ix, linewidth) { + let draw_flags = info_bin_data[di]; + if write_path(tile, tile_ix, draw_flags) { write_image(di + 1u); } } @@ -395,7 +395,7 @@ fn main( // DRAWTAG_END_CLIP case 0x21u: { clip_depth -= 1u; - write_path(tile, tile_ix, -1.0); + write_path(tile, tile_ix, 0u); let blend = scene[dd]; let alpha = bitcast(scene[dd + 1u]); write_end_clip(CmdEndClip(blend, alpha)); diff --git a/shader/draw_leaf.wgsl b/shader/draw_leaf.wgsl index 827825974..920d6714b 100644 --- a/shader/draw_leaf.wgsl +++ b/shader/draw_leaf.wgsl @@ -109,25 +109,20 @@ fn main( // let y1 = f32(bbox.y1); // let bbox_f = vec4(x0, y0, x1, y1); var transform = Transform(); - var linewidth = bbox.linewidth; - if linewidth >= 0.0 || tag_word == DRAWTAG_FILL_LIN_GRADIENT || tag_word == DRAWTAG_FILL_RAD_GRADIENT || + let draw_flags = bbox.draw_flags; + if tag_word == DRAWTAG_FILL_LIN_GRADIENT || tag_word == DRAWTAG_FILL_RAD_GRADIENT || tag_word == DRAWTAG_FILL_IMAGE { transform = read_transform(config.transform_base, bbox.trans_ix); } - if linewidth >= 0.0 { - // Note: doesn't deal with anisotropic case - let matrx = transform.matrx; - linewidth *= sqrt(abs(matrx.x * matrx.w - matrx.y * matrx.z)); - } switch tag_word { // DRAWTAG_FILL_COLOR case 0x44u: { - info[di] = bitcast(linewidth); + info[di] = draw_flags; } // DRAWTAG_FILL_LIN_GRADIENT case 0x114u: { - info[di] = bitcast(linewidth); + info[di] = draw_flags; var p0 = bitcast>(vec2(scene[dd + 1u], scene[dd + 2u])); var p1 = bitcast>(vec2(scene[dd + 3u], scene[dd + 4u])); p0 = transform_apply(transform, p0); @@ -146,7 +141,7 @@ fn main( // on the algorithm at // This epsilon matches what Skia uses let GRADIENT_EPSILON = 1.0 / f32(1 << 12u); - info[di] = bitcast(linewidth); + info[di] = draw_flags; var p0 = bitcast>(vec2(scene[dd + 1u], scene[dd + 2u])); var p1 = bitcast>(vec2(scene[dd + 3u], scene[dd + 4u])); var r0 = bitcast(scene[dd + 5u]); @@ -226,7 +221,7 @@ fn main( } // DRAWTAG_FILL_IMAGE case 0x248u: { - info[di] = bitcast(linewidth); + info[di] = draw_flags; let inv = transform_inverse(transform); info[di + 1u] = bitcast(inv.matrx.x); info[di + 2u] = bitcast(inv.matrx.y); diff --git a/shader/flatten.wgsl b/shader/flatten.wgsl index a5d328f09..11b7818c0 100644 --- a/shader/flatten.wgsl +++ b/shader/flatten.wgsl @@ -3,6 +3,7 @@ // Flatten curves to lines #import config +#import drawtag #import pathtag #import segment #import cubic @@ -22,7 +23,7 @@ struct AtomicPathBbox { y0: atomic, x1: atomic, y1: atomic, - linewidth: f32, + draw_flags: u32, trans_ix: u32, } @@ -221,12 +222,9 @@ fn main( 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); + let draw_flags = select(DRAW_INFO_FLAGS_FILL_RULE_BIT, 0u, (style_flags & STYLE_FLAGS_FILL_BIT) == 0u); if (tag_byte & PATH_TAG_PATH) != 0u { - (*out).linewidth = linewidth; + (*out).draw_flags = draw_flags; (*out).trans_ix = tm.trans_ix; } // Decode path data @@ -277,15 +275,19 @@ fn main( } } var stroke = vec2(0.0, 0.0); - if linewidth >= 0.0 { + let is_stroke = (style_flags & STYLE_FLAGS_STYLE_BIT) != 0u; + /* + // TODO: the stroke handling here is dead code until the new explansion and flattening logic + // is implemented. + if is_stroke { // See https://www.iquilezles.org/www/articles/ellipses/ellipses.htm // This is the correct bounding box, but we're not handling rendering // in the isotropic case, so it may mismatch. stroke = 0.5 * linewidth * vec2(length(transform.mat.xz), length(transform.mat.yw)); bbox += vec4(-stroke, stroke); } - let flags = u32(linewidth >= 0.0); - flatten_cubic(Cubic(p0, p1, p2, p3, stroke, tm.path_ix, flags)); + */ + flatten_cubic(Cubic(p0, p1, p2, p3, stroke, tm.path_ix, u32(is_stroke))); // Update bounding box using atomics only. Computing a monoid is a // potential future optimization. if bbox.z > bbox.x || bbox.w > bbox.y { diff --git a/shader/shared/bbox.wgsl b/shader/shared/bbox.wgsl index 714602d9d..a3e23040a 100644 --- a/shader/shared/bbox.wgsl +++ b/shader/shared/bbox.wgsl @@ -4,12 +4,16 @@ // but contains a link to the active transform, mostly for gradients. // Coordinates are integer pixels (for the convenience of atomic update) // but will probably become fixed-point fractions for rectangles. +// +// TODO: This also carries a `draw_flags` field that contains information that gets propagated to +// the draw info stream. This is currently only used for the fill rule. If the other bits remain +// unused we could possibly pack this into some other field, such as the the MSB of `trans_ix`. struct PathBbox { x0: i32, y0: i32, x1: i32, y1: i32, - linewidth: f32, + draw_flags: u32, trans_ix: u32, } diff --git a/shader/shared/drawtag.wgsl b/shader/shared/drawtag.wgsl index 0b8ae41e7..231657f52 100644 --- a/shader/shared/drawtag.wgsl +++ b/shader/shared/drawtag.wgsl @@ -23,6 +23,11 @@ let DRAWTAG_FILL_IMAGE = 0x248u; let DRAWTAG_BEGIN_CLIP = 0x9u; let DRAWTAG_END_CLIP = 0x21u; +/// The first word of each draw info stream entry contains the flags. This is not a part of the +/// draw object stream but get used after the draw objects have been reduced on the GPU. +/// 0 represents a non-zero fill. 1 represents an even-odd fill. +let DRAW_INFO_FLAGS_FILL_RULE_BIT = 1u; + fn draw_monoid_identity() -> DrawMonoid { return DrawMonoid(); } diff --git a/src/cpu_shader/coarse.rs b/src/cpu_shader/coarse.rs index 390df7f74..70304ad46 100644 --- a/src/cpu_shader/coarse.rs +++ b/src/cpu_shader/coarse.rs @@ -241,7 +241,10 @@ fn coarse_main( let n_segs = tile.segment_count_or_ix; let include_tile = n_segs != 0 || (tile.backdrop == 0) == is_clip || is_blend; if include_tile { - // TODO: get drawinfo (linewidth for fills) + // TODO: The first word of the info buffer (`info[di]`) contains flags that + // indicate the even-odd vs non-zero fill rule. Read that here and pass it + // to `write_path` so it gets propagated to the PTCL (see + // `PathBbox::FLAGS_FILL_STYLE_BIT` for its interpretation). match DrawTag(drawtag) { DrawTag::COLOR => { tile_state.write_path(config, bump, ptcl, tile); diff --git a/src/cpu_shader/draw_leaf.rs b/src/cpu_shader/draw_leaf.rs index 0aa779e5c..6dfa82208 100644 --- a/src/cpu_shader/draw_leaf.rs +++ b/src/cpu_shader/draw_leaf.rs @@ -41,13 +41,13 @@ fn draw_leaf_main( { let bbox = path_bbox[m.path_ix as usize]; let transform = Transform::read(config.layout.transform_base, bbox.trans_ix, scene); - let linewidth = bbox.linewidth; + let flags = bbox.draw_flags; match tag_word { DrawTag::COLOR => { - info[di] = f32::to_bits(linewidth); + info[di] = flags; } DrawTag::LINEAR_GRADIENT => { - info[di] = f32::to_bits(linewidth); + info[di] = flags; let p0 = Vec2::new( f32::from_bits(scene[dd as usize + 1]), f32::from_bits(scene[dd as usize + 2]), @@ -67,7 +67,7 @@ fn draw_leaf_main( info[di + 3] = f32::to_bits(line_c); } DrawTag::RADIAL_GRADIENT => { - info[di] = f32::to_bits(linewidth); + info[di] = flags; let p0 = Vec2::new( f32::from_bits(scene[dd as usize + 1]), f32::from_bits(scene[dd as usize + 2]), @@ -108,7 +108,7 @@ fn draw_leaf_main( info[di + 19] = f32::to_bits(roff); } DrawTag::IMAGE => { - info[di] = f32::to_bits(linewidth); + info[di] = flags; let z = transform.0; let inv_det = (z[0] * z[3] - z[1] * z[2]).recip(); let inv_mat = [ diff --git a/src/cpu_shader/flatten.rs b/src/cpu_shader/flatten.rs index 150aec174..bbf9e49ef 100644 --- a/src/cpu_shader/flatten.rs +++ b/src/cpu_shader/flatten.rs @@ -6,6 +6,7 @@ use crate::cpu_dispatch::CpuBinding; use super::util::{Transform, Vec2}; use vello_encoding::{ BumpAllocators, ConfigUniform, LineSoup, Monoid, PathBbox, PathMonoid, Style, + DRAW_INFO_FLAGS_FILL_RULE_BIT, }; fn to_minus_one_quarter(x: f32) -> f32 { @@ -218,13 +219,10 @@ fn flatten_main( let out = &mut path_bboxes[tm.path_ix as usize]; // 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 + out.draw_flags = if (style_flags & Style::FLAGS_FILL_BIT) == 0 { + 0 } else { - -2.0 + DRAW_INFO_FLAGS_FILL_RULE_BIT }; out.trans_ix = tm.trans_ix; }