Skip to main content

vello_cpu/dispatch/multi_threaded/
worker.rs

1// Copyright 2025 the Vello Authors
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3
4use crate::Level;
5use crate::dispatch::multi_threaded::{
6    RecordedCommand, RecordedCommandSender, RecordedCommandTask, RenderTask, RenderTaskType,
7};
8use std::vec::Vec;
9use vello_common::clip::PathDataRef;
10use vello_common::geometry::RectU16;
11use vello_common::strip_generator::{GenerationMode, StripGenerator, StripStorage};
12use vello_common::util::strip_bbox;
13
14#[derive(Debug)]
15pub(crate) struct Worker {
16    strip_generator: StripGenerator,
17    strip_storage: StripStorage,
18    thread_id: u8,
19}
20
21impl Worker {
22    pub(crate) fn new(width: u16, height: u16, thread_id: u8, level: Level) -> Self {
23        let strip_generator = StripGenerator::new(width, height, level);
24        let strip_storage = StripStorage::default();
25
26        Self {
27            strip_generator,
28            strip_storage,
29            thread_id,
30        }
31    }
32
33    pub(crate) fn init(&mut self, alphas: Vec<u8>) {
34        self.strip_storage.alphas = alphas;
35    }
36
37    pub(crate) fn thread_id(&self) -> u8 {
38        self.thread_id
39    }
40
41    pub(crate) fn reset(&mut self, width: u16, height: u16) {
42        self.strip_generator.reset(width, height);
43    }
44
45    pub(crate) fn run_render_task(
46        &mut self,
47        mut render_task: RenderTask,
48        result_sender: &mut RecordedCommandSender,
49    ) {
50        let num_tasks = render_task.allocation_group.render_tasks.len();
51        self.strip_storage.strips.clear();
52        self.strip_storage
53            .set_generation_mode(GenerationMode::Append);
54        let task_idx = render_task.idx;
55        let path_clip = render_task.clip_path.as_ref().map(|c| PathDataRef {
56            strips: c.strips.as_ref(),
57            alphas: c.alphas.as_ref(),
58            bbox: c.bbox,
59        });
60
61        for task in render_task
62            .allocation_group
63            .render_tasks
64            .drain(0..num_tasks)
65        {
66            match task {
67                RenderTaskType::FillPath {
68                    path_range,
69                    transform,
70                    paint,
71                    fill_rule,
72                    blend_mode,
73                    aliasing_threshold,
74                    mask,
75                } => {
76                    let start = self.strip_storage.strips.len() as u32;
77                    let path = &render_task.allocation_group.path
78                        [path_range.start as usize..path_range.end as usize];
79
80                    self.strip_generator.generate_filled_path(
81                        path.iter().copied(),
82                        fill_rule,
83                        transform,
84                        aliasing_threshold,
85                        &mut self.strip_storage,
86                        path_clip,
87                    );
88                    let end = self.strip_storage.strips.len() as u32;
89
90                    let recorded_command = RecordedCommand::RenderPath {
91                        thread_id: self.thread_id,
92                        strips: start..end,
93                        blend_mode,
94                        paint,
95                        mask,
96                    };
97
98                    render_task
99                        .allocation_group
100                        .recorded_commands
101                        .push(recorded_command);
102                }
103                RenderTaskType::StrokePath {
104                    path_range,
105                    transform,
106                    paint,
107                    blend_mode,
108                    stroke,
109                    aliasing_threshold,
110                    mask,
111                } => {
112                    let start = self.strip_storage.strips.len() as u32;
113                    let path = &render_task.allocation_group.path
114                        [path_range.start as usize..path_range.end as usize];
115
116                    self.strip_generator.generate_stroked_path(
117                        path.iter().copied(),
118                        &stroke,
119                        transform,
120                        aliasing_threshold,
121                        &mut self.strip_storage,
122                        path_clip,
123                    );
124                    let end = self.strip_storage.strips.len() as u32;
125
126                    let recorded_command = RecordedCommand::RenderPath {
127                        thread_id: self.thread_id,
128                        strips: start..end,
129                        blend_mode,
130                        paint,
131                        mask,
132                    };
133
134                    render_task
135                        .allocation_group
136                        .recorded_commands
137                        .push(recorded_command);
138                }
139                RenderTaskType::PushLayer {
140                    clip_path,
141                    blend_mode,
142                    opacity,
143                    mask,
144                    fill_rule,
145                    aliasing_threshold,
146                } => {
147                    let (clip, clip_bbox) = if let Some((path_range, transform)) = clip_path {
148                        let start = self.strip_storage.strips.len() as u32;
149                        let path = &render_task.allocation_group.path
150                            [path_range.start as usize..path_range.end as usize];
151
152                        self.strip_generator.generate_filled_path(
153                            path.iter().copied(),
154                            fill_rule,
155                            transform,
156                            aliasing_threshold,
157                            &mut self.strip_storage,
158                            path_clip,
159                        );
160
161                        let end = self.strip_storage.strips.len() as u32;
162                        let range = start..end;
163                        let bbox = strip_bbox(
164                            &self.strip_storage.strips[range.start as usize..range.end as usize],
165                        )
166                        .unwrap_or(RectU16::ZERO);
167
168                        (Some(range), Some(bbox))
169                    } else {
170                        (None, None)
171                    };
172
173                    let recorded_command = RecordedCommand::PushLayer {
174                        thread_id: self.thread_id,
175                        clip_path: clip,
176                        clip_bbox,
177                        blend_mode,
178                        mask,
179                        opacity,
180                    };
181
182                    render_task
183                        .allocation_group
184                        .recorded_commands
185                        .push(recorded_command);
186                }
187                RenderTaskType::PopLayer => {
188                    render_task
189                        .allocation_group
190                        .recorded_commands
191                        .push(RecordedCommand::PopLayer);
192                }
193            }
194        }
195
196        let taken_strips = std::mem::replace(
197            &mut self.strip_storage.strips,
198            render_task.allocation_group.strips,
199        );
200        render_task.allocation_group.strips = taken_strips;
201
202        let task = RecordedCommandTask {
203            allocation_group: render_task.allocation_group,
204        };
205
206        result_sender.send(task_idx as usize, task).unwrap();
207    }
208
209    pub(crate) fn finalize(&mut self) -> Vec<u8> {
210        std::mem::take(&mut self.strip_storage.alphas)
211    }
212}