Skip to main content

aetna_core/
ir.rs

1//! Backend-neutral draw-op IR.
2//!
3//! Every visual fact in the laid-out tree resolves to a [`DrawOp`] bound
4//! to a [`ShaderHandle`] and a uniform block. The wgpu renderer dispatches
5//! by shader handle; the SVG fallback (`crate::bundle::svg`) interprets stock
6//! shaders best-effort and emits placeholder rects for custom ones.
7//!
8//! `BackdropSnapshot` is emitted by [`crate::runtime::RunnerCore`] when
9//! the resolved paint stream first needs a backdrop-sampling shader. See
10//! `docs/SHADER_VISION.md` for the backend contract.
11//!
12//! # Why DrawOp over RenderCmd
13//!
14//! Aetna keeps visual material decisions in shader handles and uniform blocks
15//! instead of baking CSS-shaped fields into the IR. Rect colors, gradients,
16//! shadows, focus rings, and glass effects resolve to stock shader uniforms or
17//! custom shader bindings before a backend records GPU commands.
18
19use crate::image::{Image, ImageFit};
20use crate::shader::{ShaderHandle, UniformBlock};
21use crate::svg_icon::IconSource;
22use crate::text::atlas::RunStyle;
23use crate::text::metrics::TextLayout;
24use crate::tree::{Color, Corners, FontFamily, FontWeight, Rect, TextWrap};
25use crate::vector::VectorRenderMode;
26
27/// One paint operation in the laid-out frame.
28#[derive(Clone, Debug)]
29pub enum DrawOp {
30    /// A rectangular region painted by a shader (typically
31    /// `stock::rounded_rect`, but custom shaders also emit `Quad`).
32    Quad {
33        id: String,
34        rect: Rect,
35        scissor: Option<Rect>,
36        shader: ShaderHandle,
37        uniforms: UniformBlock,
38    },
39    /// A run of text. The draw op carries the author text and measured layout;
40    /// backends shape/rasterize through the shared glyph atlas path.
41    GlyphRun {
42        id: String,
43        rect: Rect,
44        scissor: Option<Rect>,
45        shader: ShaderHandle,
46        /// Carried explicitly on the op for SVG fallback and backend text
47        /// shaping.
48        color: Color,
49        text: String,
50        size: f32,
51        line_height: f32,
52        family: FontFamily,
53        /// Monospace face used when `mono` is set. Stamped from the
54        /// source El's `mono_font_family` (themed via
55        /// `Theme::mono_font_family`).
56        mono_family: FontFamily,
57        weight: FontWeight,
58        mono: bool,
59        wrap: TextWrap,
60        anchor: TextAnchor,
61        layout: TextLayout,
62        /// Underline / strikethrough state lifted from the source El's
63        /// `text_underline` / `text_strikethrough`. Backends fold them
64        /// into the synthesized [`RunStyle`] before shaping so the
65        /// decoration pass in [`crate::text::atlas`] runs uniformly
66        /// for standalone leaves and attributed paragraphs.
67        underline: bool,
68        strikethrough: bool,
69        /// Optional link URL from the El's `text_link`. Carried for
70        /// future hit-test work; today it just pins color + underline
71        /// via [`RunStyle::with_link`].
72        link: Option<String>,
73    },
74    /// An attributed paragraph: a sequence of styled runs that flow
75    /// together inside one `rect`. The runtime hands `runs` straight to
76    /// [`crate::text::atlas::GlyphAtlas::shape_and_rasterize_runs`] so
77    /// wrapping decisions cross run boundaries (real prose, not glued
78    /// segments). `layout` is an approximate pre-shaping measurement
79    /// from `text::metrics` — backends shape for accurate placement;
80    /// SVG uses it to lay tspan baselines.
81    AttributedText {
82        id: String,
83        rect: Rect,
84        scissor: Option<Rect>,
85        shader: ShaderHandle,
86        /// Source-order styled spans. Each `String` may contain
87        /// embedded `\n` to express in-paragraph hard breaks.
88        runs: Vec<(String, RunStyle)>,
89        size: f32,
90        line_height: f32,
91        wrap: TextWrap,
92        anchor: TextAnchor,
93        layout: TextLayout,
94    },
95    /// A vector icon scaled into `rect`. The `source` is either a
96    /// built-in [`crate::tree::IconName`] (24x24 lucide-style) or an
97    /// app-supplied [`crate::SvgIcon`]. SVG bundle output renders the
98    /// vector paths directly; wgpu/vulkano backends bake an MTSDF (or
99    /// tessellate for non-flat materials); backends without a native
100    /// vector painter fall back to a glyph for built-ins.
101    Icon {
102        id: String,
103        rect: Rect,
104        scissor: Option<Rect>,
105        source: IconSource,
106        color: Color,
107        size: f32,
108        stroke_width: f32,
109    },
110    /// A raster image painted into `rect`. The `image` carries the
111    /// pixel data (Arc-shared with the source El) and is keyed by
112    /// `image.content_hash()` in backend texture caches. `rect` is the
113    /// post-`fit` destination; for `Cover` it can extend past the El's
114    /// content area and is clipped via `scissor`. SVG bundle output
115    /// emits a placeholder rect labelled with the image's hash.
116    Image {
117        id: String,
118        rect: Rect,
119        scissor: Option<Rect>,
120        image: Image,
121        tint: Option<Color>,
122        radius: Corners,
123        fit: ImageFit,
124    },
125    /// An app-owned GPU texture composited into the paint stream.
126    /// Unlike `DrawOp::Image`, the backend does not upload pixels —
127    /// it samples the existing texture identified by `texture` during
128    /// paint, keying its bind-group cache on
129    /// [`crate::surface::AppTextureId`]. `rect` is the post-`fit`
130    /// destination rect; for `Cover` it can extend past the El's
131    /// content area and is clipped via `scissor`. `transform` is an
132    /// affine applied to the textured quad in destination space,
133    /// around the centre of `rect`. `alpha` selects the blend path.
134    /// SVG bundle output emits a placeholder rect labelled with the
135    /// texture's id.
136    AppTexture {
137        id: String,
138        rect: Rect,
139        scissor: Option<Rect>,
140        texture: crate::surface::AppTexture,
141        alpha: crate::surface::SurfaceAlpha,
142        fit: ImageFit,
143        transform: crate::affine::Affine2,
144    },
145    /// An app-supplied vector asset. `render_mode` decides whether the
146    /// backend preserves authored paint or treats the asset as a
147    /// one-colour mask. SVG bundle output emits a placeholder rect
148    /// labelled with the asset's hash.
149    Vector {
150        id: String,
151        rect: Rect,
152        scissor: Option<Rect>,
153        asset: std::sync::Arc<crate::vector::VectorAsset>,
154        render_mode: VectorRenderMode,
155    },
156    /// Mid-frame snapshot of the current target into a sampled texture,
157    /// scheduled before any backdrop-sampling pass.
158    BackdropSnapshot,
159}
160
161#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
162pub enum TextAnchor {
163    Start,
164    Middle,
165    End,
166}
167
168impl DrawOp {
169    pub fn id(&self) -> &str {
170        match self {
171            DrawOp::Quad { id, .. }
172            | DrawOp::GlyphRun { id, .. }
173            | DrawOp::AttributedText { id, .. }
174            | DrawOp::Icon { id, .. }
175            | DrawOp::Image { id, .. }
176            | DrawOp::AppTexture { id, .. }
177            | DrawOp::Vector { id, .. } => id,
178            DrawOp::BackdropSnapshot => "<backdrop-snapshot>",
179        }
180    }
181    pub fn shader(&self) -> Option<&ShaderHandle> {
182        match self {
183            DrawOp::Quad { shader, .. }
184            | DrawOp::GlyphRun { shader, .. }
185            | DrawOp::AttributedText { shader, .. } => Some(shader),
186            DrawOp::Icon { .. }
187            | DrawOp::Image { .. }
188            | DrawOp::AppTexture { .. }
189            | DrawOp::Vector { .. } => None,
190            DrawOp::BackdropSnapshot => None,
191        }
192    }
193    pub fn scissor(&self) -> Option<Rect> {
194        match self {
195            DrawOp::Quad { scissor, .. }
196            | DrawOp::GlyphRun { scissor, .. }
197            | DrawOp::AttributedText { scissor, .. }
198            | DrawOp::Icon { scissor, .. }
199            | DrawOp::Image { scissor, .. }
200            | DrawOp::AppTexture { scissor, .. }
201            | DrawOp::Vector { scissor, .. } => *scissor,
202            DrawOp::BackdropSnapshot => None,
203        }
204    }
205}