Skip to main content

gpui_engine/
scene.rs

1// todo("windows"): remove
2#![cfg_attr(windows, allow(dead_code))]
3
4use schemars::JsonSchema;
5use serde::{Deserialize, Serialize};
6
7use crate::{AtlasTextureId, AtlasTile, CustomRenderPrimitive, bounds_tree::BoundsTree};
8use gpui_types::{
9    Background, Bounds, ContentMask, Corners, DrawOrder, Edges, Hsla, Pixels, Point, Radians,
10    ScaledPixels, Shadow, Size, point,
11};
12use std::{
13    fmt::Debug,
14    iter::Peekable,
15    ops::{Add, Range, Sub},
16    slice,
17};
18
19#[allow(non_camel_case_types, unused)]
20#[expect(missing_docs)]
21pub type PathVertex_ScaledPixels = PathVertex<ScaledPixels>;
22
23/// A boolean stored as a `u32` so that GPU-facing structs contain no
24/// compiler-inserted padding bytes, which would be undefined behavior to
25/// reinterpret as `&[u8]` when writing instance buffers. Guaranteed to be
26/// `0` or `1` by construction; shaders read it as a `u32`/`uint`.
27#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
28#[repr(transparent)]
29pub struct PaddedBool32(u32);
30
31impl From<bool> for PaddedBool32 {
32    fn from(value: bool) -> Self {
33        PaddedBool32(value as u32)
34    }
35}
36
37#[derive(Default)]
38#[expect(missing_docs)]
39pub struct Scene {
40    pub(crate) paint_operations: Vec<PaintOperation>,
41    primitive_bounds: BoundsTree<ScaledPixels>,
42    layer_stack: Vec<DrawOrder>,
43    pub shadows: Vec<Shadow>,
44    pub quads: Vec<Quad>,
45    pub paths: Vec<Path<ScaledPixels>>,
46    pub underlines: Vec<Underline>,
47    pub monochrome_sprites: Vec<MonochromeSprite>,
48    pub subpixel_sprites: Vec<SubpixelSprite>,
49    pub polychrome_sprites: Vec<PolychromeSprite>,
50    pub surfaces: Vec<PaintSurface>,
51    pub custom: Vec<CustomRenderPrimitive>,
52}
53
54#[expect(missing_docs)]
55impl Scene {
56    pub fn clear(&mut self) {
57        self.paint_operations.clear();
58        self.primitive_bounds.clear();
59        self.layer_stack.clear();
60        self.paths.clear();
61        self.shadows.clear();
62        self.quads.clear();
63        self.underlines.clear();
64        self.monochrome_sprites.clear();
65        self.subpixel_sprites.clear();
66        self.polychrome_sprites.clear();
67        self.surfaces.clear();
68        self.custom.clear();
69    }
70
71    pub fn len(&self) -> usize {
72        self.paint_operations.len()
73    }
74
75    pub fn push_layer(&mut self, bounds: Bounds<ScaledPixels>) {
76        let order = self.primitive_bounds.insert(bounds);
77        self.layer_stack.push(order);
78        self.paint_operations
79            .push(PaintOperation::StartLayer(bounds));
80    }
81
82    pub fn pop_layer(&mut self) {
83        self.layer_stack.pop();
84        self.paint_operations.push(PaintOperation::EndLayer);
85    }
86
87    pub fn insert_primitive(&mut self, primitive: impl Into<Primitive>) {
88        let mut primitive = primitive.into();
89        let clipped_bounds = primitive
90            .bounds()
91            .intersect(&primitive.content_mask().bounds);
92
93        if clipped_bounds.is_empty() {
94            return;
95        }
96
97        let order = self
98            .layer_stack
99            .last()
100            .copied()
101            .unwrap_or_else(|| self.primitive_bounds.insert(clipped_bounds));
102        match &mut primitive {
103            Primitive::Shadow(shadow) => {
104                shadow.order = order;
105                self.shadows.push(*shadow);
106            }
107            Primitive::Quad(quad) => {
108                quad.order = order;
109                self.quads.push(*quad);
110            }
111            Primitive::Path(path) => {
112                path.order = order;
113                path.id = PathId(self.paths.len());
114                self.paths.push(path.clone());
115            }
116            Primitive::Underline(underline) => {
117                underline.order = order;
118                self.underlines.push(*underline);
119            }
120            Primitive::MonochromeSprite(sprite) => {
121                sprite.order = order;
122                self.monochrome_sprites.push(*sprite);
123            }
124            Primitive::SubpixelSprite(sprite) => {
125                sprite.order = order;
126                self.subpixel_sprites.push(*sprite);
127            }
128            Primitive::PolychromeSprite(sprite) => {
129                sprite.order = order;
130                self.polychrome_sprites.push(*sprite);
131            }
132            Primitive::Surface(surface) => {
133                // On a platform with no surface backend yet, `SurfaceSource` — and so `PaintSurface` —
134                // is uninhabited and this arm is unreachable. It stays for the platforms that do
135                // carry a source (macOS and Windows today, Linux once the dma-buf backend lands).
136                #[allow(unreachable_code)]
137                {
138                    surface.order = order;
139                    self.surfaces.push(surface.clone());
140                }
141            }
142            Primitive::Custom(custom) => {
143                *custom.order_mut() = order;
144                self.custom.push(custom.clone());
145            }
146        }
147        self.paint_operations
148            .push(PaintOperation::Primitive(primitive));
149    }
150
151    pub fn replay(&mut self, range: Range<usize>, prev_scene: &Scene) {
152        for operation in &prev_scene.paint_operations[range] {
153            match operation {
154                PaintOperation::Primitive(primitive) => self.insert_primitive(primitive.clone()),
155                PaintOperation::StartLayer(bounds) => self.push_layer(*bounds),
156                PaintOperation::EndLayer => self.pop_layer(),
157            }
158        }
159    }
160
161    pub fn finish(&mut self) {
162        self.shadows.sort_by_key(|shadow| shadow.order);
163        self.quads.sort_by_key(|quad| quad.order);
164        self.paths.sort_by_key(|path| path.order);
165        self.underlines.sort_by_key(|underline| underline.order);
166        self.monochrome_sprites
167            .sort_by_key(|sprite| (sprite.order, sprite.tile.tile_id));
168        self.subpixel_sprites
169            .sort_by_key(|sprite| (sprite.order, sprite.tile.tile_id));
170        self.polychrome_sprites
171            .sort_by_key(|sprite| (sprite.order, sprite.tile.tile_id));
172        self.surfaces.sort_by_key(|surface| surface.order);
173        self.custom.sort_by_key(|custom| custom.order());
174    }
175
176    #[cfg_attr(
177        all(
178            any(target_os = "linux", target_os = "freebsd"),
179            not(any(feature = "x11", feature = "wayland"))
180        ),
181        allow(dead_code)
182    )]
183    pub fn batches(&self) -> impl Iterator<Item = PrimitiveBatch> + '_ {
184        BatchIterator {
185            shadows_start: 0,
186            shadows_iter: self.shadows.iter().peekable(),
187            quads_start: 0,
188            quads_iter: self.quads.iter().peekable(),
189            paths_start: 0,
190            paths_iter: self.paths.iter().peekable(),
191            underlines_start: 0,
192            underlines_iter: self.underlines.iter().peekable(),
193            monochrome_sprites_start: 0,
194            monochrome_sprites_iter: self.monochrome_sprites.iter().peekable(),
195            subpixel_sprites_start: 0,
196            subpixel_sprites_iter: self.subpixel_sprites.iter().peekable(),
197            polychrome_sprites_start: 0,
198            polychrome_sprites_iter: self.polychrome_sprites.iter().peekable(),
199            surfaces_start: 0,
200            surfaces_iter: self.surfaces.iter().peekable(),
201            custom_start: 0,
202            custom_iter: self.custom.iter().peekable(),
203        }
204    }
205}
206
207#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Default)]
208#[cfg_attr(
209    all(
210        any(target_os = "linux", target_os = "freebsd"),
211        not(any(feature = "x11", feature = "wayland"))
212    ),
213    allow(dead_code)
214)]
215pub(crate) enum PrimitiveKind {
216    Shadow,
217    #[default]
218    Quad,
219    Path,
220    Underline,
221    MonochromeSprite,
222    SubpixelSprite,
223    PolychromeSprite,
224    Surface,
225    Custom,
226}
227
228pub(crate) enum PaintOperation {
229    Primitive(Primitive),
230    StartLayer(Bounds<ScaledPixels>),
231    EndLayer,
232}
233
234#[derive(Clone)]
235#[expect(missing_docs)]
236pub enum Primitive {
237    Shadow(Shadow),
238    Quad(Quad),
239    Path(Path<ScaledPixels>),
240    Underline(Underline),
241    MonochromeSprite(MonochromeSprite),
242    SubpixelSprite(SubpixelSprite),
243    PolychromeSprite(PolychromeSprite),
244    Surface(PaintSurface),
245    Custom(CustomRenderPrimitive),
246}
247
248#[expect(missing_docs)]
249impl Primitive {
250    pub fn bounds(&self) -> &Bounds<ScaledPixels> {
251        match self {
252            Primitive::Shadow(shadow) => &shadow.bounds,
253            Primitive::Quad(quad) => &quad.bounds,
254            Primitive::Path(path) => &path.bounds,
255            Primitive::Underline(underline) => &underline.bounds,
256            Primitive::MonochromeSprite(sprite) => &sprite.bounds,
257            Primitive::SubpixelSprite(sprite) => &sprite.bounds,
258            Primitive::PolychromeSprite(sprite) => &sprite.bounds,
259            Primitive::Surface(surface) => &surface.bounds,
260            Primitive::Custom(custom) => custom.bounds(),
261        }
262    }
263
264    pub fn content_mask(&self) -> &ContentMask<ScaledPixels> {
265        match self {
266            Primitive::Shadow(shadow) => &shadow.content_mask,
267            Primitive::Quad(quad) => &quad.content_mask,
268            Primitive::Path(path) => &path.content_mask,
269            Primitive::Underline(underline) => &underline.content_mask,
270            Primitive::MonochromeSprite(sprite) => &sprite.content_mask,
271            Primitive::SubpixelSprite(sprite) => &sprite.content_mask,
272            Primitive::PolychromeSprite(sprite) => &sprite.content_mask,
273            Primitive::Surface(surface) => &surface.content_mask,
274            Primitive::Custom(custom) => custom.content_mask(),
275        }
276    }
277}
278
279#[cfg_attr(
280    all(
281        any(target_os = "linux", target_os = "freebsd"),
282        not(any(feature = "x11", feature = "wayland"))
283    ),
284    allow(dead_code)
285)]
286struct BatchIterator<'a> {
287    shadows_start: usize,
288    shadows_iter: Peekable<slice::Iter<'a, Shadow>>,
289    quads_start: usize,
290    quads_iter: Peekable<slice::Iter<'a, Quad>>,
291    paths_start: usize,
292    paths_iter: Peekable<slice::Iter<'a, Path<ScaledPixels>>>,
293    underlines_start: usize,
294    underlines_iter: Peekable<slice::Iter<'a, Underline>>,
295    monochrome_sprites_start: usize,
296    monochrome_sprites_iter: Peekable<slice::Iter<'a, MonochromeSprite>>,
297    subpixel_sprites_start: usize,
298    subpixel_sprites_iter: Peekable<slice::Iter<'a, SubpixelSprite>>,
299    polychrome_sprites_start: usize,
300    polychrome_sprites_iter: Peekable<slice::Iter<'a, PolychromeSprite>>,
301    surfaces_start: usize,
302    surfaces_iter: Peekable<slice::Iter<'a, PaintSurface>>,
303    custom_start: usize,
304    custom_iter: Peekable<slice::Iter<'a, CustomRenderPrimitive>>,
305}
306
307impl<'a> Iterator for BatchIterator<'a> {
308    type Item = PrimitiveBatch;
309
310    fn next(&mut self) -> Option<Self::Item> {
311        let mut orders_and_kinds = [
312            (
313                self.shadows_iter.peek().map(|s| s.order),
314                PrimitiveKind::Shadow,
315            ),
316            (self.quads_iter.peek().map(|q| q.order), PrimitiveKind::Quad),
317            (self.paths_iter.peek().map(|q| q.order), PrimitiveKind::Path),
318            (
319                self.underlines_iter.peek().map(|u| u.order),
320                PrimitiveKind::Underline,
321            ),
322            (
323                self.monochrome_sprites_iter.peek().map(|s| s.order),
324                PrimitiveKind::MonochromeSprite,
325            ),
326            (
327                self.subpixel_sprites_iter.peek().map(|s| s.order),
328                PrimitiveKind::SubpixelSprite,
329            ),
330            (
331                self.polychrome_sprites_iter.peek().map(|s| s.order),
332                PrimitiveKind::PolychromeSprite,
333            ),
334            (
335                self.surfaces_iter.peek().map(|s| s.order),
336                PrimitiveKind::Surface,
337            ),
338            (
339                self.custom_iter.peek().map(|c| c.order()),
340                PrimitiveKind::Custom,
341            ),
342        ];
343        orders_and_kinds.sort_by_key(|(order, kind)| (order.unwrap_or(u32::MAX), *kind));
344
345        let first = orders_and_kinds[0];
346        let second = orders_and_kinds[1];
347        let (batch_kind, max_order_and_kind) = if first.0.is_some() {
348            (first.1, (second.0.unwrap_or(u32::MAX), second.1))
349        } else {
350            return None;
351        };
352
353        match batch_kind {
354            PrimitiveKind::Shadow => {
355                let shadows_start = self.shadows_start;
356                let mut shadows_end = shadows_start + 1;
357                self.shadows_iter.next();
358                while self
359                    .shadows_iter
360                    .next_if(|shadow| (shadow.order, batch_kind) < max_order_and_kind)
361                    .is_some()
362                {
363                    shadows_end += 1;
364                }
365                self.shadows_start = shadows_end;
366                Some(PrimitiveBatch::Shadows(shadows_start..shadows_end))
367            }
368            PrimitiveKind::Quad => {
369                let quads_start = self.quads_start;
370                let mut quads_end = quads_start + 1;
371                self.quads_iter.next();
372                while self
373                    .quads_iter
374                    .next_if(|quad| (quad.order, batch_kind) < max_order_and_kind)
375                    .is_some()
376                {
377                    quads_end += 1;
378                }
379                self.quads_start = quads_end;
380                Some(PrimitiveBatch::Quads(quads_start..quads_end))
381            }
382            PrimitiveKind::Path => {
383                let paths_start = self.paths_start;
384                let mut paths_end = paths_start + 1;
385                self.paths_iter.next();
386                while self
387                    .paths_iter
388                    .next_if(|path| (path.order, batch_kind) < max_order_and_kind)
389                    .is_some()
390                {
391                    paths_end += 1;
392                }
393                self.paths_start = paths_end;
394                Some(PrimitiveBatch::Paths(paths_start..paths_end))
395            }
396            PrimitiveKind::Underline => {
397                let underlines_start = self.underlines_start;
398                let mut underlines_end = underlines_start + 1;
399                self.underlines_iter.next();
400                while self
401                    .underlines_iter
402                    .next_if(|underline| (underline.order, batch_kind) < max_order_and_kind)
403                    .is_some()
404                {
405                    underlines_end += 1;
406                }
407                self.underlines_start = underlines_end;
408                Some(PrimitiveBatch::Underlines(underlines_start..underlines_end))
409            }
410            PrimitiveKind::MonochromeSprite => {
411                let texture_id = self.monochrome_sprites_iter.peek().unwrap().tile.texture_id;
412                let sprites_start = self.monochrome_sprites_start;
413                let mut sprites_end = sprites_start + 1;
414                self.monochrome_sprites_iter.next();
415                while self
416                    .monochrome_sprites_iter
417                    .next_if(|sprite| {
418                        (sprite.order, batch_kind) < max_order_and_kind
419                            && sprite.tile.texture_id == texture_id
420                    })
421                    .is_some()
422                {
423                    sprites_end += 1;
424                }
425                self.monochrome_sprites_start = sprites_end;
426                Some(PrimitiveBatch::MonochromeSprites {
427                    texture_id,
428                    range: sprites_start..sprites_end,
429                })
430            }
431            PrimitiveKind::SubpixelSprite => {
432                let texture_id = self.subpixel_sprites_iter.peek().unwrap().tile.texture_id;
433                let sprites_start = self.subpixel_sprites_start;
434                let mut sprites_end = sprites_start + 1;
435                self.subpixel_sprites_iter.next();
436                while self
437                    .subpixel_sprites_iter
438                    .next_if(|sprite| {
439                        (sprite.order, batch_kind) < max_order_and_kind
440                            && sprite.tile.texture_id == texture_id
441                    })
442                    .is_some()
443                {
444                    sprites_end += 1;
445                }
446                self.subpixel_sprites_start = sprites_end;
447                Some(PrimitiveBatch::SubpixelSprites {
448                    texture_id,
449                    range: sprites_start..sprites_end,
450                })
451            }
452            PrimitiveKind::PolychromeSprite => {
453                let texture_id = self.polychrome_sprites_iter.peek().unwrap().tile.texture_id;
454                let sprites_start = self.polychrome_sprites_start;
455                let mut sprites_end = sprites_start + 1;
456                self.polychrome_sprites_iter.next();
457                while self
458                    .polychrome_sprites_iter
459                    .next_if(|sprite| {
460                        (sprite.order, batch_kind) < max_order_and_kind
461                            && sprite.tile.texture_id == texture_id
462                    })
463                    .is_some()
464                {
465                    sprites_end += 1;
466                }
467                self.polychrome_sprites_start = sprites_end;
468                Some(PrimitiveBatch::PolychromeSprites {
469                    texture_id,
470                    range: sprites_start..sprites_end,
471                })
472            }
473            PrimitiveKind::Surface => {
474                let surfaces_start = self.surfaces_start;
475                let mut surfaces_end = surfaces_start + 1;
476                self.surfaces_iter.next();
477                while self
478                    .surfaces_iter
479                    .next_if(|surface| (surface.order, batch_kind) < max_order_and_kind)
480                    .is_some()
481                {
482                    surfaces_end += 1;
483                }
484                self.surfaces_start = surfaces_end;
485                Some(PrimitiveBatch::Surfaces(surfaces_start..surfaces_end))
486            }
487            PrimitiveKind::Custom => {
488                let custom_start = self.custom_start;
489                let mut custom_end = custom_start + 1;
490                self.custom_iter.next();
491                while self
492                    .custom_iter
493                    .next_if(|custom| (custom.order(), batch_kind) < max_order_and_kind)
494                    .is_some()
495                {
496                    custom_end += 1;
497                }
498                self.custom_start = custom_end;
499                Some(PrimitiveBatch::Custom(custom_start..custom_end))
500            }
501        }
502    }
503}
504
505#[derive(Debug)]
506#[cfg_attr(
507    all(
508        any(target_os = "linux", target_os = "freebsd"),
509        not(any(feature = "x11", feature = "wayland"))
510    ),
511    allow(dead_code)
512)]
513#[allow(missing_docs)]
514pub enum PrimitiveBatch {
515    Shadows(Range<usize>),
516    Quads(Range<usize>),
517    Paths(Range<usize>),
518    Underlines(Range<usize>),
519    MonochromeSprites {
520        texture_id: AtlasTextureId,
521        range: Range<usize>,
522    },
523    #[cfg_attr(target_os = "macos", allow(dead_code))]
524    SubpixelSprites {
525        texture_id: AtlasTextureId,
526        range: Range<usize>,
527    },
528    PolychromeSprites {
529        texture_id: AtlasTextureId,
530        range: Range<usize>,
531    },
532    Surfaces(Range<usize>),
533    Custom(Range<usize>),
534}
535
536impl PrimitiveBatch {
537    #[expect(missing_docs)]
538    pub fn label(&self) -> String {
539        match self {
540            Self::Shadows(range) => format!("shadows ({})", range.len()),
541            Self::Quads(range) => format!("quads ({})", range.len()),
542            Self::Paths(range) => format!("paths ({})", range.len()),
543            Self::Underlines(range) => format!("underlines ({})", range.len()),
544            Self::MonochromeSprites { texture_id, range } => {
545                format!(
546                    "monochrome sprites ({}) on atlas {}",
547                    range.len(),
548                    texture_id.index
549                )
550            }
551            Self::SubpixelSprites { texture_id, range } => {
552                format!(
553                    "subpixel sprites ({}) on atlas {}",
554                    range.len(),
555                    texture_id.index
556                )
557            }
558            Self::PolychromeSprites { texture_id, range } => {
559                format!(
560                    "polychrome sprites ({}) on atlas {}",
561                    range.len(),
562                    texture_id.index
563                )
564            }
565            Self::Surfaces(range) => format!("surfaces ({})", range.len()),
566            Self::Custom(range) => format!("custom ({})", range.len()),
567        }
568    }
569}
570
571#[derive(Default, Debug, Copy, Clone)]
572#[repr(C)]
573#[expect(missing_docs)]
574pub struct Quad {
575    pub order: DrawOrder,
576    pub border_style: BorderStyle,
577    pub bounds: Bounds<ScaledPixels>,
578    pub content_mask: ContentMask<ScaledPixels>,
579    pub background: Background,
580    pub border_color: Hsla,
581    pub corner_radii: Corners<ScaledPixels>,
582    pub border_widths: Edges<ScaledPixels>,
583}
584
585impl From<Quad> for Primitive {
586    fn from(quad: Quad) -> Self {
587        Primitive::Quad(quad)
588    }
589}
590
591#[derive(Debug, Copy, Clone)]
592#[repr(C)]
593#[expect(missing_docs)]
594pub struct Underline {
595    pub order: DrawOrder,
596    pub pad: u32, // align to 8 bytes
597    pub bounds: Bounds<ScaledPixels>,
598    pub content_mask: ContentMask<ScaledPixels>,
599    pub color: Hsla,
600    pub thickness: ScaledPixels,
601    pub wavy: PaddedBool32,
602}
603
604impl From<Underline> for Primitive {
605    fn from(underline: Underline) -> Self {
606        Primitive::Underline(underline)
607    }
608}
609
610impl From<Shadow> for Primitive {
611    fn from(shadow: Shadow) -> Self {
612        Primitive::Shadow(shadow)
613    }
614}
615
616/// The style of a border.
617#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
618#[repr(C)]
619pub enum BorderStyle {
620    /// A solid border.
621    #[default]
622    Solid = 0,
623    /// A dashed border.
624    Dashed = 1,
625}
626
627/// A data type representing a 2 dimensional transformation that can be applied to an element.
628#[derive(Debug, Clone, Copy, PartialEq)]
629#[repr(C)]
630pub struct TransformationMatrix {
631    /// 2x2 matrix containing rotation and scale,
632    /// stored row-major
633    pub rotation_scale: [[f32; 2]; 2],
634    /// translation vector
635    pub translation: [f32; 2],
636}
637
638impl Eq for TransformationMatrix {}
639
640impl TransformationMatrix {
641    /// The unit matrix, has no effect.
642    pub fn unit() -> Self {
643        Self {
644            rotation_scale: [[1.0, 0.0], [0.0, 1.0]],
645            translation: [0.0, 0.0],
646        }
647    }
648
649    /// Move the origin by a given point
650    pub fn translate(self, point: Point<ScaledPixels>) -> Self {
651        self.compose(Self {
652            rotation_scale: [[1.0, 0.0], [0.0, 1.0]],
653            translation: [point.x.0, point.y.0],
654        })
655    }
656
657    /// Clockwise rotation in radians around the origin
658    pub fn rotate(self, angle: Radians) -> Self {
659        self.compose(Self {
660            rotation_scale: [
661                [angle.0.cos(), -angle.0.sin()],
662                [angle.0.sin(), angle.0.cos()],
663            ],
664            translation: [0.0, 0.0],
665        })
666    }
667
668    /// Scale around the origin
669    pub fn scale(self, size: Size<f32>) -> Self {
670        self.compose(Self {
671            rotation_scale: [[size.width, 0.0], [0.0, size.height]],
672            translation: [0.0, 0.0],
673        })
674    }
675
676    /// Perform matrix multiplication with another transformation
677    /// to produce a new transformation that is the result of
678    /// applying both transformations: first, `other`, then `self`.
679    #[inline]
680    pub fn compose(self, other: TransformationMatrix) -> TransformationMatrix {
681        if other == Self::unit() {
682            return self;
683        }
684        // Perform matrix multiplication
685        TransformationMatrix {
686            rotation_scale: [
687                [
688                    self.rotation_scale[0][0] * other.rotation_scale[0][0]
689                        + self.rotation_scale[0][1] * other.rotation_scale[1][0],
690                    self.rotation_scale[0][0] * other.rotation_scale[0][1]
691                        + self.rotation_scale[0][1] * other.rotation_scale[1][1],
692                ],
693                [
694                    self.rotation_scale[1][0] * other.rotation_scale[0][0]
695                        + self.rotation_scale[1][1] * other.rotation_scale[1][0],
696                    self.rotation_scale[1][0] * other.rotation_scale[0][1]
697                        + self.rotation_scale[1][1] * other.rotation_scale[1][1],
698                ],
699            ],
700            translation: [
701                self.translation[0]
702                    + self.rotation_scale[0][0] * other.translation[0]
703                    + self.rotation_scale[0][1] * other.translation[1],
704                self.translation[1]
705                    + self.rotation_scale[1][0] * other.translation[0]
706                    + self.rotation_scale[1][1] * other.translation[1],
707            ],
708        }
709    }
710
711    /// Apply transformation to a point, mainly useful for debugging
712    pub fn apply(&self, point: Point<Pixels>) -> Point<Pixels> {
713        let input = [point.x.0, point.y.0];
714        let mut output = self.translation;
715        for (i, output_cell) in output.iter_mut().enumerate() {
716            for (k, input_cell) in input.iter().enumerate() {
717                *output_cell += self.rotation_scale[i][k] * *input_cell;
718            }
719        }
720        Point::new(output[0].into(), output[1].into())
721    }
722}
723
724impl Default for TransformationMatrix {
725    fn default() -> Self {
726        Self::unit()
727    }
728}
729
730#[derive(Copy, Clone, Debug)]
731#[repr(C)]
732#[expect(missing_docs)]
733pub struct MonochromeSprite {
734    pub order: DrawOrder,
735    pub pad: u32,
736    pub bounds: Bounds<ScaledPixels>,
737    pub content_mask: ContentMask<ScaledPixels>,
738    pub color: Hsla,
739    pub tile: AtlasTile,
740    pub transformation: TransformationMatrix,
741}
742
743impl From<MonochromeSprite> for Primitive {
744    fn from(sprite: MonochromeSprite) -> Self {
745        Primitive::MonochromeSprite(sprite)
746    }
747}
748
749#[derive(Copy, Clone, Debug)]
750#[repr(C)]
751#[expect(missing_docs)]
752pub struct SubpixelSprite {
753    pub order: DrawOrder,
754    pub pad: u32, // align to 8 bytes
755    pub bounds: Bounds<ScaledPixels>,
756    pub content_mask: ContentMask<ScaledPixels>,
757    pub color: Hsla,
758    pub tile: AtlasTile,
759    pub transformation: TransformationMatrix,
760}
761
762impl From<SubpixelSprite> for Primitive {
763    fn from(sprite: SubpixelSprite) -> Self {
764        Primitive::SubpixelSprite(sprite)
765    }
766}
767
768#[derive(Copy, Clone, Debug)]
769#[repr(C)]
770#[expect(missing_docs)]
771pub struct PolychromeSprite {
772    pub order: DrawOrder,
773    pub pad: u32,
774    pub grayscale: PaddedBool32,
775    pub opacity: f32,
776    pub bounds: Bounds<ScaledPixels>,
777    pub content_mask: ContentMask<ScaledPixels>,
778    pub corner_radii: Corners<ScaledPixels>,
779    pub tile: AtlasTile,
780}
781
782impl From<PolychromeSprite> for Primitive {
783    fn from(sprite: PolychromeSprite) -> Self {
784        Primitive::PolychromeSprite(sprite)
785    }
786}
787
788/// Where a Windows surface's pixels come from. Every variant ends at the same
789/// `ID3D11ShaderResourceView` in the renderer; the difference is who makes it, and on which device.
790///
791/// The texture variant is the ergonomic default for a producer that already renders on the window
792/// renderer's own device: it holds the resource and hands it here, and the renderer — which owns the
793/// device — makes the view. The view variant is the escape for a producer that is the authority on
794/// its own format, plane and mip interpretation and would rather make the view itself. Both are
795/// device-bound, so they must be made on the window renderer's device; the shared variant is the
796/// device-independent one — an NT handle the renderer opens on its own device and views — the
797/// Direct3D counterpart of a dma-buf or an IOSurface, and the one a `surface()` element can carry,
798/// because it can be built before the window's device exists.
799#[cfg(target_os = "windows")]
800#[derive(Clone, Debug, PartialEq, Eq)]
801#[allow(missing_docs)]
802pub enum DirectXSource {
803    /// A texture on the window renderer's own device — the renderer makes the view.
804    Texture(windows::Win32::Graphics::Direct3D11::ID3D11Texture2D),
805    /// A view the producer already made — it is the authority on the interpretation.
806    View(windows::Win32::Graphics::Direct3D11::ID3D11ShaderResourceView),
807    /// A texture shared across devices: the renderer opens the handle on its own device and views it.
808    Shared(SharedDirectXSurface),
809}
810
811/// A Direct3D texture a producer made on *its own* device and shared, plus the fence that orders its
812/// writes before the renderer samples it.
813///
814/// This is the device-independent Direct3D payload: the handle names a resource the renderer opens
815/// with `OpenSharedResource1` and views on the device it draws with, so the producer never needs the
816/// window's device — which is what lets `surface()` carry a Direct3D surface, where the device-bound
817/// [`Texture`](DirectXSource::Texture) and [`View`](DirectXSource::View) cannot.
818#[cfg(target_os = "windows")]
819#[derive(Clone, Debug, PartialEq, Eq)]
820pub struct SharedDirectXSurface {
821    /// The texture's NT handle, as `CreateSharedHandle` produced it.
822    pub texture: windows::Win32::Foundation::HANDLE,
823    /// The fence that orders the producer's writes before the draw, when it signals one; `None` for a
824    /// producer that has already flushed.
825    pub fence: Option<SharedDirectXFence>,
826    /// The texture's width in pixels, for the element's object-fit.
827    pub width: u32,
828    /// The texture's height in pixels, for the element's object-fit.
829    pub height: u32,
830}
831
832/// A producer's shared fence, and the value the renderer waits for before sampling the texture.
833#[cfg(target_os = "windows")]
834#[derive(Clone, Debug, PartialEq, Eq)]
835pub struct SharedDirectXFence {
836    /// The fence's NT handle, opened on the renderer's device with `OpenSharedFence`.
837    pub handle: windows::Win32::Foundation::HANDLE,
838    /// The value the renderer waits for on the device's context before it samples the texture.
839    pub value: u64,
840}
841
842/// A source of a surface's content: pixels GPUI did not draw, arriving on the renderer's own
843/// device or from another one.
844///
845/// The variant is a cfg-gated payload, exactly as the field it replaces was, so the engine names
846/// only the transport it is compiled against and stays free of every graphics API but that one. A
847/// same-device texture and a cross-process handle are the same value here: both reach the renderer
848/// as a view it samples, and the renderer cannot tell how the view was made.
849#[derive(Clone, Debug, PartialEq, Eq)]
850#[allow(missing_docs)]
851pub enum SurfaceSource {
852    /// A CoreVideo image buffer, backed by an IOSurface.
853    #[cfg(target_os = "macos")]
854    CoreVideo(core_video::pixel_buffer::CVPixelBuffer),
855    /// The pixels of a Direct3D 11 surface, as either a texture the renderer views or a view a
856    /// producer already made. [`DirectXSource`] carries both and explains why both are offered.
857    #[cfg(target_os = "windows")]
858    DirectX(DirectXSource),
859    /// A dma-buf: an fd plus a DRM fourcc, modifier, stride and offset.
860    #[cfg(target_os = "linux")]
861    DmaBuf(crate::dmabuf::DmaBufHandle),
862}
863
864#[cfg(target_os = "macos")]
865impl From<core_video::pixel_buffer::CVPixelBuffer> for SurfaceSource {
866    fn from(image_buffer: core_video::pixel_buffer::CVPixelBuffer) -> Self {
867        SurfaceSource::CoreVideo(image_buffer)
868    }
869}
870
871#[cfg(target_os = "windows")]
872impl From<windows::Win32::Graphics::Direct3D11::ID3D11ShaderResourceView> for DirectXSource {
873    fn from(view: windows::Win32::Graphics::Direct3D11::ID3D11ShaderResourceView) -> Self {
874        DirectXSource::View(view)
875    }
876}
877
878#[cfg(target_os = "windows")]
879impl From<windows::Win32::Graphics::Direct3D11::ID3D11Texture2D> for DirectXSource {
880    fn from(texture: windows::Win32::Graphics::Direct3D11::ID3D11Texture2D) -> Self {
881        DirectXSource::Texture(texture)
882    }
883}
884
885#[cfg(target_os = "windows")]
886impl From<windows::Win32::Graphics::Direct3D11::ID3D11ShaderResourceView> for SurfaceSource {
887    fn from(view: windows::Win32::Graphics::Direct3D11::ID3D11ShaderResourceView) -> Self {
888        SurfaceSource::DirectX(view.into())
889    }
890}
891
892#[cfg(target_os = "windows")]
893impl From<windows::Win32::Graphics::Direct3D11::ID3D11Texture2D> for SurfaceSource {
894    fn from(texture: windows::Win32::Graphics::Direct3D11::ID3D11Texture2D) -> Self {
895        SurfaceSource::DirectX(texture.into())
896    }
897}
898
899#[cfg(target_os = "linux")]
900impl From<crate::dmabuf::DmaBufHandle> for SurfaceSource {
901    fn from(handle: crate::dmabuf::DmaBufHandle) -> Self {
902        SurfaceSource::DmaBuf(handle)
903    }
904}
905
906#[derive(Clone, Debug)]
907#[allow(missing_docs)]
908pub struct PaintSurface {
909    pub order: DrawOrder,
910    pub bounds: Bounds<ScaledPixels>,
911    pub content_mask: ContentMask<ScaledPixels>,
912    /// The pixels to composite, and the transport they arrived on.
913    pub source: SurfaceSource,
914}
915
916impl PaintSurface {
917    /// Encode this surface into the quad instance record the backends' shaders read.
918    ///
919    /// The geometry and the content-mask clip are the quad path's rather than a second
920    /// implementation of them, which is the whole reason the surface rides `PaintSurface` and not
921    /// a primitive of its own. The surface carries no opacity, y-flip or corner radii yet: the
922    /// element sets no rounded corners, and the payload is sampled straight through, so the record
923    /// is the plain one a quad with no styling would build.
924    pub fn to_quad_record(&self) -> Quad {
925        let mut background = Background::default();
926        background.solid = Hsla {
927            h: 0.0,
928            s: 0.0,
929            l: 0.0,
930            a: 1.0,
931        };
932
933        Quad {
934            order: self.order,
935            border_style: BorderStyle::Solid,
936            bounds: self.bounds,
937            content_mask: self.content_mask,
938            background,
939            border_color: Hsla::default(),
940            corner_radii: Corners::default(),
941            border_widths: Edges::default(),
942        }
943    }
944}
945
946impl From<PaintSurface> for Primitive {
947    fn from(surface: PaintSurface) -> Self {
948        Primitive::Surface(surface)
949    }
950}
951
952impl From<CustomRenderPrimitive> for Primitive {
953    fn from(custom: CustomRenderPrimitive) -> Self {
954        Primitive::Custom(custom)
955    }
956}
957
958#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
959#[expect(missing_docs)]
960pub struct PathId(pub usize);
961
962/// A line made up of a series of vertices and control points.
963#[derive(Clone, Debug)]
964#[expect(missing_docs)]
965pub struct Path<P: Clone + Debug + Default + PartialEq> {
966    pub id: PathId,
967    pub order: DrawOrder,
968    pub bounds: Bounds<P>,
969    pub content_mask: ContentMask<P>,
970    pub vertices: Vec<PathVertex<P>>,
971    pub color: Background,
972    start: Point<P>,
973    current: Point<P>,
974    contour_count: usize,
975}
976
977impl Path<Pixels> {
978    /// Create a new path with the given starting point.
979    pub fn new(start: Point<Pixels>) -> Self {
980        Self {
981            id: PathId(0),
982            order: DrawOrder::default(),
983            vertices: Vec::new(),
984            start,
985            current: start,
986            bounds: Bounds {
987                origin: start,
988                size: Default::default(),
989            },
990            content_mask: Default::default(),
991            color: Default::default(),
992            contour_count: 0,
993        }
994    }
995
996    /// Scale this path by the given factor.
997    pub fn scale(&self, factor: f32) -> Path<ScaledPixels> {
998        Path {
999            id: self.id,
1000            order: self.order,
1001            bounds: self.bounds.scale(factor),
1002            content_mask: self.content_mask.scale(factor),
1003            vertices: self
1004                .vertices
1005                .iter()
1006                .map(|vertex| vertex.scale(factor))
1007                .collect(),
1008            start: self.start.map(|start| start.scale(factor)),
1009            current: self.current.scale(factor),
1010            contour_count: self.contour_count,
1011            color: self.color,
1012        }
1013    }
1014
1015    /// Move the start, current point to the given point.
1016    pub fn move_to(&mut self, to: Point<Pixels>) {
1017        self.contour_count += 1;
1018        self.start = to;
1019        self.current = to;
1020    }
1021
1022    /// Draw a straight line from the current point to the given point.
1023    pub fn line_to(&mut self, to: Point<Pixels>) {
1024        self.contour_count += 1;
1025        if self.contour_count > 1 {
1026            self.push_triangle(
1027                (self.start, self.current, to),
1028                (point(0., 1.), point(0., 1.), point(0., 1.)),
1029            );
1030        }
1031        self.current = to;
1032    }
1033
1034    /// Draw a curve from the current point to the given point, using the given control point.
1035    pub fn curve_to(&mut self, to: Point<Pixels>, ctrl: Point<Pixels>) {
1036        self.contour_count += 1;
1037        if self.contour_count > 1 {
1038            self.push_triangle(
1039                (self.start, self.current, to),
1040                (point(0., 1.), point(0., 1.), point(0., 1.)),
1041            );
1042        }
1043
1044        self.push_triangle(
1045            (self.current, ctrl, to),
1046            (point(0., 0.), point(0.5, 0.), point(1., 1.)),
1047        );
1048        self.current = to;
1049    }
1050
1051    /// Push a triangle to the Path.
1052    pub fn push_triangle(
1053        &mut self,
1054        xy: (Point<Pixels>, Point<Pixels>, Point<Pixels>),
1055        st: (Point<f32>, Point<f32>, Point<f32>),
1056    ) {
1057        self.bounds = self
1058            .bounds
1059            .union(&Bounds {
1060                origin: xy.0,
1061                size: Default::default(),
1062            })
1063            .union(&Bounds {
1064                origin: xy.1,
1065                size: Default::default(),
1066            })
1067            .union(&Bounds {
1068                origin: xy.2,
1069                size: Default::default(),
1070            });
1071
1072        self.vertices.push(PathVertex {
1073            xy_position: xy.0,
1074            st_position: st.0,
1075            content_mask: Default::default(),
1076        });
1077        self.vertices.push(PathVertex {
1078            xy_position: xy.1,
1079            st_position: st.1,
1080            content_mask: Default::default(),
1081        });
1082        self.vertices.push(PathVertex {
1083            xy_position: xy.2,
1084            st_position: st.2,
1085            content_mask: Default::default(),
1086        });
1087    }
1088}
1089
1090impl<T> Path<T>
1091where
1092    T: Clone + Debug + Default + PartialEq + PartialOrd + Add<T, Output = T> + Sub<Output = T>,
1093{
1094    #[allow(unused)]
1095    #[expect(missing_docs)]
1096    pub fn clipped_bounds(&self) -> Bounds<T> {
1097        self.bounds.intersect(&self.content_mask.bounds)
1098    }
1099}
1100
1101impl From<Path<ScaledPixels>> for Primitive {
1102    fn from(path: Path<ScaledPixels>) -> Self {
1103        Primitive::Path(path)
1104    }
1105}
1106
1107#[derive(Clone, Debug)]
1108#[repr(C)]
1109#[expect(missing_docs)]
1110pub struct PathVertex<P: Clone + Debug + Default + PartialEq> {
1111    pub xy_position: Point<P>,
1112    pub st_position: Point<f32>,
1113    pub content_mask: ContentMask<P>,
1114}
1115
1116#[expect(missing_docs)]
1117impl PathVertex<Pixels> {
1118    pub fn scale(&self, factor: f32) -> PathVertex<ScaledPixels> {
1119        PathVertex {
1120            xy_position: self.xy_position.scale(factor),
1121            st_position: self.st_position,
1122            content_mask: self.content_mask.scale(factor),
1123        }
1124    }
1125}