Skip to content

Commit 5c534c6

Browse files
committed
Implement new clipping
1 parent 462fd4e commit 5c534c6

File tree

20 files changed

+1184
-66
lines changed

20 files changed

+1184
-66
lines changed

sparse_strips/vello_common/src/clip.rs

Lines changed: 717 additions & 0 deletions
Large diffs are not rendered by default.

sparse_strips/vello_common/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ extern crate alloc;
6262
extern crate std;
6363

6464
pub mod blurred_rounded_rect;
65+
pub mod clip;
6566
pub mod coarse;
6667
#[cfg(feature = "text")]
6768
pub mod colr;

sparse_strips/vello_common/src/strip.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use alloc::vec::Vec;
1111
use fearless_simd::*;
1212

1313
/// A strip.
14-
#[derive(Debug, Clone, Copy)]
14+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1515
pub struct Strip {
1616
/// The x coordinate of the strip, in user coordinates.
1717
pub x: u16,
@@ -44,7 +44,12 @@ impl Strip {
4444
}
4545
}
4646

47-
/// Returns the y coordinate of the strip, in strip units.
47+
/// Return whether the strip is a sentinel strip.
48+
pub fn is_sentinel(&self) -> bool {
49+
self.x == u16::MAX
50+
}
51+
52+
/// Return the y coordinate of the strip, in strip units.
4853
pub fn strip_y(&self) -> u16 {
4954
self.y / Tile::HEIGHT
5055
}

sparse_strips/vello_common/src/strip_generator.rs

Lines changed: 52 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
//! Abstraction for generating strips from paths.
55
6+
use crate::clip::{PathDataRef, intersect};
67
use crate::fearless_simd::Level;
78
use crate::flatten::{FlattenCtx, Line};
89
use crate::kurbo::{Affine, PathEl, Stroke};
@@ -14,7 +15,7 @@ use alloc::vec::Vec;
1415
use peniko::kurbo::StrokeCtx;
1516

1617
/// A storage for storing strip-related data.
17-
#[derive(Debug, Default)]
18+
#[derive(Debug, Default, PartialEq, Eq)]
1819
pub struct StripStorage {
1920
/// The strips in the storage.
2021
pub strips: Vec<Strip>,
@@ -49,15 +50,22 @@ impl StripStorage {
4950
pub fn is_empty(&self) -> bool {
5051
self.strips.is_empty() && self.alphas.is_empty()
5152
}
53+
54+
/// Extend the current strip storage with the data from another storage.
55+
pub fn extend(&mut self, other: &Self) {
56+
self.strips.extend(&other.strips);
57+
self.alphas.extend(&other.alphas);
58+
}
5259
}
5360

5461
/// An object for easily generating strips for a filled/stroked path.
5562
#[derive(Debug)]
5663
pub struct StripGenerator {
57-
level: Level,
64+
pub(crate) level: Level,
5865
line_buf: Vec<Line>,
5966
flatten_ctx: FlattenCtx,
6067
stroke_ctx: StrokeCtx,
68+
temp_storage: StripStorage,
6169
tiles: Tiles,
6270
width: u16,
6371
height: u16,
@@ -72,6 +80,7 @@ impl StripGenerator {
7280
tiles: Tiles::new(level),
7381
flatten_ctx: FlattenCtx::default(),
7482
stroke_ctx: StrokeCtx::default(),
83+
temp_storage: StripStorage::default(),
7584
width,
7685
height,
7786
}
@@ -85,6 +94,7 @@ impl StripGenerator {
8594
transform: Affine,
8695
aliasing_threshold: Option<u8>,
8796
strip_storage: &mut StripStorage,
97+
clip_path: Option<PathDataRef<'_>>,
8898
) {
8999
flatten::fill(
90100
self.level,
@@ -93,7 +103,8 @@ impl StripGenerator {
93103
&mut self.line_buf,
94104
&mut self.flatten_ctx,
95105
);
96-
self.make_strips(strip_storage, fill_rule, aliasing_threshold);
106+
107+
self.generate_with_clip(aliasing_threshold, strip_storage, fill_rule, clip_path);
97108
}
98109

99110
/// Generate the strips for a stroked path.
@@ -104,6 +115,7 @@ impl StripGenerator {
104115
transform: Affine,
105116
aliasing_threshold: Option<u8>,
106117
strip_storage: &mut StripStorage,
118+
clip_path: Option<PathDataRef<'_>>,
107119
) {
108120
flatten::stroke(
109121
self.level,
@@ -114,34 +126,64 @@ impl StripGenerator {
114126
&mut self.flatten_ctx,
115127
&mut self.stroke_ctx,
116128
);
117-
self.make_strips(strip_storage, Fill::NonZero, aliasing_threshold);
129+
self.generate_with_clip(aliasing_threshold, strip_storage, Fill::NonZero, clip_path);
130+
}
131+
132+
fn generate_with_clip(
133+
&mut self,
134+
aliasing_threshold: Option<u8>,
135+
strip_storage: &mut StripStorage,
136+
fill_rule: Fill,
137+
clip_path: Option<PathDataRef<'_>>,
138+
) {
139+
if strip_storage.generation_mode == GenerationMode::Replace {
140+
strip_storage.strips.clear();
141+
}
142+
143+
if let Some(clip_path) = clip_path {
144+
self.make_strips(strip_storage, fill_rule, aliasing_threshold, true);
145+
let path_data = PathDataRef {
146+
strips: &self.temp_storage.strips,
147+
alphas: &self.temp_storage.alphas,
148+
};
149+
150+
intersect(self.level, clip_path, path_data, strip_storage);
151+
} else {
152+
self.make_strips(strip_storage, fill_rule, aliasing_threshold, false);
153+
}
118154
}
119155

120156
/// Reset the strip generator.
121157
pub fn reset(&mut self) {
122158
self.line_buf.clear();
123159
self.tiles.reset();
160+
self.temp_storage.clear();
124161
}
125162

126163
fn make_strips(
127164
&mut self,
128165
strip_storage: &mut StripStorage,
129166
fill_rule: Fill,
130167
aliasing_threshold: Option<u8>,
168+
temp: bool,
131169
) {
132170
self.tiles
133171
.make_tiles(&self.line_buf, self.width, self.height);
134172
self.tiles.sort_tiles();
135173

136-
if strip_storage.generation_mode == GenerationMode::Replace {
137-
strip_storage.strips.clear();
138-
}
174+
let storage = if temp {
175+
self.temp_storage.clear();
176+
177+
&mut self.temp_storage
178+
} else {
179+
strip_storage
180+
};
139181

140182
strip::render(
141183
self.level,
142184
&self.tiles,
143-
&mut strip_storage.strips,
144-
&mut strip_storage.alphas,
185+
&mut storage.strips,
186+
&mut storage.alphas,
145187
fill_rule,
146188
aliasing_threshold,
147189
&self.line_buf,
@@ -168,6 +210,7 @@ mod tests {
168210
Affine::IDENTITY,
169211
None,
170212
&mut storage,
213+
None,
171214
);
172215

173216
assert!(!generator.line_buf.is_empty());

sparse_strips/vello_common/src/util.rs

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
//! Utility functions.
55
6-
use fearless_simd::{Simd, f32x16, u8x16};
6+
use fearless_simd::{Simd, SimdBase, f32x16, u8x16, u8x32, u16x16, u16x32};
77

88
/// Convert f32x16 to u8x16.
99
#[inline(always)]
@@ -19,3 +19,39 @@ pub fn f32_to_u8<S: Simd>(val: f32x16<S>) -> u8x16<S> {
1919
let uzp2 = simd.unzip_low_u8x16(p3, p4);
2020
simd.unzip_low_u8x16(uzp1, uzp2)
2121
}
22+
23+
/// A trait for implementing a fast approximal division by 255 for integers.
24+
pub trait Div255Ext {
25+
/// Divide by 255.
26+
fn div_255(self) -> Self;
27+
}
28+
29+
impl<S: Simd> Div255Ext for u16x32<S> {
30+
#[inline(always)]
31+
fn div_255(self) -> Self {
32+
let p1 = Self::splat(self.simd, 255);
33+
let p2 = self + p1;
34+
p2.shr(8)
35+
}
36+
}
37+
38+
impl<S: Simd> Div255Ext for u16x16<S> {
39+
#[inline(always)]
40+
fn div_255(self) -> Self {
41+
let p1 = Self::splat(self.simd, 255);
42+
let p2 = self + p1;
43+
p2.shr(8)
44+
}
45+
}
46+
47+
/// Perform a normalized multiplication for u8x32.
48+
#[inline(always)]
49+
pub fn normalized_mul_u8x32<S: Simd>(a: u8x32<S>, b: u8x32<S>) -> u16x32<S> {
50+
(S::widen_u8x32(a.simd, a) * S::widen_u8x32(b.simd, b)).div_255()
51+
}
52+
53+
/// Perform a normalized multiplication for u8x16.
54+
#[inline(always)]
55+
pub fn normalized_mul_u8x16<S: Simd>(a: u8x16<S>, b: u8x16<S>) -> u16x16<S> {
56+
(S::widen_u8x16(a.simd, a) * S::widen_u8x16(b.simd, b)).div_255()
57+
}

sparse_strips/vello_cpu/src/dispatch/mod.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,14 @@ pub(crate) trait Dispatcher: Debug + Send + Sync {
3535
paint: Paint,
3636
aliasing_threshold: Option<u8>,
3737
);
38+
fn push_clip_path(
39+
&mut self,
40+
path: &BezPath,
41+
fill_rule: Fill,
42+
transform: Affine,
43+
aliasing_threshold: Option<u8>,
44+
);
45+
fn pop_clip_path(&mut self);
3846
fn push_layer(
3947
&mut self,
4048
clip_path: Option<&BezPath>,

sparse_strips/vello_cpu/src/dispatch/multi_threaded.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use std::ops::Range;
2121
use std::sync::atomic::{AtomicBool, AtomicU8, Ordering};
2222
use std::sync::{Barrier, Mutex};
2323
use thread_local::ThreadLocal;
24+
use vello_common::clip::ClipContext;
2425
use vello_common::coarse::{Cmd, MODE_CPU, Wide};
2526
use vello_common::encode::EncodedPaint;
2627
use vello_common::fearless_simd::{Level, Simd, simd_dispatch};
@@ -49,6 +50,7 @@ type CoarseTaskReceiver = ordered_channel::Receiver<CoarseTask>;
4950
pub(crate) struct MultiThreadedDispatcher {
5051
/// The wide tile container.
5152
wide: Wide,
53+
clip_context: ClipContext,
5254
/// The thread pool that is used for dispatching tasks.
5355
thread_pool: ThreadPool,
5456
allocation_group: AllocationGroup,
@@ -141,6 +143,7 @@ impl MultiThreadedDispatcher {
141143
task_idx,
142144
flushed,
143145
workers,
146+
clip_context: ClipContext::new(),
144147
task_sender: None,
145148
coarse_task_receiver: None,
146149
strip_generator: StripGenerator::new(width, height, level),
@@ -248,8 +251,13 @@ impl MultiThreadedDispatcher {
248251
let allocation_group =
249252
std::mem::replace(&mut self.allocation_group, self.allocations.get());
250253
let task_sender = self.task_sender.as_mut().unwrap();
254+
let clip_path = self.clip_context.get().map(|c| OwnedClip {
255+
strips: c.strips.into(),
256+
alphas: c.alphas.into(),
257+
});
251258
let task = RenderTask {
252259
idx: task_idx,
260+
clip_path,
253261
allocation_group,
254262
};
255263
task_sender.send(task).unwrap();
@@ -453,6 +461,7 @@ impl Dispatcher for MultiThreadedDispatcher {
453461

454462
fn reset(&mut self) {
455463
self.wide.reset();
464+
self.clip_context.reset();
456465
self.allocation_group.clear();
457466
self.batch_cost = 0.0;
458467
self.task_idx = 0;
@@ -544,6 +553,28 @@ impl Dispatcher for MultiThreadedDispatcher {
544553
fn strip_storage_mut(&mut self) -> &mut StripStorage {
545554
&mut self.strip_storage
546555
}
556+
557+
fn push_clip_path(
558+
&mut self,
559+
path: &BezPath,
560+
fill_rule: Fill,
561+
transform: Affine,
562+
aliasing_threshold: Option<u8>,
563+
) {
564+
self.flush_tasks();
565+
self.clip_context.push_clip(
566+
path,
567+
&mut self.strip_generator,
568+
fill_rule,
569+
transform,
570+
aliasing_threshold,
571+
);
572+
}
573+
574+
fn pop_clip_path(&mut self) {
575+
self.flush_tasks();
576+
self.clip_context.pop_clip();
577+
}
547578
}
548579

549580
simd_dispatch!(
@@ -596,6 +627,12 @@ impl Debug for MultiThreadedDispatcher {
596627
}
597628
}
598629

630+
#[derive(Debug)]
631+
pub(crate) struct OwnedClip {
632+
strips: Box<[Strip]>,
633+
alphas: Box<[u8]>,
634+
}
635+
599636
/// A structure that allows storing and fetching existing allocations.
600637
struct AllocationManager<T> {
601638
entries: Vec<Vec<T>>,
@@ -685,6 +722,7 @@ impl AllocationGroup {
685722
#[derive(Debug)]
686723
pub(crate) struct RenderTask {
687724
pub(crate) idx: u32,
725+
pub(crate) clip_path: Option<OwnedClip>,
688726
pub(crate) allocation_group: AllocationGroup,
689727
}
690728

sparse_strips/vello_cpu/src/dispatch/multi_threaded/worker.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use crate::dispatch::multi_threaded::{
66
CoarseTask, CoarseTaskSender, CoarseTaskType, RenderTask, RenderTaskType,
77
};
88
use std::vec::Vec;
9+
use vello_common::clip::PathDataRef;
910
use vello_common::strip_generator::{GenerationMode, StripGenerator, StripStorage};
1011

1112
#[derive(Debug)]
@@ -49,6 +50,10 @@ impl Worker {
4950
self.strip_storage
5051
.set_generation_mode(GenerationMode::Append);
5152
let task_idx = render_task.idx;
53+
let path_clip = render_task.clip_path.as_ref().map(|c| PathDataRef {
54+
strips: c.strips.as_ref(),
55+
alphas: c.alphas.as_ref(),
56+
});
5257

5358
for task in render_task
5459
.allocation_group
@@ -73,6 +78,7 @@ impl Worker {
7378
transform,
7479
aliasing_threshold,
7580
&mut self.strip_storage,
81+
path_clip,
7682
);
7783
let end = self.strip_storage.strips.len() as u32;
7884

@@ -104,6 +110,7 @@ impl Worker {
104110
transform,
105111
aliasing_threshold,
106112
&mut self.strip_storage,
113+
path_clip,
107114
);
108115
let end = self.strip_storage.strips.len() as u32;
109116

@@ -137,6 +144,7 @@ impl Worker {
137144
transform,
138145
aliasing_threshold,
139146
&mut self.strip_storage,
147+
path_clip,
140148
);
141149

142150
let end = self.strip_storage.strips.len() as u32;

0 commit comments

Comments
 (0)