Checkpoint

Antonio Scandurra created

Change summary

crates/gpui3/build.rs                           |   2 
crates/gpui3/src/platform.rs                    |  32 +
crates/gpui3/src/platform/mac/metal_atlas.rs    | 118 ++++--
crates/gpui3/src/platform/mac/metal_renderer.rs |  26 +
crates/gpui3/src/platform/mac/window.rs         |   6 
crates/gpui3/src/scene.rs                       | 319 ++++++++++++------
crates/gpui3/src/style.rs                       |  59 --
crates/gpui3/src/window.rs                      | 159 ++++++--
8 files changed, 463 insertions(+), 258 deletions(-)

Detailed changes

crates/gpui3/build.rs 🔗

@@ -47,7 +47,7 @@ fn generate_shader_bindings() -> PathBuf {
         "Pixels".into(),
         "PointF".into(),
         "Hsla".into(),
-        "ScaledContentMask".into(),
+        "ContentMask".into(),
         "Uniforms".into(),
         "AtlasTile".into(),
         "ShadowInputIndex".into(),

crates/gpui3/src/platform.rs 🔗

@@ -40,7 +40,7 @@ pub(crate) fn current_platform() -> Arc<dyn Platform> {
     Arc::new(MacPlatform::new())
 }
 
-pub trait Platform: 'static {
+pub(crate) trait Platform: 'static {
     fn executor(&self) -> Executor;
     fn display_linker(&self) -> Arc<dyn PlatformDisplayLinker>;
     fn text_system(&self) -> Arc<dyn PlatformTextSystem>;
@@ -113,7 +113,7 @@ impl Debug for DisplayId {
 
 unsafe impl Send for DisplayId {}
 
-pub trait PlatformWindow {
+pub(crate) trait PlatformWindow {
     fn bounds(&self) -> WindowBounds;
     fn content_size(&self) -> Size<Pixels>;
     fn scale_factor(&self) -> f32;
@@ -194,11 +194,17 @@ pub enum AtlasKey {
 }
 
 impl AtlasKey {
-    pub fn is_monochrome(&self) -> bool {
+    pub(crate) fn texture_kind(&self) -> AtlasTextureKind {
         match self {
-            AtlasKey::Glyph(params) => !params.is_emoji,
-            AtlasKey::Svg(_) => true,
-            AtlasKey::Image(_) => false,
+            AtlasKey::Glyph(params) => {
+                if params.is_emoji {
+                    AtlasTextureKind::Polychrome
+                } else {
+                    AtlasTextureKind::Monochrome
+                }
+            }
+            AtlasKey::Svg(_) => AtlasTextureKind::Monochrome,
+            AtlasKey::Image(_) => AtlasTextureKind::Polychrome,
         }
     }
 }
@@ -241,7 +247,19 @@ pub struct AtlasTile {
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
 #[repr(C)]
-pub(crate) struct AtlasTextureId(pub(crate) u32); // We use u32 instead of usize for Metal Shader Language compatibility
+pub(crate) struct AtlasTextureId {
+    // We use u32 instead of usize for Metal Shader Language compatibility
+    pub(crate) index: u32,
+    pub(crate) kind: AtlasTextureKind,
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+#[repr(C)]
+pub(crate) enum AtlasTextureKind {
+    Monochrome = 0,
+    Polychrome = 1,
+    Path = 2,
+}
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
 #[repr(C)]

crates/gpui3/src/platform/mac/metal_atlas.rs 🔗

@@ -1,14 +1,14 @@
-use std::borrow::Cow;
-
 use crate::{
-    AtlasKey, AtlasTextureId, AtlasTile, Bounds, DevicePixels, PlatformAtlas, Point, Size,
+    AtlasKey, AtlasTextureId, AtlasTextureKind, AtlasTile, Bounds, DevicePixels, PlatformAtlas,
+    Point, Size,
 };
-use anyhow::{anyhow, Result};
+use anyhow::Result;
 use collections::HashMap;
 use derive_more::{Deref, DerefMut};
 use etagere::BucketedAtlasAllocator;
 use metal::Device;
 use parking_lot::Mutex;
+use std::borrow::Cow;
 
 pub struct MetalAtlas(Mutex<MetalAtlasState>);
 
@@ -16,19 +16,31 @@ impl MetalAtlas {
     pub fn new(device: Device) -> Self {
         MetalAtlas(Mutex::new(MetalAtlasState {
             device: AssertSend(device),
-            textures: Default::default(),
+            monochrome_textures: Default::default(),
+            polychrome_textures: Default::default(),
+            path_textures: Default::default(),
             tiles_by_key: Default::default(),
         }))
     }
 
-    pub(crate) fn texture(&self, id: AtlasTextureId) -> metal::Texture {
-        self.0.lock().textures[id.0 as usize].metal_texture.clone()
+    pub(crate) fn metal_texture(&self, id: AtlasTextureId) -> metal::Texture {
+        self.0.lock().texture(id).metal_texture.clone()
+    }
+
+    pub(crate) fn allocate(
+        &self,
+        size: Size<DevicePixels>,
+        texture_kind: AtlasTextureKind,
+    ) -> AtlasTile {
+        self.0.lock().allocate(size, texture_kind)
     }
 }
 
 struct MetalAtlasState {
     device: AssertSend<Device>,
-    textures: Vec<MetalAtlasTexture>,
+    monochrome_textures: Vec<MetalAtlasTexture>,
+    polychrome_textures: Vec<MetalAtlasTexture>,
+    path_textures: Vec<MetalAtlasTexture>,
     tiles_by_key: HashMap<AtlasKey, AtlasTile>,
 }
 
@@ -43,23 +55,9 @@ impl PlatformAtlas for MetalAtlas {
             return Ok(tile.clone());
         } else {
             let (size, bytes) = build()?;
-            let tile = lock
-                .textures
-                .iter_mut()
-                .rev()
-                .find_map(|texture| {
-                    if texture.monochrome == key.is_monochrome() {
-                        texture.upload(size, &bytes)
-                    } else {
-                        None
-                    }
-                })
-                .or_else(|| {
-                    let texture = lock.push_texture(size, key.is_monochrome());
-                    texture.upload(size, &bytes)
-                })
-                .ok_or_else(|| anyhow!("could not allocate in new texture"))?;
-            lock.tiles_by_key.insert(key.clone(), tile.clone());
+            let tile = lock.allocate(size, key.texture_kind());
+            let texture = lock.texture(tile.texture_id);
+            texture.upload(tile.bounds, &bytes);
             Ok(tile)
         }
     }
@@ -70,10 +68,26 @@ impl PlatformAtlas for MetalAtlas {
 }
 
 impl MetalAtlasState {
+    fn allocate(&mut self, size: Size<DevicePixels>, texture_kind: AtlasTextureKind) -> AtlasTile {
+        let textures = match texture_kind {
+            AtlasTextureKind::Monochrome => &mut self.monochrome_textures,
+            AtlasTextureKind::Polychrome => &mut self.polychrome_textures,
+            AtlasTextureKind::Path => &mut self.path_textures,
+        };
+        textures
+            .iter_mut()
+            .rev()
+            .find_map(|texture| texture.allocate(size))
+            .unwrap_or_else(|| {
+                let texture = self.push_texture(size, texture_kind);
+                texture.allocate(size).unwrap()
+            })
+    }
+
     fn push_texture(
         &mut self,
         min_size: Size<DevicePixels>,
-        monochrome: bool,
+        kind: AtlasTextureKind,
     ) -> &mut MetalAtlasTexture {
         const DEFAULT_ATLAS_SIZE: Size<DevicePixels> = Size {
             width: DevicePixels(1024),
@@ -84,21 +98,38 @@ impl MetalAtlasState {
         let texture_descriptor = metal::TextureDescriptor::new();
         texture_descriptor.set_width(size.width.into());
         texture_descriptor.set_height(size.height.into());
-        if monochrome {
-            texture_descriptor.set_pixel_format(metal::MTLPixelFormat::A8Unorm);
-        } else {
-            texture_descriptor.set_pixel_format(metal::MTLPixelFormat::BGRA8Unorm);
-        }
+        let pixel_format = match kind {
+            AtlasTextureKind::Monochrome => metal::MTLPixelFormat::A8Unorm,
+            AtlasTextureKind::Polychrome => metal::MTLPixelFormat::BGRA8Unorm,
+            AtlasTextureKind::Path => metal::MTLPixelFormat::R16Float,
+        };
+        texture_descriptor.set_pixel_format(pixel_format);
         let metal_texture = self.device.new_texture(&texture_descriptor);
 
+        let textures = match kind {
+            AtlasTextureKind::Monochrome => &mut self.monochrome_textures,
+            AtlasTextureKind::Polychrome => &mut self.polychrome_textures,
+            AtlasTextureKind::Path => &mut self.path_textures,
+        };
         let atlas_texture = MetalAtlasTexture {
-            id: AtlasTextureId(self.textures.len() as u32),
+            id: AtlasTextureId {
+                index: textures.len() as u32,
+                kind,
+            },
             allocator: etagere::BucketedAtlasAllocator::new(size.into()),
             metal_texture: AssertSend(metal_texture),
-            monochrome,
         };
-        self.textures.push(atlas_texture);
-        self.textures.last_mut().unwrap()
+        textures.push(atlas_texture);
+        textures.last_mut().unwrap()
+    }
+
+    fn texture(&self, id: AtlasTextureId) -> &MetalAtlasTexture {
+        let textures = match id.kind {
+            crate::AtlasTextureKind::Monochrome => &self.monochrome_textures,
+            crate::AtlasTextureKind::Polychrome => &self.polychrome_textures,
+            crate::AtlasTextureKind::Path => &self.path_textures,
+        };
+        &textures[id.index as usize]
     }
 }
 
@@ -106,11 +137,10 @@ struct MetalAtlasTexture {
     id: AtlasTextureId,
     allocator: BucketedAtlasAllocator,
     metal_texture: AssertSend<metal::Texture>,
-    monochrome: bool,
 }
 
 impl MetalAtlasTexture {
-    fn upload(&mut self, size: Size<DevicePixels>, bytes: &[u8]) -> Option<AtlasTile> {
+    fn allocate(&mut self, size: Size<DevicePixels>) -> Option<AtlasTile> {
         let allocation = self.allocator.allocate(size.into())?;
         let tile = AtlasTile {
             texture_id: self.id,
@@ -120,20 +150,22 @@ impl MetalAtlasTexture {
                 size,
             },
         };
+        Some(tile)
+    }
 
+    fn upload(&self, bounds: Bounds<DevicePixels>, bytes: &[u8]) {
         let region = metal::MTLRegion::new_2d(
-            tile.bounds.origin.x.into(),
-            tile.bounds.origin.y.into(),
-            tile.bounds.size.width.into(),
-            tile.bounds.size.height.into(),
+            bounds.origin.x.into(),
+            bounds.origin.y.into(),
+            bounds.size.width.into(),
+            bounds.size.height.into(),
         );
         self.metal_texture.replace_region(
             region,
             0,
             bytes.as_ptr() as *const _,
-            u32::from(tile.bounds.size.width.to_bytes(self.bytes_per_pixel())) as u64,
+            u32::from(bounds.size.width.to_bytes(self.bytes_per_pixel())) as u64,
         );
-        Some(tile)
     }
 
     fn bytes_per_pixel(&self) -> u8 {

crates/gpui3/src/platform/mac/metal_renderer.rs 🔗

@@ -1,12 +1,14 @@
 use crate::{
-    point, size, AtlasTextureId, DevicePixels, MetalAtlas, MonochromeSprite, PolychromeSprite,
-    PrimitiveBatch, Quad, Scene, Shadow, Size, Underline,
+    point, size, AtlasTextureId, AtlasTextureKind, AtlasTile, DevicePixels, MetalAtlas,
+    MonochromeSprite, PathId, PolychromeSprite, PrimitiveBatch, Quad, Scene, Shadow, Size,
+    Underline,
 };
 use cocoa::{
     base::{NO, YES},
     foundation::NSUInteger,
     quartzcore::AutoresizingMask,
 };
+use collections::HashMap;
 use metal::{CommandQueue, MTLPixelFormat, MTLResourceOptions, NSRange};
 use objc::{self, msg_send, sel, sel_impl};
 use std::{ffi::c_void, mem, ptr, sync::Arc};
@@ -14,7 +16,7 @@ use std::{ffi::c_void, mem, ptr, sync::Arc};
 const SHADERS_METALLIB: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/shaders.metallib"));
 const INSTANCE_BUFFER_SIZE: usize = 8192 * 1024; // This is an arbitrary decision. There's probably a more optimal value.
 
-pub struct MetalRenderer {
+pub(crate) struct MetalRenderer {
     layer: metal::MetalLayer,
     command_queue: CommandQueue,
     shadows_pipeline_state: metal::RenderPipelineState,
@@ -150,7 +152,7 @@ impl MetalRenderer {
         &self.sprite_atlas
     }
 
-    pub fn draw(&mut self, scene: &mut Scene) {
+    pub fn draw(&mut self, scene: &Scene) {
         let layer = self.layer.clone();
         let viewport_size = layer.drawable_size();
         let viewport_size: Size<DevicePixels> = size(
@@ -192,6 +194,15 @@ impl MetalRenderer {
         });
 
         let mut instance_offset = 0;
+
+        let mut path_tiles: HashMap<PathId, AtlasTile> = HashMap::default();
+        for path in scene.paths() {
+            let tile = self
+                .sprite_atlas
+                .allocate(path.bounds.size.map(Into::into), AtlasTextureKind::Path);
+            path_tiles.insert(path.id, tile);
+        }
+
         for batch in scene.batches() {
             match batch {
                 PrimitiveBatch::Shadows(shadows) => {
@@ -205,6 +216,9 @@ impl MetalRenderer {
                 PrimitiveBatch::Quads(quads) => {
                     self.draw_quads(quads, &mut instance_offset, viewport_size, command_encoder);
                 }
+                PrimitiveBatch::Paths(paths) => {
+                    // self.draw_paths(paths, &mut instance_offset, viewport_size, command_encoder);
+                }
                 PrimitiveBatch::Underlines(underlines) => {
                     self.draw_underlines(
                         underlines,
@@ -441,7 +455,7 @@ impl MetalRenderer {
         }
         align_offset(offset);
 
-        let texture = self.sprite_atlas.texture(texture_id);
+        let texture = self.sprite_atlas.metal_texture(texture_id);
         let texture_size = size(
             DevicePixels(texture.width() as i32),
             DevicePixels(texture.height() as i32),
@@ -512,7 +526,7 @@ impl MetalRenderer {
         }
         align_offset(offset);
 
-        let texture = self.sprite_atlas.texture(texture_id);
+        let texture = self.sprite_atlas.metal_texture(texture_id);
         let texture_size = size(
             DevicePixels(texture.width() as i32),
             DevicePixels(texture.height() as i32),

crates/gpui3/src/platform/mac/window.rs 🔗

@@ -911,7 +911,7 @@ impl PlatformWindow for MacWindow {
         }
     }
 
-    fn draw(&self, scene: crate::Scene) {
+    fn draw(&self, scene: Scene) {
         let mut this = self.0.lock();
         this.scene_to_render = Some(scene);
         unsafe {
@@ -1395,8 +1395,8 @@ extern "C" fn display_layer(this: &Object, _: Sel, _: id) {
     unsafe {
         let window_state = get_window_state(this);
         let mut window_state = window_state.as_ref().lock();
-        if let Some(mut scene) = window_state.scene_to_render.take() {
-            window_state.renderer.draw(&mut scene);
+        if let Some(scene) = window_state.scene_to_render.take() {
+            window_state.renderer.draw(&scene);
         }
     }
 }

crates/gpui3/src/scene.rs 🔗

@@ -1,16 +1,12 @@
 use crate::{
-    point, px, AtlasTextureId, AtlasTile, Bounds, Corners, Edges, Hsla, Pixels, Point,
-    ScaledContentMask, ScaledPixels,
+    point, AtlasTextureId, AtlasTile, Bounds, ContentMask, Corners, Edges, Hsla, Pixels, Point,
+    ScaledPixels,
 };
 use collections::BTreeMap;
 use etagere::euclid::{Point3D, Vector3D};
 use plane_split::{BspSplitter, Polygon as BspPolygon};
 use smallvec::SmallVec;
-use std::{
-    iter::Peekable,
-    mem, slice,
-    sync::atomic::{AtomicU32, Ordering::SeqCst},
-};
+use std::{fmt::Debug, iter::Peekable, mem, slice};
 
 // Exported to metal
 pub type PointF = Point<f32>;
@@ -18,94 +14,57 @@ pub type StackingOrder = SmallVec<[u32; 16]>;
 pub type LayerId = u32;
 pub type DrawOrder = u32;
 
-#[derive(Debug)]
-pub struct Scene {
-    pub(crate) scale_factor: f32,
-    pub(crate) layers: BTreeMap<StackingOrder, LayerId>,
-    pub shadows: Vec<Shadow>,
-    pub quads: Vec<Quad>,
-    pub underlines: Vec<Underline>,
-    pub monochrome_sprites: Vec<MonochromeSprite>,
-    pub polychrome_sprites: Vec<PolychromeSprite>,
-}
-
-impl Scene {
-    pub fn new(scale_factor: f32) -> Scene {
-        Scene {
-            scale_factor,
-            layers: BTreeMap::new(),
+pub(crate) struct SceneBuilder {
+    layers_by_order: BTreeMap<StackingOrder, LayerId>,
+    splitter: BspSplitter<(PrimitiveKind, usize)>,
+    shadows: Vec<Shadow>,
+    quads: Vec<Quad>,
+    paths: Vec<Path<ScaledPixels>>,
+    underlines: Vec<Underline>,
+    monochrome_sprites: Vec<MonochromeSprite>,
+    polychrome_sprites: Vec<PolychromeSprite>,
+}
+
+impl SceneBuilder {
+    pub fn new() -> SceneBuilder {
+        SceneBuilder {
+            layers_by_order: BTreeMap::new(),
+            splitter: BspSplitter::new(),
             shadows: Vec::new(),
             quads: Vec::new(),
+            paths: Vec::new(),
             underlines: Vec::new(),
             monochrome_sprites: Vec::new(),
             polychrome_sprites: Vec::new(),
         }
     }
 
-    pub fn take(&mut self) -> Scene {
-        Scene {
-            scale_factor: self.scale_factor,
-            layers: mem::take(&mut self.layers),
-            shadows: mem::take(&mut self.shadows),
-            quads: mem::take(&mut self.quads),
-            underlines: mem::take(&mut self.underlines),
-            monochrome_sprites: mem::take(&mut self.monochrome_sprites),
-            polychrome_sprites: mem::take(&mut self.polychrome_sprites),
-        }
-    }
-
-    pub fn insert(&mut self, layer_id: StackingOrder, primitive: impl Into<Primitive>) {
-        let next_id = self.layers.len() as LayerId;
-        let layer_id = *self.layers.entry(layer_id).or_insert(next_id);
-        let primitive = primitive.into();
-        match primitive {
-            Primitive::Shadow(mut shadow) => {
-                shadow.order = layer_id;
-                self.shadows.push(shadow);
-            }
-            Primitive::Quad(mut quad) => {
-                quad.order = layer_id;
-                self.quads.push(quad);
-            }
-            Primitive::Underline(mut underline) => {
-                underline.order = layer_id;
-                self.underlines.push(underline);
-            }
-            Primitive::MonochromeSprite(mut sprite) => {
-                sprite.order = layer_id;
-                self.monochrome_sprites.push(sprite);
-            }
-            Primitive::PolychromeSprite(mut sprite) => {
-                sprite.order = layer_id;
-                self.polychrome_sprites.push(sprite);
-            }
-        }
-    }
-
-    pub(crate) fn batches(&mut self) -> impl Iterator<Item = PrimitiveBatch> {
+    pub fn build(&mut self) -> Scene {
         // Map each layer id to a float between 0. and 1., with 1. closer to the viewer.
-        let mut layer_z_values = vec![0.; self.layers.len()];
-        for (ix, layer_id) in self.layers.values().enumerate() {
-            layer_z_values[*layer_id as usize] = ix as f32 / self.layers.len() as f32;
+        let mut layer_z_values = vec![0.; self.layers_by_order.len()];
+        for (ix, layer_id) in self.layers_by_order.values().enumerate() {
+            layer_z_values[*layer_id as usize] = ix as f32 / self.layers_by_order.len() as f32;
         }
+        self.layers_by_order.clear();
 
         // Add all primitives to the BSP splitter to determine draw order
-        // todo!("reuse the same splitter")
-        let mut splitter = BspSplitter::new();
+        self.splitter.reset();
 
         for (ix, shadow) in self.shadows.iter().enumerate() {
             let z = layer_z_values[shadow.order as LayerId as usize];
-            splitter.add(shadow.bounds.to_bsp_polygon(z, (PrimitiveKind::Shadow, ix)));
+            self.splitter
+                .add(shadow.bounds.to_bsp_polygon(z, (PrimitiveKind::Shadow, ix)));
         }
 
         for (ix, quad) in self.quads.iter().enumerate() {
             let z = layer_z_values[quad.order as LayerId as usize];
-            splitter.add(quad.bounds.to_bsp_polygon(z, (PrimitiveKind::Quad, ix)));
+            self.splitter
+                .add(quad.bounds.to_bsp_polygon(z, (PrimitiveKind::Quad, ix)));
         }
 
         for (ix, underline) in self.underlines.iter().enumerate() {
             let z = layer_z_values[underline.order as LayerId as usize];
-            splitter.add(
+            self.splitter.add(
                 underline
                     .bounds
                     .to_bsp_polygon(z, (PrimitiveKind::Underline, ix)),
@@ -114,7 +73,7 @@ impl Scene {
 
         for (ix, monochrome_sprite) in self.monochrome_sprites.iter().enumerate() {
             let z = layer_z_values[monochrome_sprite.order as LayerId as usize];
-            splitter.add(
+            self.splitter.add(
                 monochrome_sprite
                     .bounds
                     .to_bsp_polygon(z, (PrimitiveKind::MonochromeSprite, ix)),
@@ -123,7 +82,7 @@ impl Scene {
 
         for (ix, polychrome_sprite) in self.polychrome_sprites.iter().enumerate() {
             let z = layer_z_values[polychrome_sprite.order as LayerId as usize];
-            splitter.add(
+            self.splitter.add(
                 polychrome_sprite
                     .bounds
                     .to_bsp_polygon(z, (PrimitiveKind::PolychromeSprite, ix)),
@@ -132,10 +91,16 @@ impl Scene {
 
         // Sort all polygons, then reassign the order field of each primitive to `draw_order`
         // We need primitives to be repr(C), hence the weird reuse of the order field for two different types.
-        for (draw_order, polygon) in splitter.sort(Vector3D::new(0., 0., 1.)).iter().enumerate() {
+        for (draw_order, polygon) in self
+            .splitter
+            .sort(Vector3D::new(0., 0., 1.))
+            .iter()
+            .enumerate()
+        {
             match polygon.anchor {
                 (PrimitiveKind::Shadow, ix) => self.shadows[ix].order = draw_order as DrawOrder,
                 (PrimitiveKind::Quad, ix) => self.quads[ix].order = draw_order as DrawOrder,
+                (PrimitiveKind::Path, ix) => self.paths[ix].order = draw_order as DrawOrder,
                 (PrimitiveKind::Underline, ix) => {
                     self.underlines[ix].order = draw_order as DrawOrder
                 }
@@ -148,13 +113,78 @@ impl Scene {
             }
         }
 
-        // Sort the primitives
         self.shadows.sort_unstable();
         self.quads.sort_unstable();
+        self.paths.sort_unstable();
         self.underlines.sort_unstable();
         self.monochrome_sprites.sort_unstable();
         self.polychrome_sprites.sort_unstable();
 
+        Scene {
+            shadows: mem::take(&mut self.shadows),
+            quads: mem::take(&mut self.quads),
+            paths: mem::take(&mut self.paths),
+            underlines: mem::take(&mut self.underlines),
+            monochrome_sprites: mem::take(&mut self.monochrome_sprites),
+            polychrome_sprites: mem::take(&mut self.polychrome_sprites),
+        }
+    }
+
+    pub fn insert(&mut self, order: &StackingOrder, primitive: impl Into<Primitive>) {
+        let layer_id = if let Some(layer_id) = self.layers_by_order.get(order) {
+            *layer_id
+        } else {
+            let next_id = self.layers_by_order.len() as LayerId;
+            self.layers_by_order.insert(order.clone(), next_id);
+            next_id
+        };
+
+        let primitive = primitive.into();
+        match primitive {
+            Primitive::Shadow(mut shadow) => {
+                shadow.order = layer_id;
+                self.shadows.push(shadow);
+            }
+            Primitive::Quad(mut quad) => {
+                quad.order = layer_id;
+                self.quads.push(quad);
+            }
+            Primitive::Path(mut path) => {
+                path.order = layer_id;
+                path.id = PathId(self.paths.len());
+                self.paths.push(path);
+            }
+            Primitive::Underline(mut underline) => {
+                underline.order = layer_id;
+                self.underlines.push(underline);
+            }
+            Primitive::MonochromeSprite(mut sprite) => {
+                sprite.order = layer_id;
+                self.monochrome_sprites.push(sprite);
+            }
+            Primitive::PolychromeSprite(mut sprite) => {
+                sprite.order = layer_id;
+                self.polychrome_sprites.push(sprite);
+            }
+        }
+    }
+}
+
+pub(crate) struct Scene {
+    pub shadows: Vec<Shadow>,
+    pub quads: Vec<Quad>,
+    pub paths: Vec<Path<ScaledPixels>>,
+    pub underlines: Vec<Underline>,
+    pub monochrome_sprites: Vec<MonochromeSprite>,
+    pub polychrome_sprites: Vec<PolychromeSprite>,
+}
+
+impl Scene {
+    pub fn paths(&self) -> impl Iterator<Item = &Path<ScaledPixels>> {
+        self.paths.iter()
+    }
+
+    pub fn batches(&self) -> impl Iterator<Item = PrimitiveBatch> {
         BatchIterator {
             shadows: &self.shadows,
             shadows_start: 0,
@@ -162,6 +192,9 @@ impl Scene {
             quads: &self.quads,
             quads_start: 0,
             quads_iter: self.quads.iter().peekable(),
+            paths: &self.paths,
+            paths_start: 0,
+            paths_iter: self.paths.iter().peekable(),
             underlines: &self.underlines,
             underlines_start: 0,
             underlines_iter: self.underlines.iter().peekable(),
@@ -176,12 +209,15 @@ impl Scene {
 }
 
 struct BatchIterator<'a> {
-    quads: &'a [Quad],
-    quads_start: usize,
-    quads_iter: Peekable<slice::Iter<'a, Quad>>,
     shadows: &'a [Shadow],
     shadows_start: usize,
     shadows_iter: Peekable<slice::Iter<'a, Shadow>>,
+    quads: &'a [Quad],
+    quads_start: usize,
+    quads_iter: Peekable<slice::Iter<'a, Quad>>,
+    paths: &'a [Path<ScaledPixels>],
+    paths_start: usize,
+    paths_iter: Peekable<slice::Iter<'a, Path<ScaledPixels>>>,
     underlines: &'a [Underline],
     underlines_start: usize,
     underlines_iter: Peekable<slice::Iter<'a, Underline>>,
@@ -255,6 +291,19 @@ impl<'a> Iterator for BatchIterator<'a> {
                 self.quads_start = quads_end;
                 Some(PrimitiveBatch::Quads(&self.quads[quads_start..quads_end]))
             }
+            PrimitiveKind::Path => {
+                let paths_start = self.paths_start;
+                let mut paths_end = paths_start;
+                while self
+                    .paths_iter
+                    .next_if(|path| path.order <= max_order)
+                    .is_some()
+                {
+                    paths_end += 1;
+                }
+                self.paths_start = paths_end;
+                Some(PrimitiveBatch::Paths(&self.paths[paths_start..paths_end]))
+            }
             PrimitiveKind::Underline => {
                 let underlines_start = self.underlines_start;
                 let mut underlines_end = underlines_start;
@@ -317,15 +366,16 @@ pub enum PrimitiveKind {
     Shadow,
     #[default]
     Quad,
+    Path,
     Underline,
     MonochromeSprite,
     PolychromeSprite,
 }
 
-#[derive(Clone, Debug)]
 pub enum Primitive {
     Shadow(Shadow),
     Quad(Quad),
+    Path(Path<ScaledPixels>),
     Underline(Underline),
     MonochromeSprite(MonochromeSprite),
     PolychromeSprite(PolychromeSprite),
@@ -335,6 +385,7 @@ pub enum Primitive {
 pub(crate) enum PrimitiveBatch<'a> {
     Shadows(&'a [Shadow]),
     Quads(&'a [Quad]),
+    Paths(&'a [Path<ScaledPixels>]),
     Underlines(&'a [Underline]),
     MonochromeSprites {
         texture_id: AtlasTextureId,
@@ -351,7 +402,7 @@ pub(crate) enum PrimitiveBatch<'a> {
 pub struct Quad {
     pub order: u32, // Initially a LayerId, then a DrawOrder.
     pub bounds: Bounds<ScaledPixels>,
-    pub content_mask: ScaledContentMask,
+    pub content_mask: ContentMask<ScaledPixels>,
     pub background: Hsla,
     pub border_color: Hsla,
     pub corner_radii: Corners<ScaledPixels>,
@@ -381,7 +432,7 @@ impl From<Quad> for Primitive {
 pub struct Underline {
     pub order: u32,
     pub bounds: Bounds<ScaledPixels>,
-    pub content_mask: ScaledContentMask,
+    pub content_mask: ContentMask<ScaledPixels>,
     pub thickness: ScaledPixels,
     pub color: Hsla,
     pub wavy: bool,
@@ -411,7 +462,7 @@ pub struct Shadow {
     pub order: u32,
     pub bounds: Bounds<ScaledPixels>,
     pub corner_radii: Corners<ScaledPixels>,
-    pub content_mask: ScaledContentMask,
+    pub content_mask: ContentMask<ScaledPixels>,
     pub color: Hsla,
     pub blur_radius: ScaledPixels,
 }
@@ -439,7 +490,7 @@ impl From<Shadow> for Primitive {
 pub struct MonochromeSprite {
     pub order: u32,
     pub bounds: Bounds<ScaledPixels>,
-    pub content_mask: ScaledContentMask,
+    pub content_mask: ContentMask<ScaledPixels>,
     pub color: Hsla,
     pub tile: AtlasTile,
 }
@@ -470,7 +521,7 @@ impl From<MonochromeSprite> for Primitive {
 pub struct PolychromeSprite {
     pub order: u32,
     pub bounds: Bounds<ScaledPixels>,
-    pub content_mask: ScaledContentMask,
+    pub content_mask: ContentMask<ScaledPixels>,
     pub corner_radii: Corners<ScaledPixels>,
     pub tile: AtlasTile,
     pub grayscale: bool,
@@ -497,21 +548,24 @@ impl From<PolychromeSprite> for Primitive {
     }
 }
 
-#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
-pub struct PathId(u32);
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+pub(crate) struct PathId(pub(crate) usize);
 
-pub struct PathBuilder {
-    bounds: Bounds<Pixels>,
-    vertices: Vec<PathVertex>,
-    start: Option<Point<Pixels>>,
-    current: Point<Pixels>,
+#[derive(Debug)]
+pub struct Path<P: Clone + Debug> {
+    pub(crate) id: PathId,
+    order: u32,
+    pub(crate) bounds: Bounds<P>,
+    pub(crate) vertices: Vec<PathVertex<P>>,
+    start: Option<Point<P>>,
+    current: Point<P>,
 }
 
-impl PathBuilder {
+impl Path<Pixels> {
     pub fn new() -> Self {
-        const NEXT_PATH_ID: AtomicU32 = AtomicU32::new(0);
-
         Self {
+            id: PathId(0),
+            order: 0,
             vertices: Vec::new(),
             start: Default::default(),
             current: Default::default(),
@@ -519,6 +573,21 @@ impl PathBuilder {
         }
     }
 
+    pub fn scale(&self, factor: f32) -> Path<ScaledPixels> {
+        Path {
+            id: self.id,
+            order: self.order,
+            bounds: self.bounds.scale(factor),
+            vertices: self
+                .vertices
+                .iter()
+                .map(|vertex| vertex.scale(factor))
+                .collect(),
+            start: self.start.map(|start| start.scale(factor)),
+            current: self.current.scale(factor),
+        }
+    }
+
     pub fn line_to(&mut self, to: Point<Pixels>) {
         if let Some(start) = self.start {
             self.push_triangle(
@@ -568,23 +637,63 @@ impl PathBuilder {
         self.vertices.push(PathVertex {
             xy_position: xy.0,
             st_position: st.0,
+            content_mask: Default::default(),
         });
         self.vertices.push(PathVertex {
             xy_position: xy.1,
             st_position: st.1,
+            content_mask: Default::default(),
         });
         self.vertices.push(PathVertex {
             xy_position: xy.2,
             st_position: st.2,
+            content_mask: Default::default(),
         });
     }
 }
 
-#[derive(Clone, Debug)]
+impl Eq for Path<ScaledPixels> {}
+
+impl PartialEq for Path<ScaledPixels> {
+    fn eq(&self, other: &Self) -> bool {
+        self.order == other.order
+    }
+}
+
+impl Ord for Path<ScaledPixels> {
+    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+        self.order.cmp(&other.order)
+    }
+}
+
+impl PartialOrd for Path<ScaledPixels> {
+    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+        Some(self.cmp(other))
+    }
+}
+
+impl From<Path<ScaledPixels>> for Primitive {
+    fn from(path: Path<ScaledPixels>) -> Self {
+        Primitive::Path(path)
+    }
+}
+
+#[derive(Debug)]
 #[repr(C)]
-pub struct PathVertex {
-    pub xy_position: Point<Pixels>,
-    pub st_position: Point<f32>,
+pub struct PathVertex<P: Clone + Debug> {
+    pub(crate) xy_position: Point<P>,
+    pub(crate) st_position: Point<f32>,
+    pub(crate) content_mask: ContentMask<P>,
+}
+
+impl PathVertex<Pixels> {
+    pub fn scale(&self, factor: f32) -> PathVertex<ScaledPixels> {
+        PathVertex {
+            xy_position: self.xy_position.scale(factor),
+            st_position: self.st_position,
+            content_mask: self.content_mask.scale(factor),
+        }
+    }
 }
 
 #[derive(Copy, Clone, Debug)]
@@ -619,15 +728,15 @@ mod tests {
 
     #[test]
     fn test_scene() {
-        let mut scene = Scene::new(1.0);
-        assert_eq!(scene.layers.len(), 0);
+        let mut scene = SceneBuilder::new();
+        assert_eq!(scene.layers_by_order.len(), 0);
 
-        scene.insert(smallvec![1], quad());
-        scene.insert(smallvec![2], shadow());
-        scene.insert(smallvec![3], quad());
+        scene.insert(&smallvec![1], quad());
+        scene.insert(&smallvec![2], shadow());
+        scene.insert(&smallvec![3], quad());
 
         let mut batches_count = 0;
-        for _ in scene.batches() {
+        for _ in scene.build().batches() {
             batches_count += 1;
         }
         assert_eq!(batches_count, 3);

crates/gpui3/src/style.rs 🔗

@@ -1,8 +1,8 @@
 use crate::{
     phi, point, rems, AbsoluteLength, BorrowAppContext, BorrowWindow, Bounds, ContentMask, Corners,
     CornersRefinement, DefiniteLength, Edges, EdgesRefinement, Font, FontFeatures, FontStyle,
-    FontWeight, Hsla, Length, Pixels, Point, PointRefinement, Quad, Rems, Result, RunStyle, Shadow,
-    SharedString, Size, SizeRefinement, ViewContext, WindowContext,
+    FontWeight, Hsla, Length, Pixels, Point, PointRefinement, Rems, Result, RunStyle, SharedString,
+    Size, SizeRefinement, ViewContext, WindowContext,
 };
 use refineable::Refineable;
 use smallvec::SmallVec;
@@ -243,51 +243,24 @@ impl Style {
     /// Paints the background of an element styled with this style.
     pub fn paint<V: 'static>(&self, bounds: Bounds<Pixels>, cx: &mut ViewContext<V>) {
         let rem_size = cx.rem_size();
-        let scale = cx.scale_factor();
-
-        for shadow in &self.box_shadow {
-            let content_mask = cx.content_mask();
-            let mut shadow_bounds = bounds;
-            shadow_bounds.origin += shadow.offset;
-            shadow_bounds.dilate(shadow.spread_radius);
-            cx.stack(0, |cx| {
-                let layer_id = cx.current_stacking_order();
-                cx.scene().insert(
-                    layer_id,
-                    Shadow {
-                        order: 0,
-                        bounds: shadow_bounds.scale(scale),
-                        content_mask: content_mask.scale(scale),
-                        corner_radii: self
-                            .corner_radii
-                            .to_pixels(shadow_bounds.size, rem_size)
-                            .scale(scale),
-                        color: shadow.color,
-                        blur_radius: shadow.blur_radius.scale(scale),
-                    },
-                );
-            })
-        }
+
+        cx.stack(0, |cx| {
+            cx.paint_shadows(
+                bounds,
+                self.corner_radii.to_pixels(bounds.size, rem_size),
+                &self.box_shadow,
+            );
+        });
 
         let background_color = self.fill.as_ref().and_then(Fill::color);
         if background_color.is_some() || self.is_border_visible() {
-            let content_mask = cx.content_mask();
             cx.stack(1, |cx| {
-                let order = cx.current_stacking_order();
-                cx.scene().insert(
-                    order,
-                    Quad {
-                        order: 0,
-                        bounds: bounds.scale(scale),
-                        content_mask: content_mask.scale(scale),
-                        background: background_color.unwrap_or_default(),
-                        border_color: self.border_color.unwrap_or_default(),
-                        corner_radii: self
-                            .corner_radii
-                            .to_pixels(bounds.size, rem_size)
-                            .scale(scale),
-                        border_widths: self.border_widths.to_pixels(rem_size).scale(scale),
-                    },
+                cx.paint_quad(
+                    bounds,
+                    self.corner_radii.to_pixels(bounds.size, rem_size),
+                    background_color.unwrap_or_default(),
+                    self.border_widths.to_pixels(rem_size),
+                    self.border_color.unwrap_or_default(),
                 );
             });
         }

crates/gpui3/src/window.rs 🔗

@@ -1,15 +1,17 @@
 use crate::{
     image_cache::RenderImageParams, px, size, AnyView, AppContext, AsyncWindowContext,
-    AvailableSpace, BorrowAppContext, Bounds, Context, Corners, DevicePixels, DisplayId, Effect,
-    Element, EntityId, FontId, GlyphId, Handle, Hsla, ImageData, IsZero, LayoutId, MainThread,
-    MainThreadOnly, MonochromeSprite, Pixels, PlatformAtlas, PlatformWindow, Point,
-    PolychromeSprite, Reference, RenderGlyphParams, RenderSvgParams, ScaledPixels, Scene,
-    SharedString, Size, StackingOrder, Style, TaffyLayoutEngine, Task, Underline, UnderlineStyle,
-    WeakHandle, WindowOptions, SUBPIXEL_VARIANTS,
+    AvailableSpace, BorrowAppContext, Bounds, BoxShadow, Context, Corners, DevicePixels, DisplayId,
+    Edges, Effect, Element, EntityId, FontId, GlyphId, Handle, Hsla, ImageData, IsZero, LayoutId,
+    MainThread, MainThreadOnly, MonochromeSprite, Path, Pixels, PlatformAtlas, PlatformWindow,
+    Point, PolychromeSprite, Quad, Reference, RenderGlyphParams, RenderSvgParams, ScaledPixels,
+    SceneBuilder, Shadow, SharedString, Size, StackingOrder, Style, TaffyLayoutEngine, Task,
+    Underline, UnderlineStyle, WeakHandle, WindowOptions, SUBPIXEL_VARIANTS,
 };
 use anyhow::Result;
 use smallvec::SmallVec;
-use std::{any::TypeId, borrow::Cow, future::Future, marker::PhantomData, mem, sync::Arc};
+use std::{
+    any::TypeId, borrow::Cow, fmt::Debug, future::Future, marker::PhantomData, mem, sync::Arc,
+};
 use util::ResultExt;
 
 pub struct AnyWindow {}
@@ -25,8 +27,9 @@ pub struct Window {
     pub(crate) root_view: Option<AnyView<()>>,
     mouse_position: Point<Pixels>,
     current_stacking_order: StackingOrder,
-    content_mask_stack: Vec<ContentMask>,
-    pub(crate) scene: Scene,
+    content_mask_stack: Vec<ContentMask<Pixels>>,
+    scale_factor: f32,
+    pub(crate) scene_builder: SceneBuilder,
     pub(crate) dirty: bool,
 }
 
@@ -47,7 +50,8 @@ impl Window {
             let cx = cx.to_async();
             move |content_size, scale_factor| {
                 cx.update_window(handle, |cx| {
-                    cx.window.scene = Scene::new(scale_factor);
+                    cx.window.scale_factor = scale_factor;
+                    cx.window.scene_builder = SceneBuilder::new();
                     cx.window.content_size = content_size;
                     cx.window.display_id = cx
                         .window
@@ -75,20 +79,22 @@ impl Window {
             mouse_position,
             current_stacking_order: SmallVec::new(),
             content_mask_stack: Vec::new(),
-            scene: Scene::new(scale_factor),
+            scale_factor,
+            scene_builder: SceneBuilder::new(),
             dirty: true,
         }
     }
 }
 
-#[derive(Clone, Debug)]
-pub struct ContentMask {
-    pub bounds: Bounds<Pixels>,
+#[derive(Clone, Debug, Default, PartialEq, Eq)]
+#[repr(C)]
+pub struct ContentMask<P: Clone + Debug> {
+    pub bounds: Bounds<P>,
 }
 
-impl ContentMask {
-    pub fn scale(&self, factor: f32) -> ScaledContentMask {
-        ScaledContentMask {
+impl ContentMask<Pixels> {
+    pub fn scale(&self, factor: f32) -> ContentMask<ScaledPixels> {
+        ContentMask {
             bounds: self.bounds.scale(factor),
         }
     }
@@ -99,12 +105,6 @@ impl ContentMask {
     }
 }
 
-#[derive(Default, Clone, Debug, PartialEq, Eq)]
-#[repr(C)]
-pub struct ScaledContentMask {
-    bounds: Bounds<ScaledPixels>,
-}
-
 pub struct WindowContext<'a, 'w> {
     app: Reference<'a, AppContext>,
     window: Reference<'w, Window>,
@@ -234,7 +234,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
     }
 
     pub fn scale_factor(&self) -> f32 {
-        self.window.scene.scale_factor
+        self.window.scale_factor
     }
 
     pub fn rem_size(&self) -> Pixels {
@@ -245,10 +245,6 @@ impl<'a, 'w> WindowContext<'a, 'w> {
         self.window.mouse_position
     }
 
-    pub fn scene(&mut self) -> &mut Scene {
-        &mut self.window.scene
-    }
-
     pub fn stack<R>(&mut self, order: u32, f: impl FnOnce(&mut Self) -> R) -> R {
         self.window.current_stacking_order.push(order);
         let result = f(self);
@@ -256,8 +252,69 @@ impl<'a, 'w> WindowContext<'a, 'w> {
         result
     }
 
-    pub fn current_stacking_order(&self) -> StackingOrder {
-        self.window.current_stacking_order.clone()
+    pub fn paint_shadows(
+        &mut self,
+        bounds: Bounds<Pixels>,
+        corner_radii: Corners<Pixels>,
+        shadows: &[BoxShadow],
+    ) {
+        let scale_factor = self.scale_factor();
+        let content_mask = self.content_mask();
+        let window = &mut *self.window;
+        for shadow in shadows {
+            let mut shadow_bounds = bounds;
+            shadow_bounds.origin += shadow.offset;
+            shadow_bounds.dilate(shadow.spread_radius);
+            window.scene_builder.insert(
+                &window.current_stacking_order,
+                Shadow {
+                    order: 0,
+                    bounds: shadow_bounds.scale(scale_factor),
+                    content_mask: content_mask.scale(scale_factor),
+                    corner_radii: corner_radii.scale(scale_factor),
+                    color: shadow.color,
+                    blur_radius: shadow.blur_radius.scale(scale_factor),
+                },
+            );
+        }
+    }
+
+    pub fn paint_quad(
+        &mut self,
+        bounds: Bounds<Pixels>,
+        corner_radii: Corners<Pixels>,
+        background: impl Into<Hsla>,
+        border_widths: Edges<Pixels>,
+        border_color: impl Into<Hsla>,
+    ) {
+        let scale_factor = self.scale_factor();
+        let content_mask = self.content_mask();
+
+        let window = &mut *self.window;
+        window.scene_builder.insert(
+            &window.current_stacking_order,
+            Quad {
+                order: 0,
+                bounds: bounds.scale(scale_factor),
+                content_mask: content_mask.scale(scale_factor),
+                background: background.into(),
+                border_color: border_color.into(),
+                corner_radii: corner_radii.scale(scale_factor),
+                border_widths: border_widths.scale(scale_factor),
+            },
+        );
+    }
+
+    pub fn paint_path(&mut self, mut path: Path<Pixels>) {
+        let scale_factor = self.scale_factor();
+        let content_mask = self.content_mask();
+        for vertex in &mut path.vertices {
+            vertex.content_mask = content_mask.clone();
+        }
+        let window = &mut *self.window;
+        window
+            .scene_builder
+            .insert(&window.current_stacking_order, path.scale(scale_factor));
     }
 
     pub fn paint_underline(
@@ -277,9 +334,9 @@ impl<'a, 'w> WindowContext<'a, 'w> {
             size: size(width, height),
         };
         let content_mask = self.content_mask();
-        let layer_id = self.current_stacking_order();
-        self.window.scene.insert(
-            layer_id,
+        let window = &mut *self.window;
+        window.scene_builder.insert(
+            &window.current_stacking_order,
             Underline {
                 order: 0,
                 bounds: bounds.scale(scale_factor),
@@ -317,7 +374,6 @@ impl<'a, 'w> WindowContext<'a, 'w> {
 
         let raster_bounds = self.text_system().raster_bounds(&params)?;
         if !raster_bounds.is_zero() {
-            let layer_id = self.current_stacking_order();
             let tile =
                 self.window
                     .sprite_atlas
@@ -330,9 +386,9 @@ impl<'a, 'w> WindowContext<'a, 'w> {
                 size: tile.bounds.size.map(Into::into),
             };
             let content_mask = self.content_mask().scale(scale_factor);
-
-            self.window.scene.insert(
-                layer_id,
+            let window = &mut *self.window;
+            window.scene_builder.insert(
+                &window.current_stacking_order,
                 MonochromeSprite {
                     order: 0,
                     bounds,
@@ -366,7 +422,6 @@ impl<'a, 'w> WindowContext<'a, 'w> {
 
         let raster_bounds = self.text_system().raster_bounds(&params)?;
         if !raster_bounds.is_zero() {
-            let layer_id = self.current_stacking_order();
             let tile =
                 self.window
                     .sprite_atlas
@@ -379,9 +434,10 @@ impl<'a, 'w> WindowContext<'a, 'w> {
                 size: tile.bounds.size.map(Into::into),
             };
             let content_mask = self.content_mask().scale(scale_factor);
+            let window = &mut *self.window;
 
-            self.window.scene.insert(
-                layer_id,
+            window.scene_builder.insert(
+                &window.current_stacking_order,
                 PolychromeSprite {
                     order: 0,
                     bounds,
@@ -411,7 +467,6 @@ impl<'a, 'w> WindowContext<'a, 'w> {
                 .map(|pixels| DevicePixels::from((pixels.0 * 2.).ceil() as i32)),
         };
 
-        let layer_id = self.current_stacking_order();
         let tile =
             self.window
                 .sprite_atlas
@@ -421,8 +476,9 @@ impl<'a, 'w> WindowContext<'a, 'w> {
                 })?;
         let content_mask = self.content_mask().scale(scale_factor);
 
-        self.window.scene.insert(
-            layer_id,
+        let window = &mut *self.window;
+        window.scene_builder.insert(
+            &window.current_stacking_order,
             MonochromeSprite {
                 order: 0,
                 bounds,
@@ -446,7 +502,6 @@ impl<'a, 'w> WindowContext<'a, 'w> {
         let bounds = bounds.scale(scale_factor);
         let params = RenderImageParams { image_id: data.id };
 
-        let order = self.current_stacking_order();
         let tile = self
             .window
             .sprite_atlas
@@ -456,8 +511,9 @@ impl<'a, 'w> WindowContext<'a, 'w> {
         let content_mask = self.content_mask().scale(scale_factor);
         let corner_radii = corner_radii.scale(scale_factor);
 
-        self.window.scene.insert(
-            order,
+        let window = &mut *self.window;
+        window.scene_builder.insert(
+            &window.current_stacking_order,
             PolychromeSprite {
                 order: 0,
                 bounds,
@@ -467,7 +523,6 @@ impl<'a, 'w> WindowContext<'a, 'w> {
                 grayscale,
             },
         );
-
         Ok(())
     }
 
@@ -485,7 +540,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
 
             root_view.paint(layout, &mut (), &mut frame_state, cx)?;
             cx.window.root_view = Some(root_view);
-            let scene = cx.window.scene.take();
+            let scene = cx.window.scene_builder.build();
 
             cx.run_on_main(view, |_, cx| {
                 cx.window
@@ -557,7 +612,11 @@ pub trait BorrowWindow: BorrowAppContext {
     fn window(&self) -> &Window;
     fn window_mut(&mut self) -> &mut Window;
 
-    fn with_content_mask<R>(&mut self, mask: ContentMask, f: impl FnOnce(&mut Self) -> R) -> R {
+    fn with_content_mask<R>(
+        &mut self,
+        mask: ContentMask<Pixels>,
+        f: impl FnOnce(&mut Self) -> R,
+    ) -> R {
         let mask = mask.intersect(&self.content_mask());
         self.window_mut().content_mask_stack.push(mask);
         let result = f(self);
@@ -565,7 +624,7 @@ pub trait BorrowWindow: BorrowAppContext {
         result
     }
 
-    fn content_mask(&self) -> ContentMask {
+    fn content_mask(&self) -> ContentMask<Pixels> {
         self.window()
             .content_mask_stack
             .last()