vello_cpu/dispatch/
single_threaded.rs

1// Copyright 2025 the Vello Authors
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3
4use crate::RenderMode;
5use crate::dispatch::Dispatcher;
6use crate::fine::{F32Kernel, Fine, FineKernel, U8Kernel};
7use crate::kurbo::{Affine, BezPath, Stroke};
8use crate::peniko::{BlendMode, Fill};
9use crate::region::Regions;
10use alloc::vec::Vec;
11use vello_common::coarse::{MODE_CPU, Wide};
12use vello_common::encode::EncodedPaint;
13use vello_common::fearless_simd::{Level, Simd, simd_dispatch};
14use vello_common::mask::Mask;
15use vello_common::paint::Paint;
16use vello_common::strip_generator::StripGenerator;
17
18#[derive(Debug)]
19pub(crate) struct SingleThreadedDispatcher {
20    wide: Wide,
21    strip_generator: StripGenerator,
22    level: Level,
23}
24
25impl SingleThreadedDispatcher {
26    pub(crate) fn new(width: u16, height: u16, level: Level) -> Self {
27        let wide = Wide::<MODE_CPU>::new(width, height);
28        let strip_generator = StripGenerator::new(width, height, level);
29
30        Self {
31            wide,
32            strip_generator,
33            level,
34        }
35    }
36
37    fn rasterize_f32(
38        &self,
39        buffer: &mut [u8],
40        width: u16,
41        height: u16,
42        encoded_paints: &[EncodedPaint],
43    ) {
44        rasterize_with_f32_dispatch(self.level, self, buffer, width, height, encoded_paints);
45    }
46
47    fn rasterize_u8(
48        &self,
49        buffer: &mut [u8],
50        width: u16,
51        height: u16,
52        encoded_paints: &[EncodedPaint],
53    ) {
54        rasterize_with_u8_dispatch(self.level, self, buffer, width, height, encoded_paints);
55    }
56
57    fn rasterize_with<S: Simd, F: FineKernel<S>>(
58        &self,
59        simd: S,
60        buffer: &mut [u8],
61        width: u16,
62        height: u16,
63        encoded_paints: &[EncodedPaint],
64    ) {
65        let mut buffer = Regions::new(width, height, buffer);
66        let mut fine = Fine::<S, F>::new(simd);
67
68        buffer.update_regions(|region| {
69            let x = region.x;
70            let y = region.y;
71
72            let wtile = self.wide.get(x, y);
73            fine.set_coords(x, y);
74
75            fine.clear(wtile.bg);
76            for cmd in &wtile.cmds {
77                fine.run_cmd(cmd, self.strip_generator.alpha_buf(), encoded_paints);
78            }
79
80            fine.pack(region);
81        });
82    }
83}
84
85impl Dispatcher for SingleThreadedDispatcher {
86    fn wide(&self) -> &Wide {
87        &self.wide
88    }
89
90    fn wide_mut(&mut self) -> &mut Wide {
91        &mut self.wide
92    }
93
94    fn fill_path(
95        &mut self,
96        path: &BezPath,
97        fill_rule: Fill,
98        transform: Affine,
99        paint: Paint,
100        anti_alias: bool,
101    ) {
102        let wide = &mut self.wide;
103
104        let func = |strips| wide.generate(strips, fill_rule, paint, 0);
105        self.strip_generator
106            .generate_filled_path(path, fill_rule, transform, anti_alias, func);
107    }
108
109    fn stroke_path(
110        &mut self,
111        path: &BezPath,
112        stroke: &Stroke,
113        transform: Affine,
114        paint: Paint,
115        anti_alias: bool,
116    ) {
117        let wide = &mut self.wide;
118
119        let func = |strips| wide.generate(strips, Fill::NonZero, paint, 0);
120        self.strip_generator
121            .generate_stroked_path(path, stroke, transform, anti_alias, func);
122    }
123
124    fn alpha_buf(&self) -> &[u8] {
125        self.strip_generator.alpha_buf()
126    }
127
128    fn extend_alpha_buf(&mut self, alphas: &[u8]) {
129        self.strip_generator.extend_alpha_buf(alphas);
130    }
131
132    fn replace_alpha_buf(&mut self, alphas: Vec<u8>) -> Vec<u8> {
133        self.strip_generator.replace_alpha_buf(alphas)
134    }
135
136    fn set_alpha_buf(&mut self, alphas: Vec<u8>) {
137        self.strip_generator.set_alpha_buf(alphas);
138    }
139
140    fn push_layer(
141        &mut self,
142        clip_path: Option<&BezPath>,
143        fill_rule: Fill,
144        clip_transform: Affine,
145        blend_mode: BlendMode,
146        opacity: f32,
147        anti_alias: bool,
148        mask: Option<Mask>,
149    ) {
150        let clip = if let Some(c) = clip_path {
151            // This variable will always be assigned to in the closure, but the compiler can't recognize that.
152            // So just assign a dummy value here.
153            let mut strip_buf = &[][..];
154
155            self.strip_generator.generate_filled_path(
156                c,
157                fill_rule,
158                clip_transform,
159                anti_alias,
160                |strips| strip_buf = strips,
161            );
162
163            Some((strip_buf, fill_rule))
164        } else {
165            None
166        };
167
168        self.wide.push_layer(clip, blend_mode, mask, opacity, 0);
169    }
170
171    fn pop_layer(&mut self) {
172        self.wide.pop_layer();
173    }
174
175    fn reset(&mut self) {
176        self.wide.reset();
177        self.strip_generator.reset();
178    }
179
180    fn flush(&mut self) {}
181
182    fn rasterize(
183        &self,
184        buffer: &mut [u8],
185        render_mode: RenderMode,
186        width: u16,
187        height: u16,
188        encoded_paints: &[EncodedPaint],
189    ) {
190        match render_mode {
191            RenderMode::OptimizeSpeed => self.rasterize_u8(buffer, width, height, encoded_paints),
192            RenderMode::OptimizeQuality => {
193                self.rasterize_f32(buffer, width, height, encoded_paints);
194            }
195        }
196    }
197}
198
199simd_dispatch!(
200    pub rasterize_with_f32_dispatch(
201        level,
202        self_: &SingleThreadedDispatcher,
203        buffer: &mut [u8],
204        width: u16,
205        height: u16,
206        encoded_paints: &[EncodedPaint]
207    ) = rasterize_with_f32
208);
209
210simd_dispatch!(
211    pub rasterize_with_u8_dispatch(
212        level,
213        self_: &SingleThreadedDispatcher,
214        buffer: &mut [u8],
215        width: u16,
216        height: u16,
217        encoded_paints: &[EncodedPaint]
218    ) = rasterize_with_u8
219);
220
221fn rasterize_with_f32<S: Simd>(
222    simd: S,
223    self_: &SingleThreadedDispatcher,
224    buffer: &mut [u8],
225    width: u16,
226    height: u16,
227    encoded_paints: &[EncodedPaint],
228) {
229    self_.rasterize_with::<S, F32Kernel>(simd, buffer, width, height, encoded_paints);
230}
231
232fn rasterize_with_u8<S: Simd>(
233    simd: S,
234    self_: &SingleThreadedDispatcher,
235    buffer: &mut [u8],
236    width: u16,
237    height: u16,
238    encoded_paints: &[EncodedPaint],
239) {
240    self_.rasterize_with::<S, U8Kernel>(simd, buffer, width, height, encoded_paints);
241}