Skip to main content

gpui/
scene.rs

1// todo("windows"): remove
2#![cfg_attr(windows, allow(dead_code))]
3
4use schemars::JsonSchema;
5use serde::{Deserialize, Serialize};
6
7use crate::{
8    AtlasTextureId, AtlasTile, Background, Bounds, ContentMask, Corners, Edges, Hsla, Pixels,
9    Point, Radians, ScaledPixels, Size, bounds_tree::BoundsTree, point,
10};
11use std::{
12    fmt::Debug,
13    iter::Peekable,
14    ops::{Add, Range, Sub},
15    slice,
16};
17
18#[allow(non_camel_case_types, unused)]
19#[expect(missing_docs)]
20pub type PathVertex_ScaledPixels = PathVertex<ScaledPixels>;
21
22#[expect(missing_docs)]
23pub type DrawOrder = u32;
24
25/// A boolean stored as a `u32` so that GPU-facing structs contain no
26/// compiler-inserted padding bytes, which would be undefined behavior to
27/// reinterpret as `&[u8]` when writing instance buffers. Guaranteed to be
28/// `0` or `1` by construction; shaders read it as a `u32`/`uint`.
29#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
30#[repr(transparent)]
31pub struct PaddedBool32(u32);
32
33impl From<bool> for PaddedBool32 {
34    fn from(value: bool) -> Self {
35        PaddedBool32(value as u32)
36    }
37}
38
39#[derive(Default)]
40#[expect(missing_docs)]
41pub struct Scene {
42    pub(crate) paint_operations: Vec<PaintOperation>,
43    primitive_bounds: BoundsTree<ScaledPixels>,
44    layer_stack: Vec<DrawOrder>,
45    pub shadows: Vec<Shadow>,
46    pub quads: Vec<Quad>,
47    pub paths: Vec<Path<ScaledPixels>>,
48    pub underlines: Vec<Underline>,
49    pub monochrome_sprites: Vec<MonochromeSprite>,
50    pub subpixel_sprites: Vec<SubpixelSprite>,
51    pub polychrome_sprites: Vec<PolychromeSprite>,
52    pub surfaces: Vec<PaintSurface>,
53    pub custom_draws: Vec<CustomDraw>,
54    /// Ordered, directly rendered fragment shader instances.
55    pub shader_draws: Vec<ShaderDraw>,
56    /// Explicit backdrop sampling boundaries.
57    pub backdrops: Vec<crate::BackdropDraw>,
58    /// Ordered begin/end markers for captured subtrees.
59    pub layer_boundaries: Vec<crate::LayerBoundary>,
60}
61
62#[expect(missing_docs)]
63impl Scene {
64    pub fn clear(&mut self) {
65        self.paint_operations.clear();
66        self.primitive_bounds.clear();
67        self.layer_stack.clear();
68        self.paths.clear();
69        self.shadows.clear();
70        self.quads.clear();
71        self.underlines.clear();
72        self.monochrome_sprites.clear();
73        self.subpixel_sprites.clear();
74        self.polychrome_sprites.clear();
75        self.surfaces.clear();
76        self.custom_draws.clear();
77        self.shader_draws.clear();
78        self.backdrops.clear();
79        self.layer_boundaries.clear();
80    }
81
82    pub fn len(&self) -> usize {
83        self.paint_operations.len()
84    }
85
86    pub fn push_layer(&mut self, bounds: Bounds<ScaledPixels>) {
87        let order = self.primitive_bounds.insert(bounds);
88        self.layer_stack.push(order);
89        self.paint_operations
90            .push(PaintOperation::StartLayer(bounds));
91    }
92
93    pub fn pop_layer(&mut self) {
94        self.layer_stack.pop();
95        self.paint_operations.push(PaintOperation::EndLayer);
96    }
97
98    pub fn insert_primitive(&mut self, primitive: impl Into<Primitive>) {
99        static NEXT_COMMAND: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(1);
100        self.insert_retained_primitive(
101            primitive.into(),
102            NEXT_COMMAND.fetch_add(1, std::sync::atomic::Ordering::Relaxed),
103        );
104    }
105
106    fn insert_retained_primitive(&mut self, mut primitive: Primitive, identity: u64) {
107        let clipped_bounds = primitive
108            .bounds()
109            .intersect(&primitive.content_mask().bounds);
110
111        if clipped_bounds.is_empty() {
112            return;
113        }
114
115        // A backdrop is a global pass barrier, including inside a native layer
116        // whose children would otherwise share a draw order.
117        let order = if matches!(
118            primitive,
119            Primitive::Backdrop(_) | Primitive::LayerBoundary(_)
120        ) {
121            let fence = primitive.content_mask().bounds;
122            let order = self.primitive_bounds.insert(fence);
123            let after = self.primitive_bounds.insert(fence);
124            self.layer_stack.fill(after);
125            order
126        } else {
127            self.layer_stack
128                .last()
129                .copied()
130                .unwrap_or_else(|| self.primitive_bounds.insert(clipped_bounds))
131        };
132        match &mut primitive {
133            Primitive::LayerBoundary(draw) => {
134                draw.order = order;
135                self.layer_boundaries.push(draw.clone());
136            }
137            Primitive::Backdrop(draw) => {
138                draw.order = order;
139                self.backdrops.push(draw.clone());
140            }
141            Primitive::Shadow(shadow) => {
142                shadow.order = order;
143                self.shadows.push(*shadow);
144            }
145            Primitive::Quad(quad) => {
146                quad.order = order;
147                self.quads.push(*quad);
148            }
149            Primitive::Path(path) => {
150                path.order = order;
151                path.id = PathId(self.paths.len());
152                self.paths.push(path.clone());
153            }
154            Primitive::Underline(underline) => {
155                underline.order = order;
156                self.underlines.push(*underline);
157            }
158            Primitive::MonochromeSprite(sprite) => {
159                sprite.order = order;
160                self.monochrome_sprites.push(*sprite);
161            }
162            Primitive::SubpixelSprite(sprite) => {
163                sprite.order = order;
164                self.subpixel_sprites.push(*sprite);
165            }
166            Primitive::PolychromeSprite(sprite) => {
167                sprite.order = order;
168                self.polychrome_sprites.push(*sprite);
169            }
170            Primitive::Shader(draw) => {
171                draw.order = order;
172                self.shader_draws.push(draw.clone());
173            }
174            Primitive::Custom(draw) => {
175                draw.order = order;
176                self.custom_draws.push(draw.clone());
177            }
178            Primitive::Surface(surface) => {
179                surface.order = order;
180                self.surfaces.push(surface.clone());
181            }
182        }
183        self.paint_operations
184            .push(PaintOperation::Primitive(primitive, identity));
185    }
186
187    pub fn replay(&mut self, range: Range<usize>, prev_scene: &Scene) {
188        for operation in &prev_scene.paint_operations[range] {
189            match operation {
190                PaintOperation::Primitive(primitive, identity) => {
191                    self.insert_retained_primitive(primitive.clone(), *identity)
192                }
193                PaintOperation::StartLayer(bounds) => self.push_layer(*bounds),
194                PaintOperation::EndLayer => self.pop_layer(),
195            }
196        }
197    }
198
199    /// Begin an explicit captured subtree. End with the returned description.
200    pub fn begin_layer_capture(
201        &mut self,
202        capture: std::sync::Arc<crate::LayerCapture>,
203        viewport: ContentMask<ScaledPixels>,
204    ) {
205        self.insert_primitive(Primitive::LayerBoundary(crate::LayerBoundary {
206            order: 0,
207            bounds: viewport.bounds,
208            content_mask: viewport,
209            capture,
210            begin: true,
211        }));
212    }
213    /// End the matching captured subtree.
214    pub fn end_layer_capture(
215        &mut self,
216        capture: std::sync::Arc<crate::LayerCapture>,
217        viewport: ContentMask<ScaledPixels>,
218    ) {
219        self.insert_primitive(Primitive::LayerBoundary(crate::LayerBoundary {
220            order: 0,
221            bounds: viewport.bounds,
222            content_mask: viewport,
223            capture,
224            begin: false,
225        }));
226    }
227    /// Validate nesting, bounds and command revisions before any group GPU work.
228    pub fn layer_plans(
229        &self,
230        viewport: Size<crate::DevicePixels>,
231    ) -> anyhow::Result<Vec<crate::LayerPlan>> {
232        use anyhow::ensure;
233        use std::sync::Arc;
234        if self.layer_boundaries.is_empty() {
235            return Ok(Vec::new());
236        }
237        ensure!(
238            self.layer_boundaries.len() <= crate::MAX_LAYER_GROUPS * 2,
239            "at most four captured groups per frame"
240        );
241        let mut plans: Vec<crate::LayerPlan> = Vec::new();
242        let mut revisions: Vec<Vec<u64>> = Vec::new();
243        let mut stack: Vec<(Arc<crate::LayerCapture>, usize)> = Vec::new();
244        for operation in &self.paint_operations {
245            let PaintOperation::Primitive(primitive, identity) = operation else {
246                continue;
247            };
248            let closing = matches!(primitive,Primitive::LayerBoundary(boundary) if !boundary.begin);
249            for (depth, (_, slot)) in stack.iter().enumerate() {
250                // The group's own opacity/boundary can change without repainting
251                // its retained descendants. Nested boundaries remain part of the
252                // parent's revision because they affect its captured pixels.
253                if closing && depth + 1 == stack.len() {
254                    continue;
255                }
256                revisions[*slot].push(*identity);
257                ensure!(
258                    revisions[*slot].len() <= crate::MAX_LAYER_COMMANDS,
259                    "captured group exceeds 65536 commands"
260                );
261            }
262            if let Primitive::LayerBoundary(boundary) = primitive {
263                let index = self
264                    .layer_boundaries
265                    .iter()
266                    .position(|b| {
267                        b.order == boundary.order
268                            && b.begin == boundary.begin
269                            && Arc::ptr_eq(&b.capture, &boundary.capture)
270                    })
271                    .ok_or_else(|| anyhow::anyhow!("missing layer boundary"))?;
272                if boundary.begin {
273                    ensure!(
274                        plans.len() < crate::MAX_LAYER_GROUPS,
275                        "at most four captured groups per frame"
276                    );
277                    let plan = crate::LayerPlan::new(&boundary.capture, viewport, index)?;
278                    if let Some((_, parent)) = stack.last() {
279                        let p = &plans[*parent];
280                        ensure!(
281                            plan.origin[0] >= p.origin[0]
282                                && plan.origin[1] >= p.origin[1]
283                                && plan.origin[0] + plan.size[0] <= p.origin[0] + p.size[0]
284                                && plan.origin[1] + plan.size[1] <= p.origin[1] + p.size[1],
285                            "nested layer escapes its parent capture"
286                        );
287                    }
288                    let slot = plans.len();
289                    plans.push(plan);
290                    revisions.push(Vec::new());
291                    stack.push((boundary.capture.clone(), slot));
292                } else {
293                    let (capture, slot) = stack
294                        .pop()
295                        .ok_or_else(|| anyhow::anyhow!("unmatched layer end"))?;
296                    ensure!(
297                        Arc::ptr_eq(&capture, &boundary.capture),
298                        "crossed layer boundaries"
299                    );
300                    plans[slot].end_index = index;
301                    plans[slot].revision = std::mem::take(&mut revisions[slot]).into();
302                }
303                continue;
304            }
305            if !stack.is_empty() {
306                ensure!(
307                    !matches!(primitive, Primitive::Backdrop(_)),
308                    "backdrop sampling inside captured groups is not supported"
309                );
310                ensure!(
311                    !matches!(primitive, Primitive::SubpixelSprite(_)),
312                    "captured text requires grayscale glyph coverage"
313                );
314            }
315            for (_, slot) in &stack {
316                let plan = &mut plans[*slot];
317                if matches!(primitive, Primitive::Custom(_) | Primitive::Surface(_)) {
318                    plan.reusable = false;
319                }
320                let clipped = primitive
321                    .bounds()
322                    .intersect(&primitive.content_mask().bounds);
323                ensure!(
324                    clipped.is_empty()
325                        || (clipped.left().0 >= plan.origin[0] as f32
326                            && clipped.top().0 >= plan.origin[1] as f32
327                            && clipped.right().0 <= (plan.origin[0] + plan.size[0]) as f32
328                            && clipped.bottom().0 <= (plan.origin[1] + plan.size[1]) as f32),
329                    "paint escapes its layer capture; apply the capture content mask"
330                );
331            }
332        }
333        ensure!(stack.is_empty(), "unclosed layer capture");
334        Ok(plans)
335    }
336
337    /// Conservative whole-prefix dependencies for backdrop capture reuse.
338    /// Retained replay preserves identities; newly painted commands invalidate.
339    pub fn backdrop_cache_keys(
340        &self,
341        viewport: Size<crate::DevicePixels>,
342    ) -> anyhow::Result<Vec<crate::BackdropCacheKey>> {
343        if self.backdrops.is_empty() {
344            return Ok(Vec::new());
345        }
346        anyhow::ensure!(
347            self.backdrops.len() <= crate::MAX_BACKDROPS,
348            "at most four backdrop boundaries per frame"
349        );
350        let mut keys = Vec::new();
351        let mut revision = Vec::new();
352        let mut reusable = true;
353        for operation in &self.paint_operations {
354            let PaintOperation::Primitive(primitive, identity) = operation else {
355                continue;
356            };
357            if let Primitive::Backdrop(draw) = primitive {
358                let plan = draw.plan(viewport)?;
359                keys.push((
360                    draw.order,
361                    crate::BackdropCacheKey {
362                        viewport,
363                        origin: plan.origin,
364                        size: plan.size,
365                        filter: [plan.parameters[0], plan.parameters[1]],
366                        revision: revision.clone().into(),
367                        reusable,
368                    },
369                ));
370            }
371            // These producers can mutate pixels without new retained commands.
372            if matches!(primitive, Primitive::Custom(_) | Primitive::Surface(_)) {
373                reusable = false;
374            }
375            if revision.len() < crate::MAX_LAYER_COMMANDS {
376                let order = match primitive {
377                    Primitive::Shadow(p) => p.order,
378                    Primitive::Quad(p) => p.order,
379                    Primitive::Path(p) => p.order,
380                    Primitive::Underline(p) => p.order,
381                    Primitive::MonochromeSprite(p) => p.order,
382                    Primitive::SubpixelSprite(p) => p.order,
383                    Primitive::PolychromeSprite(p) => p.order,
384                    Primitive::Surface(p) => p.order,
385                    Primitive::Custom(p) => p.order,
386                    Primitive::Shader(p) => p.order,
387                    Primitive::Backdrop(p) => p.order,
388                    Primitive::LayerBoundary(p) => p.order,
389                };
390                revision.push((*identity, order));
391            } else {
392                reusable = false;
393            }
394        }
395        anyhow::ensure!(
396            keys.len() == self.backdrops.len(),
397            "backdrops require scene insertion/replay"
398        );
399        keys.sort_by_key(|(order, _)| *order);
400        Ok(keys.into_iter().map(|(_, key)| key).collect())
401    }
402
403    pub fn finish(&mut self) {
404        self.shadows.sort_by_key(|shadow| shadow.order);
405        self.quads.sort_by_key(|quad| quad.order);
406        self.paths.sort_by_key(|path| path.order);
407        self.underlines.sort_by_key(|underline| underline.order);
408        self.monochrome_sprites
409            .sort_by_key(|sprite| (sprite.order, sprite.tile.tile_id));
410        self.subpixel_sprites
411            .sort_by_key(|sprite| (sprite.order, sprite.tile.tile_id));
412        self.polychrome_sprites
413            .sort_by_key(|sprite| (sprite.order, sprite.tile.tile_id));
414        self.surfaces.sort_by_key(|surface| surface.order);
415        self.custom_draws.sort_by_key(|draw| draw.order);
416        self.shader_draws.sort_by_key(|draw| draw.order);
417        self.backdrops.sort_by_key(|draw| draw.order);
418        self.layer_boundaries.sort_by_key(|draw| draw.order);
419    }
420
421    #[cfg_attr(
422        all(
423            any(target_os = "linux", target_os = "freebsd"),
424            not(any(feature = "x11", feature = "wayland"))
425        ),
426        allow(dead_code)
427    )]
428    pub fn batches(&self) -> impl Iterator<Item = PrimitiveBatch> + '_ {
429        BatchIterator {
430            shadows_start: 0,
431            shadows_iter: self.shadows.iter().peekable(),
432            quads_start: 0,
433            quads_iter: self.quads.iter().peekable(),
434            paths_start: 0,
435            paths_iter: self.paths.iter().peekable(),
436            underlines_start: 0,
437            underlines_iter: self.underlines.iter().peekable(),
438            monochrome_sprites_start: 0,
439            monochrome_sprites_iter: self.monochrome_sprites.iter().peekable(),
440            subpixel_sprites_start: 0,
441            subpixel_sprites_iter: self.subpixel_sprites.iter().peekable(),
442            polychrome_sprites_start: 0,
443            polychrome_sprites_iter: self.polychrome_sprites.iter().peekable(),
444            layer_boundary_start: 0,
445            layer_boundary_iter: self.layer_boundaries.iter().peekable(),
446            backdrop_start: 0,
447            backdrop_iter: self.backdrops.iter().peekable(),
448            shader_start: 0,
449            shader_iter: self.shader_draws.iter().peekable(),
450            custom_start: 0,
451            custom_iter: self.custom_draws.iter().peekable(),
452            surfaces_start: 0,
453            surfaces_iter: self.surfaces.iter().peekable(),
454        }
455    }
456}
457
458#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Default)]
459#[cfg_attr(
460    all(
461        any(target_os = "linux", target_os = "freebsd"),
462        not(any(feature = "x11", feature = "wayland"))
463    ),
464    allow(dead_code)
465)]
466pub(crate) enum PrimitiveKind {
467    Shadow,
468    #[default]
469    Quad,
470    Path,
471    Underline,
472    MonochromeSprite,
473    SubpixelSprite,
474    PolychromeSprite,
475    Surface,
476    Custom,
477    Shader,
478    Backdrop,
479    LayerBoundary,
480}
481
482pub(crate) enum PaintOperation {
483    Primitive(Primitive, u64),
484    StartLayer(Bounds<ScaledPixels>),
485    EndLayer,
486}
487
488#[derive(Clone)]
489#[expect(missing_docs)]
490pub enum Primitive {
491    Shadow(Shadow),
492    Quad(Quad),
493    Path(Path<ScaledPixels>),
494    Underline(Underline),
495    MonochromeSprite(MonochromeSprite),
496    SubpixelSprite(SubpixelSprite),
497    PolychromeSprite(PolychromeSprite),
498    Surface(PaintSurface),
499    Custom(CustomDraw),
500    Shader(ShaderDraw),
501    Backdrop(crate::BackdropDraw),
502    LayerBoundary(crate::LayerBoundary),
503}
504
505#[expect(missing_docs)]
506impl Primitive {
507    pub fn bounds(&self) -> &Bounds<ScaledPixels> {
508        match self {
509            Primitive::Shadow(shadow) => &shadow.bounds,
510            Primitive::Quad(quad) => &quad.bounds,
511            Primitive::Path(path) => &path.bounds,
512            Primitive::Underline(underline) => &underline.bounds,
513            Primitive::MonochromeSprite(sprite) => &sprite.bounds,
514            Primitive::SubpixelSprite(sprite) => &sprite.bounds,
515            Primitive::PolychromeSprite(sprite) => &sprite.bounds,
516            Primitive::Surface(surface) => &surface.bounds,
517            Primitive::Custom(draw) => &draw.bounds,
518            Primitive::Shader(draw) => &draw.bounds,
519            Primitive::Backdrop(draw) => &draw.bounds,
520            Primitive::LayerBoundary(draw) => &draw.bounds,
521        }
522    }
523
524    pub fn content_mask(&self) -> &ContentMask<ScaledPixels> {
525        match self {
526            Primitive::Shadow(shadow) => &shadow.content_mask,
527            Primitive::Quad(quad) => &quad.content_mask,
528            Primitive::Path(path) => &path.content_mask,
529            Primitive::Underline(underline) => &underline.content_mask,
530            Primitive::MonochromeSprite(sprite) => &sprite.content_mask,
531            Primitive::SubpixelSprite(sprite) => &sprite.content_mask,
532            Primitive::PolychromeSprite(sprite) => &sprite.content_mask,
533            Primitive::Surface(surface) => &surface.content_mask,
534            Primitive::Custom(draw) => &draw.content_mask,
535            Primitive::Shader(draw) => &draw.content_mask,
536            Primitive::Backdrop(draw) => &draw.content_mask,
537            Primitive::LayerBoundary(draw) => &draw.content_mask,
538        }
539    }
540}
541
542#[cfg_attr(
543    all(
544        any(target_os = "linux", target_os = "freebsd"),
545        not(any(feature = "x11", feature = "wayland"))
546    ),
547    allow(dead_code)
548)]
549struct BatchIterator<'a> {
550    layer_boundary_start: usize,
551    layer_boundary_iter: Peekable<slice::Iter<'a, crate::LayerBoundary>>,
552    backdrop_start: usize,
553    backdrop_iter: Peekable<slice::Iter<'a, crate::BackdropDraw>>,
554    shader_start: usize,
555    shader_iter: Peekable<slice::Iter<'a, ShaderDraw>>,
556    custom_start: usize,
557    custom_iter: Peekable<slice::Iter<'a, CustomDraw>>,
558    shadows_start: usize,
559    shadows_iter: Peekable<slice::Iter<'a, Shadow>>,
560    quads_start: usize,
561    quads_iter: Peekable<slice::Iter<'a, Quad>>,
562    paths_start: usize,
563    paths_iter: Peekable<slice::Iter<'a, Path<ScaledPixels>>>,
564    underlines_start: usize,
565    underlines_iter: Peekable<slice::Iter<'a, Underline>>,
566    monochrome_sprites_start: usize,
567    monochrome_sprites_iter: Peekable<slice::Iter<'a, MonochromeSprite>>,
568    subpixel_sprites_start: usize,
569    subpixel_sprites_iter: Peekable<slice::Iter<'a, SubpixelSprite>>,
570    polychrome_sprites_start: usize,
571    polychrome_sprites_iter: Peekable<slice::Iter<'a, PolychromeSprite>>,
572    surfaces_start: usize,
573    surfaces_iter: Peekable<slice::Iter<'a, PaintSurface>>,
574}
575
576impl<'a> Iterator for BatchIterator<'a> {
577    type Item = PrimitiveBatch;
578
579    fn next(&mut self) -> Option<Self::Item> {
580        let mut orders_and_kinds = [
581            (
582                self.layer_boundary_iter.peek().map(|d| d.order),
583                PrimitiveKind::LayerBoundary,
584            ),
585            (
586                self.backdrop_iter.peek().map(|d| d.order),
587                PrimitiveKind::Backdrop,
588            ),
589            (
590                self.shader_iter.peek().map(|d| d.order),
591                PrimitiveKind::Shader,
592            ),
593            (
594                self.custom_iter.peek().map(|d| d.order),
595                PrimitiveKind::Custom,
596            ),
597            (
598                self.shadows_iter.peek().map(|s| s.order),
599                PrimitiveKind::Shadow,
600            ),
601            (self.quads_iter.peek().map(|q| q.order), PrimitiveKind::Quad),
602            (self.paths_iter.peek().map(|q| q.order), PrimitiveKind::Path),
603            (
604                self.underlines_iter.peek().map(|u| u.order),
605                PrimitiveKind::Underline,
606            ),
607            (
608                self.monochrome_sprites_iter.peek().map(|s| s.order),
609                PrimitiveKind::MonochromeSprite,
610            ),
611            (
612                self.subpixel_sprites_iter.peek().map(|s| s.order),
613                PrimitiveKind::SubpixelSprite,
614            ),
615            (
616                self.polychrome_sprites_iter.peek().map(|s| s.order),
617                PrimitiveKind::PolychromeSprite,
618            ),
619            (
620                self.surfaces_iter.peek().map(|s| s.order),
621                PrimitiveKind::Surface,
622            ),
623        ];
624        orders_and_kinds.sort_by_key(|(order, kind)| (order.unwrap_or(u32::MAX), *kind));
625
626        let first = orders_and_kinds[0];
627        let second = orders_and_kinds[1];
628        let (batch_kind, max_order_and_kind) = if first.0.is_some() {
629            (first.1, (second.0.unwrap_or(u32::MAX), second.1))
630        } else {
631            return None;
632        };
633
634        match batch_kind {
635            PrimitiveKind::LayerBoundary => {
636                let index = self.layer_boundary_start;
637                self.layer_boundary_iter.next();
638                self.layer_boundary_start += 1;
639                Some(PrimitiveBatch::LayerBoundary(index))
640            }
641            PrimitiveKind::Backdrop => {
642                let index = self.backdrop_start;
643                self.backdrop_iter.next();
644                self.backdrop_start += 1;
645                Some(PrimitiveBatch::Backdrop(index))
646            }
647            PrimitiveKind::Shader => {
648                let start = self.shader_start;
649                let first = self.shader_iter.next().unwrap();
650                let program_id = first.program.id();
651                let texture_id = first.glyph_mask.map(|tile| tile.texture_id);
652                self.shader_start += 1;
653                let mut coverage_bounds = Vec::new();
654                if first.path_mask.is_some() {
655                    coverage_bounds.push(first.bounds.dilate(ScaledPixels(1.)));
656                }
657                while self
658                    .shader_iter
659                    .next_if(|draw| {
660                        let compatible = draw.program.id() == program_id
661                            && draw.glyph_mask.map(|tile| tile.texture_id) == texture_id
662                            && (draw.order, batch_kind) < max_order_and_kind;
663                        if !compatible || first.path_mask.is_some() != draw.path_mask.is_some() {
664                            return false;
665                        }
666                        if first.path_mask.is_some() {
667                            if coverage_bounds.len() >= 64 {
668                                return false;
669                            }
670                            // Keep each composite rectangle clear of every other mask,
671                            // including a device-pixel gutter for antialiasing/filtering.
672                            let bounds = draw.bounds.dilate(ScaledPixels(1.));
673                            if coverage_bounds
674                                .iter()
675                                .any(|previous| previous.intersects(&bounds))
676                            {
677                                return false;
678                            }
679                            coverage_bounds.push(bounds);
680                        }
681                        true
682                    })
683                    .is_some()
684                {
685                    self.shader_start += 1;
686                }
687                Some(PrimitiveBatch::Shaders {
688                    program_id,
689                    range: start..self.shader_start,
690                })
691            }
692            PrimitiveKind::Custom => {
693                let start = self.custom_start;
694                self.custom_iter.next();
695                self.custom_start += 1;
696                while self
697                    .custom_iter
698                    .next_if(|d| (d.order, batch_kind) < max_order_and_kind)
699                    .is_some()
700                {
701                    self.custom_start += 1;
702                }
703                Some(PrimitiveBatch::Custom(start..self.custom_start))
704            }
705            PrimitiveKind::Shadow => {
706                let shadows_start = self.shadows_start;
707                let mut shadows_end = shadows_start + 1;
708                self.shadows_iter.next();
709                while self
710                    .shadows_iter
711                    .next_if(|shadow| (shadow.order, batch_kind) < max_order_and_kind)
712                    .is_some()
713                {
714                    shadows_end += 1;
715                }
716                self.shadows_start = shadows_end;
717                Some(PrimitiveBatch::Shadows(shadows_start..shadows_end))
718            }
719            PrimitiveKind::Quad => {
720                let quads_start = self.quads_start;
721                let mut quads_end = quads_start + 1;
722                self.quads_iter.next();
723                while self
724                    .quads_iter
725                    .next_if(|quad| (quad.order, batch_kind) < max_order_and_kind)
726                    .is_some()
727                {
728                    quads_end += 1;
729                }
730                self.quads_start = quads_end;
731                Some(PrimitiveBatch::Quads(quads_start..quads_end))
732            }
733            PrimitiveKind::Path => {
734                let paths_start = self.paths_start;
735                let mut paths_end = paths_start + 1;
736                self.paths_iter.next();
737                while self
738                    .paths_iter
739                    .next_if(|path| (path.order, batch_kind) < max_order_and_kind)
740                    .is_some()
741                {
742                    paths_end += 1;
743                }
744                self.paths_start = paths_end;
745                Some(PrimitiveBatch::Paths(paths_start..paths_end))
746            }
747            PrimitiveKind::Underline => {
748                let underlines_start = self.underlines_start;
749                let mut underlines_end = underlines_start + 1;
750                self.underlines_iter.next();
751                while self
752                    .underlines_iter
753                    .next_if(|underline| (underline.order, batch_kind) < max_order_and_kind)
754                    .is_some()
755                {
756                    underlines_end += 1;
757                }
758                self.underlines_start = underlines_end;
759                Some(PrimitiveBatch::Underlines(underlines_start..underlines_end))
760            }
761            PrimitiveKind::MonochromeSprite => {
762                let texture_id = self.monochrome_sprites_iter.peek().unwrap().tile.texture_id;
763                let sprites_start = self.monochrome_sprites_start;
764                let mut sprites_end = sprites_start + 1;
765                self.monochrome_sprites_iter.next();
766                while self
767                    .monochrome_sprites_iter
768                    .next_if(|sprite| {
769                        (sprite.order, batch_kind) < max_order_and_kind
770                            && sprite.tile.texture_id == texture_id
771                    })
772                    .is_some()
773                {
774                    sprites_end += 1;
775                }
776                self.monochrome_sprites_start = sprites_end;
777                Some(PrimitiveBatch::MonochromeSprites {
778                    texture_id,
779                    range: sprites_start..sprites_end,
780                })
781            }
782            PrimitiveKind::SubpixelSprite => {
783                let texture_id = self.subpixel_sprites_iter.peek().unwrap().tile.texture_id;
784                let sprites_start = self.subpixel_sprites_start;
785                let mut sprites_end = sprites_start + 1;
786                self.subpixel_sprites_iter.next();
787                while self
788                    .subpixel_sprites_iter
789                    .next_if(|sprite| {
790                        (sprite.order, batch_kind) < max_order_and_kind
791                            && sprite.tile.texture_id == texture_id
792                    })
793                    .is_some()
794                {
795                    sprites_end += 1;
796                }
797                self.subpixel_sprites_start = sprites_end;
798                Some(PrimitiveBatch::SubpixelSprites {
799                    texture_id,
800                    range: sprites_start..sprites_end,
801                })
802            }
803            PrimitiveKind::PolychromeSprite => {
804                let texture_id = self.polychrome_sprites_iter.peek().unwrap().tile.texture_id;
805                let sprites_start = self.polychrome_sprites_start;
806                let mut sprites_end = sprites_start + 1;
807                self.polychrome_sprites_iter.next();
808                while self
809                    .polychrome_sprites_iter
810                    .next_if(|sprite| {
811                        (sprite.order, batch_kind) < max_order_and_kind
812                            && sprite.tile.texture_id == texture_id
813                    })
814                    .is_some()
815                {
816                    sprites_end += 1;
817                }
818                self.polychrome_sprites_start = sprites_end;
819                Some(PrimitiveBatch::PolychromeSprites {
820                    texture_id,
821                    range: sprites_start..sprites_end,
822                })
823            }
824            PrimitiveKind::Surface => {
825                let surfaces_start = self.surfaces_start;
826                let mut surfaces_end = surfaces_start + 1;
827                self.surfaces_iter.next();
828                while self
829                    .surfaces_iter
830                    .next_if(|surface| (surface.order, batch_kind) < max_order_and_kind)
831                    .is_some()
832                {
833                    surfaces_end += 1;
834                }
835                self.surfaces_start = surfaces_end;
836                Some(PrimitiveBatch::Surfaces(surfaces_start..surfaces_end))
837            }
838        }
839    }
840}
841
842#[derive(Debug)]
843#[cfg_attr(
844    all(
845        any(target_os = "linux", target_os = "freebsd"),
846        not(any(feature = "x11", feature = "wayland"))
847    ),
848    allow(dead_code)
849)]
850#[allow(missing_docs)]
851pub enum PrimitiveBatch {
852    /// End the current pass, sample the existing framebuffer, then resume it.
853    Backdrop(usize),
854    /// Capture entry/exit pass barrier.
855    LayerBoundary(usize),
856    Shadows(Range<usize>),
857    Quads(Range<usize>),
858    Paths(Range<usize>),
859    Underlines(Range<usize>),
860    MonochromeSprites {
861        texture_id: AtlasTextureId,
862        range: Range<usize>,
863    },
864    #[cfg_attr(target_os = "macos", allow(dead_code))]
865    SubpixelSprites {
866        texture_id: AtlasTextureId,
867        range: Range<usize>,
868    },
869    PolychromeSprites {
870        texture_id: AtlasTextureId,
871        range: Range<usize>,
872    },
873    Surfaces(Range<usize>),
874    Custom(Range<usize>),
875    /// A compatible run of fragment shader instances, in scene order.
876    Shaders {
877        program_id: u64,
878        range: Range<usize>,
879    },
880}
881
882impl PrimitiveBatch {
883    #[expect(missing_docs)]
884    pub fn label(&self) -> String {
885        match self {
886            Self::LayerBoundary(index) => format!("layer boundary ({index})"),
887            Self::Backdrop(index) => format!("backdrop ({index})"),
888            Self::Shaders { range, .. } => format!("shaders ({})", range.len()),
889            Self::Shadows(range) => format!("shadows ({})", range.len()),
890            Self::Quads(range) => format!("quads ({})", range.len()),
891            Self::Paths(range) => format!("paths ({})", range.len()),
892            Self::Underlines(range) => format!("underlines ({})", range.len()),
893            Self::MonochromeSprites { texture_id, range } => {
894                format!(
895                    "monochrome sprites ({}) on atlas {}",
896                    range.len(),
897                    texture_id.index
898                )
899            }
900            Self::SubpixelSprites { texture_id, range } => {
901                format!(
902                    "subpixel sprites ({}) on atlas {}",
903                    range.len(),
904                    texture_id.index
905                )
906            }
907            Self::PolychromeSprites { texture_id, range } => {
908                format!(
909                    "polychrome sprites ({}) on atlas {}",
910                    range.len(),
911                    texture_id.index
912                )
913            }
914            Self::Custom(range) => format!("custom ({})", range.len()),
915            Self::Surfaces(range) => format!("surfaces ({})", range.len()),
916        }
917    }
918}
919
920#[derive(Default, Debug, Copy, Clone)]
921#[repr(C)]
922#[expect(missing_docs)]
923pub struct Quad {
924    pub order: DrawOrder,
925    pub border_style: BorderStyle,
926    pub bounds: Bounds<ScaledPixels>,
927    pub content_mask: ContentMask<ScaledPixels>,
928    pub background: Background,
929    pub border_color: Hsla,
930    pub corner_radii: Corners<ScaledPixels>,
931    pub border_widths: Edges<ScaledPixels>,
932}
933
934impl From<Quad> for Primitive {
935    fn from(quad: Quad) -> Self {
936        Primitive::Quad(quad)
937    }
938}
939
940#[derive(Debug, Copy, Clone)]
941#[repr(C)]
942#[expect(missing_docs)]
943pub struct Underline {
944    pub order: DrawOrder,
945    pub pad: u32, // align to 8 bytes
946    pub bounds: Bounds<ScaledPixels>,
947    pub content_mask: ContentMask<ScaledPixels>,
948    pub color: Hsla,
949    pub thickness: ScaledPixels,
950    pub wavy: PaddedBool32,
951}
952
953impl From<Underline> for Primitive {
954    fn from(underline: Underline) -> Self {
955        Primitive::Underline(underline)
956    }
957}
958
959#[derive(Debug, Copy, Clone)]
960#[repr(C)]
961#[expect(missing_docs)]
962pub struct Shadow {
963    pub order: DrawOrder,
964    pub blur_radius: ScaledPixels,
965    pub bounds: Bounds<ScaledPixels>,
966    pub corner_radii: Corners<ScaledPixels>,
967    pub content_mask: ContentMask<ScaledPixels>,
968    pub color: Hsla,
969    pub element_bounds: Bounds<ScaledPixels>,
970    pub element_corner_radii: Corners<ScaledPixels>,
971    /// 0 = drop shadow (rendered outside the element), 1 = inset shadow (rendered inside).
972    pub inset: u32,
973    pub pad: u32, // align to 8 bytes
974}
975
976impl From<Shadow> for Primitive {
977    fn from(shadow: Shadow) -> Self {
978        Primitive::Shadow(shadow)
979    }
980}
981
982/// The style of a border.
983#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
984#[repr(C)]
985pub enum BorderStyle {
986    /// A solid border.
987    #[default]
988    Solid = 0,
989    /// A dashed border.
990    Dashed = 1,
991}
992
993/// A data type representing a 2 dimensional transformation that can be applied to an element.
994#[derive(Debug, Clone, Copy, PartialEq)]
995#[repr(C)]
996pub struct TransformationMatrix {
997    /// 2x2 matrix containing rotation and scale,
998    /// stored row-major
999    pub rotation_scale: [[f32; 2]; 2],
1000    /// translation vector
1001    pub translation: [f32; 2],
1002}
1003
1004impl Eq for TransformationMatrix {}
1005
1006impl TransformationMatrix {
1007    /// The unit matrix, has no effect.
1008    pub fn unit() -> Self {
1009        Self {
1010            rotation_scale: [[1.0, 0.0], [0.0, 1.0]],
1011            translation: [0.0, 0.0],
1012        }
1013    }
1014
1015    /// Move the origin by a given point
1016    pub fn translate(mut self, point: Point<ScaledPixels>) -> Self {
1017        self.compose(Self {
1018            rotation_scale: [[1.0, 0.0], [0.0, 1.0]],
1019            translation: [point.x.0, point.y.0],
1020        })
1021    }
1022
1023    /// Clockwise rotation in radians around the origin
1024    pub fn rotate(self, angle: Radians) -> Self {
1025        self.compose(Self {
1026            rotation_scale: [
1027                [angle.0.cos(), -angle.0.sin()],
1028                [angle.0.sin(), angle.0.cos()],
1029            ],
1030            translation: [0.0, 0.0],
1031        })
1032    }
1033
1034    /// Scale around the origin
1035    pub fn scale(self, size: Size<f32>) -> Self {
1036        self.compose(Self {
1037            rotation_scale: [[size.width, 0.0], [0.0, size.height]],
1038            translation: [0.0, 0.0],
1039        })
1040    }
1041
1042    /// Perform matrix multiplication with another transformation
1043    /// to produce a new transformation that is the result of
1044    /// applying both transformations: first, `other`, then `self`.
1045    #[inline]
1046    pub fn compose(self, other: TransformationMatrix) -> TransformationMatrix {
1047        if other == Self::unit() {
1048            return self;
1049        }
1050        // Perform matrix multiplication
1051        TransformationMatrix {
1052            rotation_scale: [
1053                [
1054                    self.rotation_scale[0][0] * other.rotation_scale[0][0]
1055                        + self.rotation_scale[0][1] * other.rotation_scale[1][0],
1056                    self.rotation_scale[0][0] * other.rotation_scale[0][1]
1057                        + self.rotation_scale[0][1] * other.rotation_scale[1][1],
1058                ],
1059                [
1060                    self.rotation_scale[1][0] * other.rotation_scale[0][0]
1061                        + self.rotation_scale[1][1] * other.rotation_scale[1][0],
1062                    self.rotation_scale[1][0] * other.rotation_scale[0][1]
1063                        + self.rotation_scale[1][1] * other.rotation_scale[1][1],
1064                ],
1065            ],
1066            translation: [
1067                self.translation[0]
1068                    + self.rotation_scale[0][0] * other.translation[0]
1069                    + self.rotation_scale[0][1] * other.translation[1],
1070                self.translation[1]
1071                    + self.rotation_scale[1][0] * other.translation[0]
1072                    + self.rotation_scale[1][1] * other.translation[1],
1073            ],
1074        }
1075    }
1076
1077    /// Apply transformation to a point, mainly useful for debugging
1078    pub fn apply(&self, point: Point<Pixels>) -> Point<Pixels> {
1079        let input = [point.x.0, point.y.0];
1080        let mut output = self.translation;
1081        for (i, output_cell) in output.iter_mut().enumerate() {
1082            for (k, input_cell) in input.iter().enumerate() {
1083                *output_cell += self.rotation_scale[i][k] * *input_cell;
1084            }
1085        }
1086        Point::new(output[0].into(), output[1].into())
1087    }
1088}
1089
1090impl Default for TransformationMatrix {
1091    fn default() -> Self {
1092        Self::unit()
1093    }
1094}
1095
1096#[derive(Copy, Clone, Debug)]
1097#[repr(C)]
1098#[expect(missing_docs)]
1099pub struct MonochromeSprite {
1100    pub order: DrawOrder,
1101    pub pad: u32,
1102    pub bounds: Bounds<ScaledPixels>,
1103    pub content_mask: ContentMask<ScaledPixels>,
1104    pub color: Hsla,
1105    pub tile: AtlasTile,
1106    pub transformation: TransformationMatrix,
1107}
1108
1109impl From<MonochromeSprite> for Primitive {
1110    fn from(sprite: MonochromeSprite) -> Self {
1111        Primitive::MonochromeSprite(sprite)
1112    }
1113}
1114
1115#[derive(Copy, Clone, Debug)]
1116#[repr(C)]
1117#[expect(missing_docs)]
1118pub struct SubpixelSprite {
1119    pub order: DrawOrder,
1120    pub pad: u32, // align to 8 bytes
1121    pub bounds: Bounds<ScaledPixels>,
1122    pub content_mask: ContentMask<ScaledPixels>,
1123    pub color: Hsla,
1124    pub tile: AtlasTile,
1125    pub transformation: TransformationMatrix,
1126}
1127
1128impl From<SubpixelSprite> for Primitive {
1129    fn from(sprite: SubpixelSprite) -> Self {
1130        Primitive::SubpixelSprite(sprite)
1131    }
1132}
1133
1134#[derive(Copy, Clone, Debug)]
1135#[repr(C)]
1136#[expect(missing_docs)]
1137pub struct PolychromeSprite {
1138    pub order: DrawOrder,
1139    pub pad: u32,
1140    pub grayscale: PaddedBool32,
1141    pub opacity: f32,
1142    pub bounds: Bounds<ScaledPixels>,
1143    pub content_mask: ContentMask<ScaledPixels>,
1144    pub corner_radii: Corners<ScaledPixels>,
1145    pub tile: AtlasTile,
1146}
1147
1148impl From<PolychromeSprite> for Primitive {
1149    fn from(sprite: PolychromeSprite) -> Self {
1150        Primitive::PolychromeSprite(sprite)
1151    }
1152}
1153
1154#[derive(Clone, Debug)]
1155#[allow(missing_docs)]
1156pub struct PaintSurface {
1157    pub order: DrawOrder,
1158    pub bounds: Bounds<ScaledPixels>,
1159    pub content_mask: ContentMask<ScaledPixels>,
1160    #[cfg(any(target_os = "macos", target_os = "ios"))]
1161    pub image_buffer: objc2_core_foundation::CFRetained<objc2_core_video::CVPixelBuffer>,
1162}
1163
1164impl From<PaintSurface> for Primitive {
1165    fn from(surface: PaintSurface) -> Self {
1166        Primitive::Surface(surface)
1167    }
1168}
1169
1170#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
1171#[expect(missing_docs)]
1172pub struct PathId(pub usize);
1173
1174/// A line made up of a series of vertices and control points.
1175#[derive(Clone, Debug)]
1176#[expect(missing_docs)]
1177pub struct Path<P: Clone + Debug + Default + PartialEq> {
1178    pub id: PathId,
1179    pub order: DrawOrder,
1180    pub bounds: Bounds<P>,
1181    pub content_mask: ContentMask<P>,
1182    pub vertices: Vec<PathVertex<P>>,
1183    pub color: Background,
1184    start: Point<P>,
1185    current: Point<P>,
1186    contour_count: usize,
1187}
1188
1189impl Path<Pixels> {
1190    /// Create a new path with the given starting point.
1191    pub fn new(start: Point<Pixels>) -> Self {
1192        Self {
1193            id: PathId(0),
1194            order: DrawOrder::default(),
1195            vertices: Vec::new(),
1196            start,
1197            current: start,
1198            bounds: Bounds {
1199                origin: start,
1200                size: Default::default(),
1201            },
1202            content_mask: Default::default(),
1203            color: Default::default(),
1204            contour_count: 0,
1205        }
1206    }
1207
1208    /// Scale this path by the given factor.
1209    pub fn scale(&self, factor: f32) -> Path<ScaledPixels> {
1210        Path {
1211            id: self.id,
1212            order: self.order,
1213            bounds: self.bounds.scale(factor),
1214            content_mask: self.content_mask.scale(factor),
1215            vertices: self
1216                .vertices
1217                .iter()
1218                .map(|vertex| vertex.scale(factor))
1219                .collect(),
1220            start: self.start.map(|start| start.scale(factor)),
1221            current: self.current.scale(factor),
1222            contour_count: self.contour_count,
1223            color: self.color,
1224        }
1225    }
1226
1227    /// Move the start, current point to the given point.
1228    pub fn move_to(&mut self, to: Point<Pixels>) {
1229        self.contour_count += 1;
1230        self.start = to;
1231        self.current = to;
1232    }
1233
1234    /// Draw a straight line from the current point to the given point.
1235    pub fn line_to(&mut self, to: Point<Pixels>) {
1236        self.contour_count += 1;
1237        if self.contour_count > 1 {
1238            self.push_triangle(
1239                (self.start, self.current, to),
1240                (point(0., 1.), point(0., 1.), point(0., 1.)),
1241            );
1242        }
1243        self.current = to;
1244    }
1245
1246    /// Draw a curve from the current point to the given point, using the given control point.
1247    pub fn curve_to(&mut self, to: Point<Pixels>, ctrl: Point<Pixels>) {
1248        self.contour_count += 1;
1249        if self.contour_count > 1 {
1250            self.push_triangle(
1251                (self.start, self.current, to),
1252                (point(0., 1.), point(0., 1.), point(0., 1.)),
1253            );
1254        }
1255
1256        self.push_triangle(
1257            (self.current, ctrl, to),
1258            (point(0., 0.), point(0.5, 0.), point(1., 1.)),
1259        );
1260        self.current = to;
1261    }
1262
1263    /// Push a triangle to the Path.
1264    pub fn push_triangle(
1265        &mut self,
1266        xy: (Point<Pixels>, Point<Pixels>, Point<Pixels>),
1267        st: (Point<f32>, Point<f32>, Point<f32>),
1268    ) {
1269        self.bounds = self
1270            .bounds
1271            .union(&Bounds {
1272                origin: xy.0,
1273                size: Default::default(),
1274            })
1275            .union(&Bounds {
1276                origin: xy.1,
1277                size: Default::default(),
1278            })
1279            .union(&Bounds {
1280                origin: xy.2,
1281                size: Default::default(),
1282            });
1283
1284        self.vertices.push(PathVertex {
1285            xy_position: xy.0,
1286            st_position: st.0,
1287            content_mask: Default::default(),
1288        });
1289        self.vertices.push(PathVertex {
1290            xy_position: xy.1,
1291            st_position: st.1,
1292            content_mask: Default::default(),
1293        });
1294        self.vertices.push(PathVertex {
1295            xy_position: xy.2,
1296            st_position: st.2,
1297            content_mask: Default::default(),
1298        });
1299    }
1300}
1301
1302impl<T> Path<T>
1303where
1304    T: Clone + Debug + Default + PartialEq + PartialOrd + Add<T, Output = T> + Sub<Output = T>,
1305{
1306    #[allow(unused)]
1307    #[expect(missing_docs)]
1308    pub fn clipped_bounds(&self) -> Bounds<T> {
1309        self.bounds.intersect(&self.content_mask.bounds)
1310    }
1311}
1312
1313impl From<Path<ScaledPixels>> for Primitive {
1314    fn from(path: Path<ScaledPixels>) -> Self {
1315        Primitive::Path(path)
1316    }
1317}
1318
1319#[derive(Clone, Debug)]
1320#[repr(C)]
1321#[expect(missing_docs)]
1322pub struct PathVertex<P: Clone + Debug + Default + PartialEq> {
1323    pub xy_position: Point<P>,
1324    pub st_position: Point<f32>,
1325    pub content_mask: ContentMask<P>,
1326}
1327
1328#[expect(missing_docs)]
1329impl PathVertex<Pixels> {
1330    pub fn scale(&self, factor: f32) -> PathVertex<ScaledPixels> {
1331        PathVertex {
1332            xy_position: self.xy_position.scale(factor),
1333            st_position: self.st_position,
1334            content_mask: self.content_mask.scale(factor),
1335        }
1336    }
1337}
1338
1339/// Geometry needed to prepare an unclipped, local custom paint source.
1340///
1341/// Raster dimensions are chosen by the renderer after snapping. They need not
1342/// equal logical dimensions multiplied by the window scale factor. Color,
1343/// clipping, opacity and submission are not part of this request.
1344#[derive(Clone, Copy, Debug, PartialEq)]
1345pub struct PreparationRequest {
1346    logical_size: Size<Pixels>,
1347    raster_size: Size<crate::DevicePixels>,
1348    scale_factor: f32,
1349}
1350
1351impl PreparationRequest {
1352    /// Construct a request with finite, positive dimensions and scale.
1353    pub fn new(
1354        logical_size: Size<Pixels>,
1355        raster_size: Size<crate::DevicePixels>,
1356        scale_factor: f32,
1357    ) -> anyhow::Result<Self> {
1358        anyhow::ensure!(
1359            logical_size.width.0.is_finite()
1360                && logical_size.width.0 > 0.
1361                && logical_size.height.0.is_finite()
1362                && logical_size.height.0 > 0.
1363                && raster_size.width.0 > 0
1364                && raster_size.height.0 > 0
1365                && scale_factor.is_finite()
1366                && scale_factor > 0.,
1367            "invalid custom preparation geometry"
1368        );
1369        Ok(Self {
1370            logical_size,
1371            raster_size,
1372            scale_factor,
1373        })
1374    }
1375
1376    /// Full source size in logical GPUI pixels, before clipping or snapping.
1377    pub fn logical_size(&self) -> Size<Pixels> {
1378        self.logical_size
1379    }
1380
1381    /// Exact physical dimensions required for the prepared texture.
1382    pub fn raster_size(&self) -> Size<crate::DevicePixels> {
1383        self.raster_size
1384    }
1385
1386    /// Window scale factor; use `raster_size` for texture allocation.
1387    pub fn scale_factor(&self) -> f32 {
1388        self.scale_factor
1389    }
1390}
1391
1392/// An immutable, replayable description of independent GPU content.
1393/// Backend contexts and outputs use checked type erasure; backend crates define
1394/// their concrete types. Implementations must not submit or present GPU work.
1395pub trait CustomContent: std::fmt::Debug + Send + Sync + 'static {
1396    /// Encoded-sRGB RGB affine rows, applied after sampling/unassociation and
1397    /// before coverage/opacity. Alpha is preserved. Identity keeps legacy output.
1398    fn image_color_transform(&self) -> [[f32; 4]; 3] {
1399        [[1., 0., 0., 0.], [0., 1., 0., 0.], [0., 0., 1., 0.]]
1400    }
1401
1402    /// Actual dimensions of an immutable source-sized texture. A large virtual
1403    /// destination need not allocate a destination-sized preparation target.
1404    /// Only valid for retained source-sized outputs, without geometry coverage.
1405    fn image_source_size(&self) -> Option<Size<crate::DevicePixels>> {
1406        None
1407    }
1408    /// Sampling visibility independent of preparation. Zero permits warming a
1409    /// source without compositing it over a progressive fallback.
1410    fn image_sample_opacity(&self) -> f32 {
1411        1.
1412    }
1413    /// Source color interpretation and explicit output policy.
1414    fn image_color_mode(&self) -> crate::ImageColorMode {
1415        crate::ImageColorMode::EncodedSrgb
1416    }
1417    /// Linear matrix and vignette in the declared working primaries.
1418    /// Replaces encoded image_color_transform; does not change source identity.
1419    fn image_linear_adjustments(&self) -> Option<crate::LinearImagePaint> {
1420        None
1421    }
1422
1423    /// Prepare content on the host device. Return an explicit error for unsupported backends.
1424    fn prepare(
1425        &self,
1426        backend: &dyn std::any::Any,
1427        size: Size<crate::DevicePixels>,
1428    ) -> anyhow::Result<Box<dyn std::any::Any>>;
1429
1430    /// Prepare within shared scene-frame admission. Existing producers retain
1431    /// their request-aware behavior without a new required implementation.
1432    fn prepare_in_frame(
1433        &self,
1434        backend: &dyn std::any::Any,
1435        request: &PreparationRequest,
1436        _frame: &crate::PreparationFrame,
1437    ) -> anyhow::Result<Box<dyn std::any::Any>> {
1438        self.prepare_with_request(backend, request)
1439    }
1440
1441    /// Prepare using logical geometry and the renderer's exact raster extent.
1442    /// Existing producers receive the same physical size through `prepare`.
1443    /// Request-aware producers may override this without changing submission.
1444    fn prepare_with_request(
1445        &self,
1446        backend: &dyn std::any::Any,
1447        request: &PreparationRequest,
1448    ) -> anyhow::Result<Box<dyn std::any::Any>> {
1449        self.prepare(backend, request.raster_size())
1450    }
1451}
1452
1453// Preserve unsnapped logical geometry without adding fields to public scene
1454// records. The original producer still owns its snapshots and preparation cache.
1455#[derive(Debug)]
1456struct ContentWithPreparationGeometry {
1457    content: std::sync::Arc<dyn CustomContent>,
1458    logical_size: Size<Pixels>,
1459    scale_factor: f32,
1460}
1461
1462impl CustomContent for ContentWithPreparationGeometry {
1463    fn image_source_size(&self) -> Option<Size<crate::DevicePixels>> {
1464        self.content.image_source_size()
1465    }
1466    fn image_sample_opacity(&self) -> f32 {
1467        self.content.image_sample_opacity()
1468    }
1469    fn image_color_mode(&self) -> crate::ImageColorMode {
1470        self.content.image_color_mode()
1471    }
1472    fn image_linear_adjustments(&self) -> Option<crate::LinearImagePaint> {
1473        self.content.image_linear_adjustments()
1474    }
1475
1476    fn image_color_transform(&self) -> [[f32; 4]; 3] {
1477        self.content.image_color_transform()
1478    }
1479
1480    fn prepare(
1481        &self,
1482        backend: &dyn std::any::Any,
1483        size: Size<crate::DevicePixels>,
1484    ) -> anyhow::Result<Box<dyn std::any::Any>> {
1485        let request = PreparationRequest::new(self.logical_size, size, self.scale_factor)?;
1486        self.content.prepare_with_request(backend, &request)
1487    }
1488
1489    fn prepare_in_frame(
1490        &self,
1491        backend: &dyn std::any::Any,
1492        request: &PreparationRequest,
1493        frame: &crate::PreparationFrame,
1494    ) -> anyhow::Result<Box<dyn std::any::Any>> {
1495        let request =
1496            PreparationRequest::new(self.logical_size, request.raster_size(), self.scale_factor)?;
1497        self.content.prepare_in_frame(backend, &request, frame)
1498    }
1499
1500    fn prepare_with_request(
1501        &self,
1502        backend: &dyn std::any::Any,
1503        request: &PreparationRequest,
1504    ) -> anyhow::Result<Box<dyn std::any::Any>> {
1505        self.prepare(backend, request.raster_size())
1506    }
1507}
1508
1509pub(crate) fn content_with_preparation_geometry(
1510    content: std::sync::Arc<dyn CustomContent>,
1511    logical_size: Size<Pixels>,
1512    scale_factor: f32,
1513) -> std::sync::Arc<dyn CustomContent> {
1514    std::sync::Arc::new(ContentWithPreparationGeometry {
1515        content,
1516        logical_size,
1517        scale_factor,
1518    })
1519}
1520
1521#[cfg(test)]
1522mod preparation_tests;
1523
1524/// Logical geometry covered by independent GPU content. GPUI owns snapping,
1525/// inherited clipping/opacity, native coverage, and retained scene insertion.
1526#[derive(Clone)]
1527pub enum CustomGeometry {
1528    /// A filled rounded rectangle over its full unclipped bounds.
1529    RoundedRect {
1530        /// Full source rectangle in logical GPUI pixels.
1531        bounds: Bounds<Pixels>,
1532        /// Logical corner radii, clamped and scaled by the host.
1533        corner_radii: Corners<Pixels>,
1534    },
1535    /// A native border ring/dashes. Only the quad's geometry fields are used.
1536    Border(crate::PaintQuad),
1537    /// An existing tessellated native fill or stroke.
1538    Path(Path<Pixels>),
1539}
1540
1541/// Native geometry used as coverage for extension-produced paint.
1542#[derive(Clone, Debug)]
1543pub enum CustomCoverage {
1544    /// Border coverage over the full outer paint rectangle.
1545    Border {
1546        /// Physical border widths.
1547        widths: Edges<ScaledPixels>,
1548        /// Existing border style.
1549        style: BorderStyle,
1550    },
1551    /// Existing tessellated path; paint maps over its unclipped bounds.
1552    Path(std::sync::Arc<Path<ScaledPixels>>),
1553}
1554
1555/// Retained custom drawing. CPU ownership data is never uploaded as an instance struct.
1556#[derive(Clone, Debug)]
1557pub struct CustomDraw {
1558    /// Assigned by scene insertion.
1559    pub order: DrawOrder,
1560    /// Destination in scaled scene coordinates.
1561    pub bounds: Bounds<ScaledPixels>,
1562    /// Inherited rectangular clip.
1563    pub content_mask: ContentMask<ScaledPixels>,
1564    /// Local corner coverage, in scaled pixels.
1565    pub corner_radii: Corners<ScaledPixels>,
1566    /// Inherited element opacity, applied once at composition.
1567    pub opacity: f32,
1568    /// Owned immutable content snapshot, shared across scene replay.
1569    /// Window painting retains original logical geometry internally. For directly
1570    /// constructed records, preparation uses `bounds.size` as logical size at 1x.
1571    pub content: std::sync::Arc<dyn CustomContent>,
1572    /// Optional native shape mask; None uses rounded rectangle coverage.
1573    pub coverage: Option<CustomCoverage>,
1574}
1575
1576impl From<CustomDraw> for Primitive {
1577    fn from(draw: CustomDraw) -> Self {
1578        Self::Custom(draw)
1579    }
1580}
1581
1582/// Maximum portable payload: eight float4 slots (128 bytes) per instance.
1583pub const SHADER_DATA_SLOTS: usize = 8;
1584
1585/// Explicit color meaning of an instanced fragment program's output.
1586/// Wide output requires an opted-in native wide-SDR compositor.
1587#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1588#[non_exhaustive]
1589pub enum ShaderOutputEncoding {
1590    /// Legacy straight-alpha encoded-sRGB SDR producer output.
1591    EncodedSrgb,
1592    /// Bounded straight-alpha linear Display P3/D65 producer output.
1593    /// The host wrapper associates alpha and converts to composition primaries.
1594    LinearDisplayP3,
1595    /// Bounded straight-alpha linear Rec.2020/D65 SDR producer output.
1596    /// The host converts to extended composition primaries after association.
1597    LinearRec2020,
1598}
1599
1600/// Backend-independent compiled fragment program. Created by shader extensions;
1601/// backend pipeline caches belong to the host renderer and its device.
1602#[derive(Debug)]
1603pub struct SceneShaderProgram {
1604    id: u64,
1605    output_encoding: ShaderOutputEncoding,
1606    glyph_mask: bool,
1607    path_mask: bool,
1608    /// Validated WGSL with host vertex/fragment entry points.
1609    wgsl: String,
1610    /// Translated Metal source and actual generated entry point names.
1611    metal: (String, String, String),
1612    /// Translated shader-model-5 HLSL and actual generated entry point names.
1613    hlsl: (String, String, String),
1614}
1615impl SceneShaderProgram {
1616    /// Allocate a unique identity for validated backend sources.
1617    pub fn new(
1618        wgsl: String,
1619        metal: (String, String, String),
1620        hlsl: (String, String, String),
1621    ) -> Self {
1622        Self::new_with_output_encoding(wgsl, metal, hlsl, ShaderOutputEncoding::EncodedSrgb)
1623    }
1624    /// Create an immutable program with explicit output meaning. Encoding is part
1625    /// of the unique program identity used by backend pipeline caches.
1626    pub fn new_with_output_encoding(
1627        wgsl: String,
1628        metal: (String, String, String),
1629        hlsl: (String, String, String),
1630        output_encoding: ShaderOutputEncoding,
1631    ) -> Self {
1632        static NEXT_ID: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(1);
1633        Self {
1634            id: NEXT_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed),
1635            output_encoding,
1636            glyph_mask: false,
1637            path_mask: false,
1638            wgsl,
1639            metal,
1640            hlsl,
1641        }
1642    }
1643    /// Declare that the validated program reads a grayscale glyph atlas at
1644    /// texture binding group 1/binding 0. Call before sharing the program.
1645    pub fn with_glyph_mask(mut self) -> Self {
1646        self.glyph_mask = true;
1647        self.path_mask = false;
1648        self
1649    }
1650    /// Declare a host-provided native path coverage texture at group 1/binding 0.
1651    pub fn with_path_mask(mut self) -> Self {
1652        self.path_mask = true;
1653        self.glyph_mask = false;
1654        self
1655    }
1656    /// Whether the host must rasterize retained path coverage before this draw.
1657    pub fn uses_path_mask(&self) -> bool {
1658        self.path_mask
1659    }
1660    /// Whether each instance requires a retained monochrome atlas tile.
1661    pub fn uses_glyph_mask(&self) -> bool {
1662        self.glyph_mask
1663    }
1664    /// Immutable output meaning; part of this program's unique cache identity.
1665    pub fn output_encoding(&self) -> ShaderOutputEncoding {
1666        self.output_encoding
1667    }
1668    /// Immutable validated WGSL source.
1669    pub fn wgsl(&self) -> &str {
1670        &self.wgsl
1671    }
1672    /// Metal source, vertex entry and fragment entry.
1673    pub fn metal(&self) -> &(String, String, String) {
1674        &self.metal
1675    }
1676    /// HLSL source, vertex entry and fragment entry.
1677    pub fn hlsl(&self) -> &(String, String, String) {
1678        &self.hlsl
1679    }
1680    /// Stable across clones of an immutable program handle.
1681    pub fn id(&self) -> u64 {
1682        self.id
1683    }
1684}
1685
1686/// Immutable instance recorded into a scene and retained through paint replay.
1687#[derive(Clone, Debug)]
1688pub struct ShaderDraw {
1689    /// Host draw order, assigned on insertion.
1690    pub order: DrawOrder,
1691    /// Full source bounds in device pixels, including any effect outset.
1692    pub bounds: Bounds<ScaledPixels>,
1693    /// Host clipping in device pixels.
1694    pub content_mask: ContentMask<ScaledPixels>,
1695    /// Inherited element opacity, applied exactly once by the generated wrapper.
1696    pub opacity: f32,
1697    /// Logical-to-device scale used for shader coordinates.
1698    pub scale_factor: f32,
1699    /// Immutable program handle.
1700    pub program: std::sync::Arc<SceneShaderProgram>,
1701    /// Optional retained grayscale atlas tile. Masked programs batch by texture.
1702    pub glyph_mask: Option<AtlasTile>,
1703    /// Native opaque-white coverage, rasterized into the existing path target.
1704    /// Disjoint composite bounds can share a coverage/composition batch.
1705    pub path_mask: Option<std::sync::Arc<Path<ScaledPixels>>>,
1706    /// Reflected and packed payload. Unused slots are zero.
1707    pub data: [[f32; 4]; SHADER_DATA_SLOTS],
1708}
1709impl ShaderDraw {
1710    /// Check mask ownership before preparing backend resources.
1711    pub fn validate_masks(&self) -> anyhow::Result<()> {
1712        anyhow::ensure!(
1713            self.program.uses_glyph_mask() == self.glyph_mask.is_some(),
1714            "shader glyph mask/program mismatch"
1715        );
1716        anyhow::ensure!(
1717            self.program.uses_path_mask() == self.path_mask.is_some(),
1718            "shader path mask/program mismatch"
1719        );
1720        anyhow::ensure!(
1721            self.glyph_mask.is_none() || self.path_mask.is_none(),
1722            "shader cannot combine glyph and path masks"
1723        );
1724        if let Some(path) = &self.path_mask {
1725            anyhow::ensure!(
1726                !path.vertices.is_empty()
1727                    && path.vertices.len() <= 65_536
1728                    && path.vertices.len() % 3 == 0,
1729                "path brush requires 1..65536 triangle vertices"
1730            );
1731            anyhow::ensure!(
1732                path.color.as_solid().is_some_and(|c| c.a == 1.),
1733                "path brush coverage must be opaque"
1734            );
1735        }
1736        Ok(())
1737    }
1738    /// Transport layout: bounds, clip, opacity/scale, then payload; 176 bytes.
1739    pub fn instance_data(&self) -> [[f32; 4]; 3 + SHADER_DATA_SLOTS] {
1740        let mut result = [[0.; 4]; 3 + SHADER_DATA_SLOTS];
1741        result[0] = [
1742            self.bounds.origin.x.0,
1743            self.bounds.origin.y.0,
1744            self.bounds.size.width.0,
1745            self.bounds.size.height.0,
1746        ];
1747        let clip = self.content_mask.bounds;
1748        result[1] = [
1749            clip.origin.x.0,
1750            clip.origin.y.0,
1751            clip.origin.x.0 + clip.size.width.0,
1752            clip.origin.y.0 + clip.size.height.0,
1753        ];
1754        result[2] = [self.opacity, self.scale_factor, 0., 0.];
1755        result[3..].copy_from_slice(&self.data);
1756        result
1757    }
1758}
1759
1760/// Extension hook for drawing monochrome glyph coverage in the main pass.
1761/// Emoji bypass this hook. Implementations must retain the atlas tile in their
1762/// recorded primitive and use the supplied inherited opacity exactly once.
1763pub trait GlyphBrush: std::fmt::Debug + Send + Sync + 'static {
1764    /// Retained identity of this immutable paint snapshot. Return the same token
1765    /// for identical clones and a new token whenever paint parameters change.
1766    fn cache_key(&self) -> std::sync::Arc<()>;
1767    /// Record this glyph using full brush coordinates and inherited opacity.
1768    fn paint_glyph(
1769        &self,
1770        window: &mut crate::Window,
1771        glyph: MonochromeSprite,
1772        brush_bounds: Bounds<Pixels>,
1773        opacity: f32,
1774    ) -> anyhow::Result<()>;
1775}