1use crate::coarse::CommandBucketer;
5use crate::coarse::depth::DepthBuffer;
6use crate::dispatch::Dispatcher;
7use crate::filter::context::FilterContext;
8use crate::fine::{Fine, FineKernel, FineRenderParams, FineResources, rasterize_region};
9use crate::kurbo::{Affine, BezPath, Rect, Stroke};
10use crate::peniko::{BlendMode, Fill};
11use crate::record::RecordedFill;
12use crate::region::Regions;
13use crate::{CompositeMode, RasterizerSettings};
14use core::cell::RefCell;
15use vello_common::encode::EncodedPaint;
16use vello_common::fearless_simd::{Level, Simd};
17use vello_common::filter::FilterData;
18use vello_common::geometry::RectU16;
19use vello_common::mask::Mask;
20use vello_common::paint::{ImageResolver, Paint};
21use vello_common::pixmap::{Pixmap, PixmapMut};
22use vello_common::record::{
23 CommandRecorder, LayerClip, LayerProps, Node, PoppedLayer, RecordedLayerKind,
24};
25use vello_common::strip_generator::{GenerationMode, StripStorage};
26use vello_common::util::strip_bbox;
27use vello_common::viewport::ViewportState;
28
29#[derive(Debug)]
31pub(crate) struct SingleThreadedDispatcher {
32 bucketer: RefCell<CommandBucketer>,
34 viewport: ViewportState,
36 recorder: CommandRecorder<RecordedFill>,
38 strip_storage: StripStorage,
40 level: Level,
42}
43
44impl SingleThreadedDispatcher {
45 pub(crate) fn new(width: u16, height: u16, level: Level) -> Self {
52 Self {
53 bucketer: RefCell::new(CommandBucketer::from_wh(width, height)),
54 viewport: ViewportState::new(width, height, level),
55 recorder: CommandRecorder::new(width, height),
56 strip_storage: StripStorage::new(GenerationMode::Append),
57 level,
58 }
59 }
60
61 #[cfg(feature = "f32_pipeline")]
66 fn rasterize_f32(
67 &self,
68 target: PixmapMut<'_>,
69 scene_width: u16,
70 scene_height: u16,
71 settings: RasterizerSettings,
72 encoded_paints: &[EncodedPaint],
73 image_resolver: &dyn ImageResolver,
74 ) {
75 use crate::fine::F32Kernel;
76 use vello_common::fearless_simd::dispatch;
77 dispatch!(self.level, simd => self.rasterize_with::<_, F32Kernel>(simd, target, scene_width, scene_height, settings, encoded_paints, image_resolver));
78 }
79
80 #[cfg(feature = "u8_pipeline")]
85 fn rasterize_u8(
86 &self,
87 target: PixmapMut<'_>,
88 scene_width: u16,
89 scene_height: u16,
90 settings: RasterizerSettings,
91 encoded_paints: &[EncodedPaint],
92 image_resolver: &dyn ImageResolver,
93 ) {
94 use crate::fine::U8Kernel;
95 use vello_common::fearless_simd::dispatch;
96 dispatch!(self.level, simd => self.rasterize_with::<_, U8Kernel>(simd, target, scene_width, scene_height, settings, encoded_paints, image_resolver));
97 }
98
99 fn rasterize_with<S: Simd, F: FineKernel<S>>(
102 &self,
103 simd: S,
104 target: PixmapMut<'_>,
105 scene_width: u16,
106 scene_height: u16,
107 settings: RasterizerSettings,
108 encoded_paints: &[EncodedPaint],
109 image_resolver: &dyn ImageResolver,
110 ) {
111 let filters = self.rasterize_filter_layers::<S, F>(simd, encoded_paints, image_resolver);
112 let use_src_over = settings.composite_mode == CompositeMode::SrcOver;
113 let params = FineRenderParams {
114 scene_size: (scene_width, scene_height),
115 target_offset: settings.offset,
116 };
117
118 self.bucket_and_rasterize::<S, F>(
119 simd,
120 &self.recorder.nodes,
121 RectU16::new(0, 0, scene_width, scene_height),
122 &filters,
123 target,
124 params,
125 use_src_over,
126 encoded_paints,
127 image_resolver,
128 );
129 }
130
131 fn bucket_and_rasterize<S: Simd, F: FineKernel<S>>(
132 &self,
133 simd: S,
134 cmds: &[Node],
135 viewport: RectU16,
136 filter_ctx: &FilterContext,
137 mut target: PixmapMut<'_>,
138 params: FineRenderParams,
139 use_src_over: bool,
140 encoded_paints: &[EncodedPaint],
141 image_resolver: &dyn ImageResolver,
142 ) {
143 let mut bucketer = self.bucketer.borrow_mut();
144 bucketer.reset(viewport);
145 bucketer.bucket_commands(
146 cmds,
147 &self.recorder.draws,
148 &self.recorder.layers,
149 &self.strip_storage.strips,
150 encoded_paints,
151 filter_ctx,
152 );
153
154 let alpha_buffers = &[self.strip_storage.alphas.as_slice()];
155 let resources = FineResources {
156 alpha_buffers,
157 encoded_paints,
158 filter_paints: &bucketer.filter_paints,
159 image_resolver,
160 };
161 let mut regions = Regions::new(
162 &mut target,
163 params.scene_size,
164 params.target_offset,
165 bucketer.rows().len(),
166 );
167 Self::rasterize_target::<S, F>(simd, &bucketer, resources, &mut regions, use_src_over);
168 }
169
170 fn rasterize_target<S: Simd, F: FineKernel<S>>(
171 simd: S,
172 bucketer: &CommandBucketer,
173 resources: FineResources<'_>,
174 regions: &mut Regions<'_>,
175 use_src_over: bool,
176 ) {
177 let mut fine = Fine::<S, F>::new(simd, bucketer.width());
179 let mut depth = DepthBuffer::new(bucketer.width());
180 regions.update(|region| {
181 rasterize_region::<S, F>(
182 &mut fine,
183 &mut depth,
184 region,
185 bucketer,
186 resources,
187 use_src_over,
188 );
189 });
190 }
191
192 fn record_fill(
193 &mut self,
194 strip_start: usize,
195 paint: Paint,
196 blend_mode: BlendMode,
197 mask: Option<Mask>,
198 ) {
199 let strip_end = self.strip_storage.strips.len();
200 let strip_range = strip_start..strip_end;
201 let strips = &self.strip_storage.strips[strip_range.start..strip_range.end];
202 let draw = RecordedFill::new(0, strip_range, paint, blend_mode, mask);
203
204 self.recorder.push_draw(draw, strips);
205 }
206
207 fn rasterize_filter_layers<S: Simd, F: FineKernel<S>>(
208 &self,
209 simd: S,
210 encoded_paints: &[EncodedPaint],
211 image_resolver: &dyn ImageResolver,
212 ) -> FilterContext {
213 let mut filter_ctx = FilterContext::new(self.recorder.layers.len());
215 for id in self.recorder.filter_layers.iter().rev().copied() {
220 let RecordedLayerKind::Filter {
221 filter_data: filter_plan,
222 placement,
223 } = &self.recorder.layers[id as usize].kind
224 else {
225 unreachable!("filter_layers only contains filter layers");
226 };
227 let pixmap_bbox = placement.pixmap_bbox;
228 if pixmap_bbox.is_empty() {
229 continue;
230 }
231
232 let width = pixmap_bbox.width();
233 let height = pixmap_bbox.height();
234 let mut pixmap = Pixmap::new(width, height);
237 let params = FineRenderParams {
238 scene_size: (width, height),
239 target_offset: (0, 0),
240 };
241
242 self.bucket_and_rasterize::<S, F>(
243 simd,
244 &self.recorder.layers[id as usize].nodes,
245 pixmap_bbox,
246 &filter_ctx,
247 (&mut pixmap).into(),
248 params,
249 false,
250 encoded_paints,
251 image_resolver,
252 );
253
254 F::filter_layer(
255 &mut pixmap,
256 &filter_plan.filter,
257 filter_ctx.scratch(),
258 filter_plan.transform,
259 );
260
261 filter_ctx.set_layer(id as usize, pixmap);
266 }
267
268 filter_ctx
269 }
270}
271
272impl Dispatcher for SingleThreadedDispatcher {
273 fn has_layers(&self) -> bool {
274 self.recorder.has_layers()
275 }
276
277 fn fill_path(
278 &mut self,
279 path: &BezPath,
280 fill_rule: Fill,
281 transform: Affine,
282 paint: Paint,
283 blend_mode: BlendMode,
284 aliasing_threshold: Option<u8>,
285 mask: Option<Mask>,
286 ) {
287 let strip_start = self.strip_storage.strips.len();
288 let strip_storage = &mut self.strip_storage;
289 self.viewport
290 .with_generator_and_clip(|strip_generator, clip_path| {
291 strip_generator.generate_filled_path(
292 path,
293 fill_rule,
294 transform,
295 aliasing_threshold,
296 strip_storage,
297 clip_path,
298 );
299 });
300 self.record_fill(strip_start, paint, blend_mode, mask);
301 }
302
303 fn stroke_path(
304 &mut self,
305 path: &BezPath,
306 stroke: &Stroke,
307 transform: Affine,
308 paint: Paint,
309 blend_mode: BlendMode,
310 aliasing_threshold: Option<u8>,
311 mask: Option<Mask>,
312 ) {
313 let strip_start = self.strip_storage.strips.len();
314 let strip_storage = &mut self.strip_storage;
315 self.viewport
316 .with_generator_and_clip(|strip_generator, clip_path| {
317 strip_generator.generate_stroked_path(
318 path,
319 stroke,
320 transform,
321 aliasing_threshold,
322 strip_storage,
323 clip_path,
324 );
325 });
326 self.record_fill(strip_start, paint, blend_mode, mask);
327 }
328
329 fn fill_rect_fast(
330 &mut self,
331 rect: &Rect,
332 paint: Paint,
333 blend_mode: BlendMode,
334 mask: Option<Mask>,
335 ) {
336 let strip_start = self.strip_storage.strips.len();
337 let strip_storage = &mut self.strip_storage;
338 self.viewport
339 .with_generator_and_clip(|strip_generator, clip_path| {
340 strip_generator.generate_filled_rect_fast(rect, strip_storage, clip_path);
341 });
342 self.record_fill(strip_start, paint, blend_mode, mask);
343 }
344
345 fn push_layer(
346 &mut self,
347 clip_path: Option<&BezPath>,
348 fill_rule: Fill,
349 clip_transform: Affine,
350 blend_mode: BlendMode,
351 opacity: f32,
352 aliasing_threshold: Option<u8>,
353 mask: Option<Mask>,
354 filter_data: Option<FilterData>,
355 ) {
356 if let Some(filter_data) = &filter_data {
357 self.viewport.push_root_viewport(filter_data);
358 }
359
360 let clip_path = clip_path.map(|clip_path| {
361 let strip_start = self.strip_storage.strips.len();
362 let strip_storage = &mut self.strip_storage;
363 self.viewport
364 .with_generator_and_clip(|strip_generator, existing_clip| {
365 strip_generator.generate_filled_path(
366 clip_path,
367 fill_rule,
368 clip_transform,
369 aliasing_threshold,
370 strip_storage,
371 existing_clip,
372 );
373
374 let strip_range = strip_start..strip_storage.strips.len();
375 LayerClip {
376 bbox: strip_bbox(&strip_storage.strips[strip_range.clone()])
377 .unwrap_or(RectU16::ZERO),
378 strip_range,
379 thread_idx: 0,
380 }
381 })
382 });
383
384 self.recorder.push_layer(
385 LayerProps {
386 blend_mode,
387 opacity,
388 mask,
389 clip_path,
390 },
391 filter_data,
392 );
393 }
394
395 fn pop_layer(&mut self) {
396 match self.recorder.pop_layer() {
397 PoppedLayer::Regular => {}
398 PoppedLayer::Filter => {
399 self.viewport.pop_root_viewport();
400 }
401 }
402 }
403
404 fn reset(&mut self, width: u16, height: u16) {
405 self.recorder.reset(width, height);
407 self.strip_storage.clear();
408 self.viewport.reset(width, height);
409 }
410
411 fn flush(&mut self) {
412 }
414
415 fn rasterize(
416 &self,
417 target: PixmapMut<'_>,
418 scene_width: u16,
419 scene_height: u16,
420 settings: RasterizerSettings,
421 encoded_paints: &[EncodedPaint],
422 image_resolver: &dyn ImageResolver,
423 ) {
424 #[cfg(all(feature = "u8_pipeline", not(feature = "f32_pipeline")))]
426 {
427 self.rasterize_u8(
428 target,
429 scene_width,
430 scene_height,
431 settings,
432 encoded_paints,
433 image_resolver,
434 );
435 }
436
437 #[cfg(all(feature = "f32_pipeline", not(feature = "u8_pipeline")))]
439 {
440 self.rasterize_f32(
441 target,
442 scene_width,
443 scene_height,
444 settings,
445 encoded_paints,
446 image_resolver,
447 );
448 }
449
450 #[cfg(all(feature = "u8_pipeline", feature = "f32_pipeline"))]
452 match settings.render_mode {
453 crate::RenderMode::OptimizeSpeed => {
454 self.rasterize_u8(
456 target,
457 scene_width,
458 scene_height,
459 settings,
460 encoded_paints,
461 image_resolver,
462 );
463 }
464 crate::RenderMode::OptimizeQuality => {
465 self.rasterize_f32(
467 target,
468 scene_width,
469 scene_height,
470 settings,
471 encoded_paints,
472 image_resolver,
473 );
474 }
475 }
476
477 #[cfg(all(not(feature = "u8_pipeline"), not(feature = "f32_pipeline")))]
478 {
479 let _ = (
482 target,
483 scene_width,
484 scene_height,
485 settings,
486 encoded_paints,
487 image_resolver,
488 );
489 }
490 }
491
492 fn push_clip_path(
493 &mut self,
494 path: &BezPath,
495 fill_rule: Fill,
496 transform: Affine,
497 aliasing_threshold: Option<u8>,
498 ) {
499 self.viewport
500 .push_clip(path, fill_rule, transform, aliasing_threshold);
501 }
502
503 fn pop_clip_path(&mut self) {
504 self.viewport.pop_clip();
505 }
506
507 fn is_multi_threaded(&self) -> bool {
508 false
509 }
510}
511
512#[allow(
515 dead_code,
516 reason = "useful debug utility, can be enabled by uncommenting the call site"
517)]
518#[cfg(all(debug_assertions, feature = "std", feature = "png"))]
519fn save_filtered_layer_debug(pixmap: &Pixmap, layer_id: usize) {
520 use std::path::PathBuf;
521
522 let diffs_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../vello_sparse_tests/diffs");
523 let _ = std::fs::create_dir_all(&diffs_path);
524 let filename = diffs_path.join(alloc::format!("filtered_layer_{layer_id}.png"));
525
526 if let Ok(png_data) = pixmap.clone().into_png() {
527 let _ = std::fs::write(&filename, &png_data);
528 }
529}
530
531#[cfg(test)]
532mod tests {
533 use super::*;
534 use crate::kurbo::Shape;
535 use vello_common::color::palette::css::BLUE;
536 use vello_common::paint::PremulColor;
537
538 #[test]
543 fn buffers_cleared_on_reset() {
544 let mut dispatcher = SingleThreadedDispatcher::new(100, 100, Level::new());
545
546 dispatcher.fill_path(
548 &Rect::new(0.0, 0.0, 50.0, 50.0).to_path(0.1),
549 Fill::NonZero,
550 Affine::IDENTITY,
551 Paint::Solid(PremulColor::from_alpha_color(BLUE)),
552 BlendMode::default(),
553 None,
554 None,
555 );
556
557 assert!(!dispatcher.strip_storage.strips.is_empty());
559 assert!(!dispatcher.recorder.nodes.is_empty());
560
561 dispatcher.reset(100, 100);
562
563 assert!(dispatcher.strip_storage.strips.is_empty());
565 assert!(dispatcher.strip_storage.alphas.is_empty());
566 assert!(dispatcher.recorder.nodes.is_empty());
567 assert!(dispatcher.recorder.layers.is_empty());
568 assert!(!dispatcher.viewport.has_root_viewports());
569 }
570}