vello_common/
recording.rs

1// Copyright 2025 the Vello Authors
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3
4//! Recording API for caching sparse strips
5
6#[cfg(feature = "text")]
7use crate::glyph::{GlyphRenderer, GlyphRunBuilder, GlyphType, PreparedGlyph};
8use crate::kurbo::{Affine, BezPath, Rect, Stroke};
9use crate::mask::Mask;
10use crate::paint::PaintType;
11#[cfg(feature = "text")]
12use crate::peniko::FontData;
13use crate::peniko::{BlendMode, Fill};
14use crate::strip::Strip;
15use crate::strip_generator::StripStorage;
16use alloc::vec::Vec;
17
18/// Cached sparse strip data.
19#[derive(Debug, Default)]
20pub struct CachedStrips {
21    /// The strip storage.
22    strip_storage: StripStorage,
23    /// Strip start indices for each geometry command.
24    strip_start_indices: Vec<usize>,
25}
26
27impl CachedStrips {
28    /// Create a new cached strips instance.
29    pub fn new(strip_storage: StripStorage, strip_start_indices: Vec<usize>) -> Self {
30        Self {
31            strip_storage,
32            strip_start_indices,
33        }
34    }
35
36    /// Clear the contents.
37    pub fn clear(&mut self) {
38        self.strip_storage.clear();
39        self.strip_start_indices.clear();
40    }
41
42    /// Check if this cached strips is empty.
43    pub fn is_empty(&self) -> bool {
44        self.strip_storage.is_empty() && self.strip_start_indices.is_empty()
45    }
46
47    /// Get the number of strips.
48    pub fn strip_count(&self) -> usize {
49        self.strip_storage.strips.len()
50    }
51
52    /// Get the number of alpha bytes.
53    pub fn alpha_count(&self) -> usize {
54        self.strip_storage.alphas.len()
55    }
56
57    /// Get strips as slice.
58    pub fn strips(&self) -> &[Strip] {
59        &self.strip_storage.strips
60    }
61
62    /// Get alphas as slice
63    pub fn alphas(&self) -> &[u8] {
64        &self.strip_storage.alphas
65    }
66
67    /// Get strip start indices.
68    pub fn strip_start_indices(&self) -> &[usize] {
69        &self.strip_start_indices
70    }
71
72    /// Takes ownership of all buffers.
73    pub fn take(&mut self) -> (StripStorage, Vec<usize>) {
74        let strip_storage = core::mem::take(&mut self.strip_storage);
75        let strip_start_indices = core::mem::take(&mut self.strip_start_indices);
76        (strip_storage, strip_start_indices)
77    }
78}
79
80/// A recording of rendering commands that can cache generated strips.
81#[derive(Debug)]
82pub struct Recording {
83    /// Recorded commands.
84    commands: Vec<RenderCommand>,
85    /// Cached sparse strips.
86    cached_strips: CachedStrips,
87    /// Track the transform of the underlying rasterization context.
88    transform: Affine,
89}
90
91/// Command for pushing a new layer.
92#[derive(Debug, Clone)]
93pub struct PushLayerCommand {
94    /// Clip path.
95    pub clip_path: Option<BezPath>,
96    /// Blend mode.
97    pub blend_mode: Option<BlendMode>,
98    /// Opacity.
99    pub opacity: Option<f32>,
100    /// Mask.
101    pub mask: Option<Mask>,
102}
103
104/// Individual rendering commands that can be recorded.
105#[derive(Debug)]
106pub enum RenderCommand {
107    /// Fill a path.
108    FillPath(BezPath),
109    /// Stroke a path.
110    StrokePath(BezPath),
111    /// Fill a rectangle.
112    FillRect(Rect),
113    /// Stroke a rectangle.
114    StrokeRect(Rect),
115    /// Set the current transform.
116    SetTransform(Affine),
117    /// Set the fill rule.
118    SetFillRule(Fill),
119    /// Set the stroke parameters.
120    SetStroke(Stroke),
121    /// Push a new layer with optional clipping and effects.
122    PushLayer(PushLayerCommand),
123    /// Pop the current layer.
124    PopLayer,
125    /// Set the current paint.
126    SetPaint(PaintType),
127    /// Set the paint transform.
128    SetPaintTransform(Affine),
129    /// Reset the paint transform.
130    ResetPaintTransform,
131    /// Render a fill outline glyph.
132    #[cfg(feature = "text")]
133    FillOutlineGlyph((BezPath, Affine)),
134    /// Render a stroke outline glyph.
135    #[cfg(feature = "text")]
136    StrokeOutlineGlyph((BezPath, Affine)),
137}
138
139impl Recording {
140    /// Create a new empty recording.
141    pub fn new() -> Self {
142        Self {
143            commands: Vec::new(),
144            cached_strips: CachedStrips::default(),
145            transform: Affine::IDENTITY,
146        }
147    }
148
149    /// Set the transform.
150    pub(crate) fn set_transform(&mut self, transform: Affine) {
151        self.transform = transform;
152    }
153
154    /// Get commands as a slice.
155    pub fn commands(&self) -> &[RenderCommand] {
156        &self.commands
157    }
158
159    /// Get the number of commands.
160    pub fn command_count(&self) -> usize {
161        self.commands.len()
162    }
163
164    /// Check if recording has cached strips.
165    pub fn has_cached_strips(&self) -> bool {
166        !self.cached_strips.is_empty()
167    }
168
169    /// Get the number of cached strips.
170    pub fn strip_count(&self) -> usize {
171        self.cached_strips.strip_count()
172    }
173
174    /// Get the number of cached alpha bytes.
175    pub fn alpha_count(&self) -> usize {
176        self.cached_strips.alpha_count()
177    }
178
179    /// Get cached strips.
180    pub fn get_cached_strips(&self) -> (&[Strip], &[u8]) {
181        (self.cached_strips.strips(), self.cached_strips.alphas())
182    }
183
184    /// Takes cached strip buffers.
185    pub fn take_cached_strips(&mut self) -> (StripStorage, Vec<usize>) {
186        self.cached_strips.take()
187    }
188
189    /// Get strip start indices.
190    pub fn get_strip_start_indices(&self) -> &[usize] {
191        self.cached_strips.strip_start_indices()
192    }
193
194    /// Clear the recording contents.
195    pub fn clear(&mut self) {
196        self.commands.clear();
197        self.cached_strips.clear();
198        self.transform = Affine::IDENTITY;
199    }
200
201    /// Add a command to the recording.
202    pub(crate) fn add_command(&mut self, command: RenderCommand) {
203        self.commands.push(command);
204    }
205
206    /// Set cached strips.
207    pub fn set_cached_strips(
208        &mut self,
209        strip_storage: StripStorage,
210        strip_start_indices: Vec<usize>,
211    ) {
212        self.cached_strips = CachedStrips::new(strip_storage, strip_start_indices);
213    }
214}
215
216impl Default for Recording {
217    fn default() -> Self {
218        Self::new()
219    }
220}
221
222/// Trait for rendering contexts that support recording and replaying operations.
223///
224/// # State Modification During Replay
225///
226/// **Important:** When replaying recordings using methods like `execute_recording()`,
227/// the renderer's state (transform, paint, fill rule, stroke settings, etc.) will be
228/// modified to match the state changes captured in the recording. The renderer will
229/// be left in the final state after all commands have been executed.
230///
231/// # Multithreading Limitation
232///
233/// **Note:** Recording and replay functionality is not currently implemented for
234/// `vello_cpu` when multithreading is enabled. This limitation only affects
235/// `vello_cpu` in multithreaded mode; single-threaded `vello_cpu` and `vello_hybrid`
236/// work correctly with recordings.
237///
238/// # Usage Pattern
239///
240/// A consumer needs to do the following to render:
241/// ```ignore
242/// let mut recording = Recording::new();
243/// scene.record(&mut recording, |ctx| { ... });
244/// scene.prepare_recording(&mut recording);
245/// scene.execute_recording(&recording);
246/// ```
247///
248/// Or to prepare for later rendering:
249/// ```ignore
250/// let mut recording = Recording::new();
251/// scene.record(&mut recording, |ctx| { ... });
252/// scene.prepare_recording(&mut recording);
253///
254/// // sometime later
255/// scene.execute_recording(&recording);
256/// ```
257pub trait Recordable {
258    /// Record rendering commands into a recording.
259    ///
260    /// This method allows you to capture a sequence of rendering operations
261    /// in a `Recording` that can be cached and replayed later.
262    ///
263    /// # Example
264    /// ```ignore
265    /// let mut recording = Recording::new();
266    /// scene.record(&mut recording, |ctx| {
267    ///     ctx.fill_rect(&Rect::new(0.0, 0.0, 100.0, 100.0));
268    ///     ctx.set_paint(Color::RED);
269    ///     ctx.stroke_path(&some_path);
270    /// });
271    /// ```
272    fn record<F>(&mut self, recording: &mut Recording, f: F)
273    where
274        F: FnOnce(&mut Recorder<'_>);
275
276    /// Generate sparse strips for a recording.
277    ///
278    /// This method processes the recorded commands and generates cached sparse strips
279    /// without executing the rendering. This allows you to pre-generate strips for
280    /// better control over when the expensive computation happens.
281    ///
282    /// # Example
283    /// ```ignore
284    /// let mut recording = Recording::new();
285    /// scene.record(&mut recording, |ctx| {
286    ///     ctx.fill_rect(&Rect::new(0.0, 0.0, 100.0, 100.0));
287    /// });
288    ///
289    /// // Generate strips explicitly
290    /// scene.prepare_recording(&mut recording);
291    /// ```
292    fn prepare_recording(&mut self, recording: &mut Recording);
293
294    /// Execute a recording directly without preparation.
295    ///
296    /// This method executes the rendering commands from a recording, using any
297    /// cached sparse strips that have been previously generated. If the recording
298    /// has not been prepared (no cached strips), this will result in empty rendering.
299    ///
300    /// Use this method when you have a recording that has already been prepared
301    /// via `prepare_recording()`, or when you want to execute commands immediately
302    /// without explicit preparation.
303    ///
304    /// # Example
305    /// ```ignore
306    /// let mut recording = Recording::new();
307    /// scene.record(&mut recording, |ctx| {
308    ///     ctx.fill_rect(&Rect::new(0.0, 0.0, 100.0, 100.0));
309    /// });
310    ///
311    /// // Prepare strips first
312    /// scene.prepare_recording(&mut recording);
313    ///
314    /// // Then execute with cached strips
315    /// scene.execute_recording(&recording);
316    /// ```
317    fn execute_recording(&mut self, recording: &Recording);
318}
319
320/// Recorder context that captures commands.
321#[derive(Debug)]
322pub struct Recorder<'a> {
323    /// The recording to capture commands into.
324    recording: &'a mut Recording,
325
326    #[cfg(feature = "text")]
327    glyph_caches: Option<crate::glyph::GlyphCaches>,
328}
329
330impl<'a> Recorder<'a> {
331    /// Create a new recorder for the given recording.
332    pub fn new(
333        recording: &'a mut Recording,
334        transform: Affine,
335        #[cfg(feature = "text")] glyph_caches: crate::glyph::GlyphCaches,
336    ) -> Self {
337        let mut s = Self {
338            recording,
339            #[cfg(feature = "text")]
340            glyph_caches: Some(glyph_caches),
341        };
342        // Ensure that the initial transform is saved on the recording.
343        s.set_transform(transform);
344        s
345    }
346
347    /// Fill a path with current paint and fill rule.
348    pub fn fill_path(&mut self, path: &BezPath) {
349        self.recording
350            .add_command(RenderCommand::FillPath(path.clone()));
351    }
352
353    /// Stroke a path with current paint and stroke settings.
354    pub fn stroke_path(&mut self, path: &BezPath) {
355        self.recording
356            .add_command(RenderCommand::StrokePath(path.clone()));
357    }
358
359    /// Fill a rectangle with current paint and fill rule.
360    pub fn fill_rect(&mut self, rect: &Rect) {
361        self.recording.add_command(RenderCommand::FillRect(*rect));
362    }
363
364    /// Stroke a rectangle with current paint and stroke settings.
365    pub fn stroke_rect(&mut self, rect: &Rect) {
366        self.recording.add_command(RenderCommand::StrokeRect(*rect));
367    }
368
369    /// Set the transform for subsequent operations.
370    pub fn set_transform(&mut self, transform: Affine) {
371        self.recording.set_transform(transform);
372        self.recording
373            .add_command(RenderCommand::SetTransform(transform));
374    }
375
376    /// Set the fill rule for subsequent fill operations.
377    pub fn set_fill_rule(&mut self, fill_rule: Fill) {
378        self.recording
379            .add_command(RenderCommand::SetFillRule(fill_rule));
380    }
381
382    /// Set the stroke settings for subsequent stroke operations.
383    pub fn set_stroke(&mut self, stroke: Stroke) {
384        self.recording.add_command(RenderCommand::SetStroke(stroke));
385    }
386
387    /// Set the paint for subsequent rendering operations.
388    pub fn set_paint(&mut self, paint: impl Into<PaintType>) {
389        self.recording
390            .add_command(RenderCommand::SetPaint(paint.into()));
391    }
392
393    /// Set the current paint transform.
394    pub fn set_paint_transform(&mut self, paint_transform: Affine) {
395        self.recording
396            .add_command(RenderCommand::SetPaintTransform(paint_transform));
397    }
398
399    /// Reset the current paint transform.
400    pub fn reset_paint_transform(&mut self) {
401        self.recording
402            .add_command(RenderCommand::ResetPaintTransform);
403    }
404
405    /// Push a new layer with the given properties.
406    pub fn push_layer(
407        &mut self,
408        clip_path: Option<&BezPath>,
409        blend_mode: Option<BlendMode>,
410        opacity: Option<f32>,
411        mask: Option<Mask>,
412    ) {
413        self.recording
414            .add_command(RenderCommand::PushLayer(PushLayerCommand {
415                clip_path: clip_path.cloned(),
416                blend_mode,
417                opacity,
418                mask,
419            }));
420    }
421
422    /// Push a new clip layer.
423    pub fn push_clip_layer(&mut self, clip_path: &BezPath) {
424        self.push_layer(Some(clip_path), None, None, None);
425    }
426
427    /// Pop the last pushed layer.
428    pub fn pop_layer(&mut self) {
429        self.recording.add_command(RenderCommand::PopLayer);
430    }
431
432    /// Creates a builder for drawing a run of glyphs that have the same attributes.
433    #[cfg(feature = "text")]
434    pub fn glyph_run(&mut self, font: &FontData) -> GlyphRunBuilder<'_, Self> {
435        GlyphRunBuilder::new(font.clone(), self.recording.transform, self)
436    }
437}
438
439#[cfg(feature = "text")]
440impl<'a> GlyphRenderer for Recorder<'a> {
441    fn fill_glyph(&mut self, glyph: PreparedGlyph<'_>) {
442        match glyph.glyph_type {
443            GlyphType::Outline(outline_glyph) => {
444                if !outline_glyph.path.is_empty() {
445                    self.recording.add_command(RenderCommand::FillOutlineGlyph((
446                        outline_glyph.path.clone(),
447                        glyph.transform,
448                    )));
449                }
450            }
451
452            _ => {
453                unimplemented!("Recording glyphs of type {:?}", glyph.glyph_type);
454            }
455        }
456    }
457
458    fn stroke_glyph(&mut self, glyph: PreparedGlyph<'_>) {
459        match glyph.glyph_type {
460            GlyphType::Outline(outline_glyph) => {
461                if !outline_glyph.path.is_empty() {
462                    self.recording
463                        .add_command(RenderCommand::StrokeOutlineGlyph((
464                            outline_glyph.path.clone(),
465                            glyph.transform,
466                        )));
467                }
468            }
469            _ => {
470                unimplemented!("Recording glyphs of type {:?}", glyph.glyph_type);
471            }
472        }
473    }
474
475    fn restore_glyph_caches(&mut self, caches: crate::glyph::GlyphCaches) {
476        self.glyph_caches = Some(caches);
477    }
478    fn take_glyph_caches(&mut self) -> crate::glyph::GlyphCaches {
479        self.glyph_caches.take().unwrap_or_default()
480    }
481}