Skip to main content

vello_cpu/coarse/
cmd.rs

1//! Render commands for a single row of strips.
2
3// Copyright 2026 the Vello Authors
4// SPDX-License-Identifier: Apache-2.0 OR MIT
5
6use crate::coarse::depth::BucketRange;
7use crate::peniko::BlendMode;
8use crate::util::Span;
9use core::num::NonZeroU32;
10use vello_common::mask::Mask;
11use vello_common::paint::Paint;
12
13// TODO: If we wanted to, we could likely reduce the memory footprint from 16 bytes to 8 bytes
14// by using 4 bits from a span (since tile size is 4, for `x` and `width` we can always store
15// the value divided by 4), and creating a bit-packed representation of all commands.
16
17/// A bucketed render command.
18#[derive(Debug, Clone, Copy)]
19pub(crate) enum RenderCmd {
20    /// See [`PaintFill`].
21    PaintFill(PaintFill),
22    /// Push a new temporary layer buffer.
23    PushBuf(Option<Span>),
24    /// Pop the last temporary layer buffer.
25    PopBuf,
26    /// See [`LayerFill`].
27    LayerFill(LayerFill),
28}
29
30/// Fill a span with the given paint and optionally some alpha coverage.
31#[derive(Debug, Clone, Copy)]
32pub(crate) struct PaintFill {
33    pub(crate) span: Span,
34    alpha_idx: Option<AlphaIdx>,
35    pub(crate) attrs_idx: u32,
36}
37
38impl PaintFill {
39    pub(crate) fn new(span: Span, alpha_idx: Option<u32>, attrs_idx: u32) -> Self {
40        Self {
41            span,
42            alpha_idx: alpha_idx.map(AlphaIdx::new),
43            attrs_idx,
44        }
45    }
46
47    pub(crate) fn alpha_idx(self) -> Option<u32> {
48        self.alpha_idx.map(AlphaIdx::get)
49    }
50}
51
52/// Fill a whole range of depth buckets with the given paint.
53#[derive(Debug, Clone, Copy)]
54pub(crate) struct DepthFill {
55    bucket_range: BucketRange,
56    pub(crate) attrs_idx: u32,
57}
58
59impl DepthFill {
60    pub(crate) fn new(bucket_range: BucketRange, attrs_idx: u32) -> Self {
61        Self {
62            bucket_range,
63            attrs_idx,
64        }
65    }
66
67    pub(crate) fn bucket_range(self) -> BucketRange {
68        self.bucket_range
69    }
70
71    pub(crate) fn span(self) -> Span {
72        self.bucket_range.span()
73    }
74}
75
76/// Composite a span from the current temporary layer buffer into the parent
77/// buffer and optionally apply some alpha coverage.
78#[derive(Debug, Clone, Copy)]
79pub(crate) struct LayerFill {
80    pub(crate) span: Span,
81    alpha_idx: Option<AlphaIdx>,
82    pub(crate) attrs_idx: u32,
83}
84
85impl LayerFill {
86    pub(crate) fn new(span: Span, alpha_idx: Option<u32>, attrs_idx: u32) -> Self {
87        Self {
88            span,
89            alpha_idx: alpha_idx.map(AlphaIdx::new),
90            attrs_idx,
91        }
92    }
93
94    pub(crate) fn alpha_idx(self) -> Option<u32> {
95        self.alpha_idx.map(AlphaIdx::get)
96    }
97}
98
99// We use `NonZeroU32` so that `Option<AlphaIdx>` still only needs 4 bytes.
100#[derive(Debug, Clone, Copy)]
101struct AlphaIdx(NonZeroU32);
102
103impl AlphaIdx {
104    fn new(alpha_idx: u32) -> Self {
105        Self(NonZeroU32::new(alpha_idx.checked_add(1).expect("alpha index overflow")).unwrap())
106    }
107
108    fn get(self) -> u32 {
109        self.0.get() - 1
110    }
111}
112
113#[doc(hidden)]
114#[derive(Debug, Clone)]
115pub struct PaintFillAttrs {
116    pub paint: Paint,
117    pub blend_mode: BlendMode,
118    pub mask: Option<Mask>,
119    pub draw_id: u32,
120    pub thread_idx: u8,
121    /// See the comment in `CommandBucketer::bucket_commands`.
122    pub origin: (u16, u16),
123}
124
125#[derive(Debug, Clone)]
126pub(crate) struct LayerFillAttrs {
127    pub(crate) blend_mode: BlendMode,
128    pub(crate) opacity: f32,
129    pub(crate) mask: Option<Mask>,
130    pub(crate) draw_id: u32,
131    /// In case there is any alpha associated with the layer command, this stores
132    /// the index of the thread that stores the alpha.
133    pub(crate) thread_idx: u8,
134}
135
136#[cfg(test)]
137mod tests {
138    use super::{AlphaIdx, RenderCmd};
139    use core::mem::{needs_drop, size_of};
140
141    #[test]
142    fn alpha_idx_size() {
143        assert_eq!(size_of::<Option<AlphaIdx>>(), 4);
144    }
145
146    #[test]
147    fn render_cmd_assertions() {
148        assert_eq!(size_of::<RenderCmd>(), 16);
149        assert!(!needs_drop::<RenderCmd>());
150    }
151}