| //! Render commands for a single row of strips. | |
| // Copyright 2026 the Vello Authors | |
| // SPDX-License-Identifier: Apache-2.0 OR MIT | |
| use crate::coarse::depth::BucketRange; | |
| use crate::peniko::BlendMode; | |
| use crate::util::Span; | |
| use core::num::NonZeroU32; | |
| use vello_common::mask::Mask; | |
| use vello_common::paint::Paint; | |
| // TODO: If we wanted to, we could likely reduce the memory footprint from 16 bytes to 8 bytes | |
| // by using 4 bits from a span (since tile size is 4, for `x` and `width` we can always store | |
| // the value divided by 4), and creating a bit-packed representation of all commands. | |
| /// A bucketed render command. | |
| #[derive(Debug, Clone, Copy)] | |
| pub(crate) enum RenderCmd { | |
| /// See [`PaintFill`]. | |
| PaintFill(PaintFill), | |
| /// Push a new temporary layer buffer. | |
| PushBuf(Option<Span>), | |
| /// Pop the last temporary layer buffer. | |
| PopBuf, | |
| /// See [`LayerFill`]. | |
| LayerFill(LayerFill), | |
| } | |
| /// Fill a span with the given paint and optionally some alpha coverage. | |
| #[derive(Debug, Clone, Copy)] | |
| pub(crate) struct PaintFill { | |
| pub(crate) span: Span, | |
| alpha_idx: Option<AlphaIdx>, | |
| pub(crate) attrs_idx: u32, | |
| } | |
| impl PaintFill { | |
| pub(crate) fn new(span: Span, alpha_idx: Option<u32>, attrs_idx: u32) -> Self { | |
| Self { | |
| span, | |
| alpha_idx: alpha_idx.map(AlphaIdx::new), | |
| attrs_idx, | |
| } | |
| } | |
| pub(crate) fn alpha_idx(self) -> Option<u32> { | |
| self.alpha_idx.map(AlphaIdx::get) | |
| } | |
| } | |
| /// Fill a whole range of depth buckets with the given paint. | |
| #[derive(Debug, Clone, Copy)] | |
| pub(crate) struct DepthFill { | |
| bucket_range: BucketRange, | |
| pub(crate) attrs_idx: u32, | |
| } | |
| impl DepthFill { | |
| pub(crate) fn new(bucket_range: BucketRange, attrs_idx: u32) -> Self { | |
| Self { | |
| bucket_range, | |
| attrs_idx, | |
| } | |
| } | |
| pub(crate) fn bucket_range(self) -> BucketRange { | |
| self.bucket_range | |
| } | |
| pub(crate) fn span(self) -> Span { | |
| self.bucket_range.span() | |
| } | |
| } | |
| /// Composite a span from the current temporary layer buffer into the parent | |
| /// buffer and optionally apply some alpha coverage. | |
| #[derive(Debug, Clone, Copy)] | |
| pub(crate) struct LayerFill { | |
| pub(crate) span: Span, | |
| alpha_idx: Option<AlphaIdx>, | |
| pub(crate) attrs_idx: u32, | |
| } | |
| impl LayerFill { | |
| pub(crate) fn new(span: Span, alpha_idx: Option<u32>, attrs_idx: u32) -> Self { | |
| Self { | |
| span, | |
| alpha_idx: alpha_idx.map(AlphaIdx::new), | |
| attrs_idx, | |
| } | |
| } | |
| pub(crate) fn alpha_idx(self) -> Option<u32> { | |
| self.alpha_idx.map(AlphaIdx::get) | |
| } | |
| } | |
| // We use `NonZeroU32` so that `Option<AlphaIdx>` still only needs 4 bytes. | |
| #[derive(Debug, Clone, Copy)] | |
| struct AlphaIdx(NonZeroU32); | |
| impl AlphaIdx { | |
| fn new(alpha_idx: u32) -> Self { | |
| Self(NonZeroU32::new(alpha_idx.checked_add(1).expect("alpha index overflow")).unwrap()) | |
| } | |
| fn get(self) -> u32 { | |
| self.0.get() - 1 | |
| } | |
| } | |
| #[doc(hidden)] | |
| #[derive(Debug, Clone)] | |
| pub struct PaintFillAttrs { | |
| pub paint: Paint, | |
| pub blend_mode: BlendMode, | |
| pub mask: Option<Mask>, | |
| pub draw_id: u32, | |
| pub thread_idx: u8, | |
| /// See the comment in `CommandBucketer::bucket_commands`. | |
| pub origin: (u16, u16), | |
| } | |
| #[derive(Debug, Clone)] | |
| pub(crate) struct LayerFillAttrs { | |
| pub(crate) blend_mode: BlendMode, | |
| pub(crate) opacity: f32, | |
| pub(crate) mask: Option<Mask>, | |
| pub(crate) draw_id: u32, | |
| /// In case there is any alpha associated with the layer command, this stores | |
| /// the index of the thread that stores the alpha. | |
| pub(crate) thread_idx: u8, | |
| } | |
| #[cfg(test)] | |
| mod tests { | |
| use super::{AlphaIdx, RenderCmd}; | |
| use core::mem::{needs_drop, size_of}; | |
| #[test] | |
| fn alpha_idx_size() { | |
| assert_eq!(size_of::<Option<AlphaIdx>>(), 4); | |
| } | |
| #[test] | |
| fn render_cmd_assertions() { | |
| assert_eq!(size_of::<RenderCmd>(), 16); | |
| assert!(!needs_drop::<RenderCmd>()); | |
| } | |
| } |