WIP

Antonio Scandurra created

Change summary

crates/gpui3/src/platform.rs                    |   1 
crates/gpui3/src/platform/mac/metal_renderer.rs | 106 ++++++++++++++++++
crates/gpui3/src/platform/mac/window.rs         |   4 
crates/gpui3/src/scene.rs                       |  59 ++++++++-
crates/gpui3/src/text_system.rs                 |  19 +++
crates/gpui3/src/text_system/line.rs            |   8 +
crates/gpui3/src/window.rs                      |  53 +++++++++
7 files changed, 235 insertions(+), 15 deletions(-)

Detailed changes

crates/gpui3/src/platform.rs 🔗

@@ -148,6 +148,7 @@ pub trait PlatformWindow {
     fn draw(&self, scene: Scene);
 
     fn monochrome_sprite_atlas(&self) -> Arc<dyn PlatformAtlas>;
+    fn polychrome_sprite_atlas(&self) -> Arc<dyn PlatformAtlas>;
 }
 
 pub trait PlatformDispatcher: Send + Sync {

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

@@ -1,5 +1,6 @@
 use crate::{
-    point, size, AtlasTextureId, DevicePixels, MetalAtlas, MonochromeSprite, Quad, Scene, Size,
+    point, size, AtlasTextureId, DevicePixels, MetalAtlas, MonochromeSprite, PolychromeSprite,
+    Quad, Scene, Size,
 };
 use cocoa::{
     base::{NO, YES},
@@ -21,6 +22,7 @@ pub struct MetalRenderer {
     unit_vertices: metal::Buffer,
     instances: metal::Buffer,
     monochrome_sprite_atlas: Arc<MetalAtlas>,
+    polychrome_sprite_atlas: Arc<MetalAtlas>,
 }
 
 impl MetalRenderer {
@@ -107,6 +109,14 @@ impl MetalRenderer {
             MTLPixelFormat::A8Unorm,
             device.clone(),
         ));
+        let polychrome_sprite_atlas = Arc::new(MetalAtlas::new(
+            Size {
+                width: DevicePixels(1024),
+                height: DevicePixels(768),
+            },
+            MTLPixelFormat::BGRA8Unorm,
+            device.clone(),
+        ));
 
         Self {
             layer,
@@ -116,6 +126,7 @@ impl MetalRenderer {
             unit_vertices,
             instances,
             monochrome_sprite_atlas,
+            polychrome_sprite_atlas,
         }
     }
 
@@ -127,6 +138,10 @@ impl MetalRenderer {
         &self.monochrome_sprite_atlas
     }
 
+    pub fn polychrome_sprite_atlas(&self) -> &Arc<MetalAtlas> {
+        &self.polychrome_sprite_atlas
+    }
+
     pub fn draw(&mut self, scene: &mut Scene) {
         let layer = self.layer.clone();
         let viewport_size = layer.drawable_size();
@@ -180,7 +195,7 @@ impl MetalRenderer {
                             command_encoder,
                         );
                     }
-                    crate::PrimitiveBatch::Sprites {
+                    crate::PrimitiveBatch::MonochromeSprites {
                         texture_id,
                         sprites,
                     } => {
@@ -192,6 +207,18 @@ impl MetalRenderer {
                             command_encoder,
                         );
                     }
+                    crate::PrimitiveBatch::PolychromeSprites {
+                        texture_id,
+                        sprites,
+                    } => {
+                        self.draw_polychrome_sprites(
+                            texture_id,
+                            sprites,
+                            &mut instance_offset,
+                            viewport_size,
+                            command_encoder,
+                        );
+                    }
                 }
             }
         }
@@ -337,6 +364,81 @@ impl MetalRenderer {
         );
         *offset = next_offset;
     }
+
+    fn draw_polychrome_sprites(
+        &mut self,
+        texture_id: AtlasTextureId,
+        sprites: &[PolychromeSprite],
+        offset: &mut usize,
+        viewport_size: Size<DevicePixels>,
+        command_encoder: &metal::RenderCommandEncoderRef,
+    ) {
+        todo!()
+        // if sprites.is_empty() {
+        //     return;
+        // }
+        // align_offset(offset);
+
+        // let texture = self.monochrome_sprite_atlas.texture(texture_id);
+        // let texture_size = size(
+        //     DevicePixels(texture.width() as i32),
+        //     DevicePixels(texture.height() as i32),
+        // );
+        // command_encoder.set_render_pipeline_state(&self.sprites_pipeline_state);
+        // command_encoder.set_vertex_buffer(
+        //     MonochromeSpriteInputIndex::Vertices as u64,
+        //     Some(&self.unit_vertices),
+        //     0,
+        // );
+        // command_encoder.set_vertex_buffer(
+        //     MonochromeSpriteInputIndex::Sprites as u64,
+        //     Some(&self.instances),
+        //     *offset as u64,
+        // );
+        // command_encoder.set_vertex_bytes(
+        //     MonochromeSpriteInputIndex::ViewportSize as u64,
+        //     mem::size_of_val(&viewport_size) as u64,
+        //     &viewport_size as *const Size<DevicePixels> as *const _,
+        // );
+        // command_encoder.set_vertex_bytes(
+        //     MonochromeSpriteInputIndex::AtlasTextureSize as u64,
+        //     mem::size_of_val(&texture_size) as u64,
+        //     &texture_size as *const Size<DevicePixels> as *const _,
+        // );
+        // command_encoder.set_fragment_buffer(
+        //     MonochromeSpriteInputIndex::Sprites as u64,
+        //     Some(&self.instances),
+        //     *offset as u64,
+        // );
+        // command_encoder.set_fragment_texture(
+        //     MonochromeSpriteInputIndex::AtlasTexture as u64,
+        //     Some(&texture),
+        // );
+
+        // let sprite_bytes_len = mem::size_of::<MonochromeSprite>() * sprites.len();
+        // let buffer_contents = unsafe { (self.instances.contents() as *mut u8).add(*offset) };
+        // unsafe {
+        //     ptr::copy_nonoverlapping(
+        //         sprites.as_ptr() as *const u8,
+        //         buffer_contents,
+        //         sprite_bytes_len,
+        //     );
+        // }
+
+        // let next_offset = *offset + sprite_bytes_len;
+        // assert!(
+        //     next_offset <= INSTANCE_BUFFER_SIZE,
+        //     "instance buffer exhausted"
+        // );
+
+        // command_encoder.draw_primitives_instanced(
+        //     metal::MTLPrimitiveType::Triangle,
+        //     0,
+        //     6,
+        //     sprites.len() as u64,
+        // );
+        // *offset = next_offset;
+    }
 }
 
 fn build_pipeline_state(

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

@@ -889,6 +889,10 @@ impl PlatformWindow for MacWindow {
     fn monochrome_sprite_atlas(&self) -> Arc<dyn PlatformAtlas> {
         self.0.lock().renderer.monochrome_sprite_atlas().clone()
     }
+
+    fn polychrome_sprite_atlas(&self) -> Arc<dyn PlatformAtlas> {
+        self.0.lock().renderer.polychrome_sprite_atlas().clone()
+    }
 }
 
 fn get_scale_factor(native_window: id) -> f32 {

crates/gpui3/src/scene.rs 🔗

@@ -38,8 +38,11 @@ impl Scene {
             Primitive::Quad(quad) => {
                 layer.quads.push(quad);
             }
-            Primitive::Sprite(sprite) => {
-                layer.sprites.push(sprite);
+            Primitive::MonochromeSprite(sprite) => {
+                layer.monochrome_sprites.push(sprite);
+            }
+            Primitive::PolychromeSprite(sprite) => {
+                layer.polychrome_sprites.push(sprite);
             }
         }
     }
@@ -52,19 +55,20 @@ impl Scene {
 #[derive(Debug, Default)]
 pub(crate) struct SceneLayer {
     pub quads: Vec<Quad>,
-    pub sprites: Vec<MonochromeSprite>,
+    pub monochrome_sprites: Vec<MonochromeSprite>,
+    pub polychrome_sprites: Vec<PolychromeSprite>,
 }
 
 impl SceneLayer {
     pub fn batches(&mut self) -> impl Iterator<Item = PrimitiveBatch> {
         self.quads.sort_unstable();
-        self.sprites.sort_unstable();
+        self.monochrome_sprites.sort_unstable();
 
         BatchIterator::new(
             &self.quads,
             self.quads.iter().peekable(),
-            &self.sprites,
-            self.sprites.iter().peekable(),
+            &self.monochrome_sprites,
+            self.monochrome_sprites.iter().peekable(),
         )
     }
 }
@@ -131,7 +135,7 @@ where
                         })
                         .count();
                 self.sprites_start = sprites_end;
-                Some(PrimitiveBatch::Sprites {
+                Some(PrimitiveBatch::MonochromeSprites {
                     texture_id,
                     sprites: &self.sprites[sprites_start..sprites_end],
                 })
@@ -171,15 +175,20 @@ pub enum PrimitiveKind {
 #[derive(Clone, Debug)]
 pub enum Primitive {
     Quad(Quad),
-    Sprite(MonochromeSprite),
+    MonochromeSprite(MonochromeSprite),
+    PolychromeSprite(PolychromeSprite),
 }
 
 pub(crate) enum PrimitiveBatch<'a> {
     Quads(&'a [Quad]),
-    Sprites {
+    MonochromeSprites {
         texture_id: AtlasTextureId,
         sprites: &'a [MonochromeSprite],
     },
+    PolychromeSprites {
+        texture_id: AtlasTextureId,
+        sprites: &'a [PolychromeSprite],
+    },
 }
 
 #[derive(Debug, Copy, Clone, Eq, PartialEq)]
@@ -256,7 +265,37 @@ impl PartialOrd for MonochromeSprite {
 
 impl From<MonochromeSprite> for Primitive {
     fn from(sprite: MonochromeSprite) -> Self {
-        Primitive::Sprite(sprite)
+        Primitive::MonochromeSprite(sprite)
+    }
+}
+
+#[derive(Clone, Debug, Eq, PartialEq)]
+#[repr(C)]
+pub struct PolychromeSprite {
+    pub order: u32,
+    pub bounds: Bounds<ScaledPixels>,
+    pub content_mask: ScaledContentMask,
+    pub tile: AtlasTile,
+}
+
+impl Ord for PolychromeSprite {
+    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+        match self.order.cmp(&other.order) {
+            std::cmp::Ordering::Equal => self.tile.tile_id.cmp(&other.tile.tile_id),
+            order => order,
+        }
+    }
+}
+
+impl PartialOrd for PolychromeSprite {
+    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+        Some(self.cmp(other))
+    }
+}
+
+impl From<PolychromeSprite> for Primitive {
+    fn from(sprite: PolychromeSprite) -> Self {
+        Primitive::PolychromeSprite(sprite)
     }
 }
 

crates/gpui3/src/text_system.rs 🔗

@@ -404,6 +404,25 @@ impl Hash for RenderGlyphParams {
     }
 }
 
+#[derive(Clone, Debug, PartialEq)]
+pub struct RenderEmojiParams {
+    pub(crate) font_id: FontId,
+    pub(crate) glyph_id: GlyphId,
+    pub(crate) font_size: Pixels,
+    pub(crate) scale_factor: f32,
+}
+
+impl Eq for RenderEmojiParams {}
+
+impl Hash for RenderEmojiParams {
+    fn hash<H: Hasher>(&self, state: &mut H) {
+        self.font_id.0.hash(state);
+        self.glyph_id.0.hash(state);
+        self.font_size.0.to_bits().hash(state);
+        self.scale_factor.to_bits().hash(state);
+    }
+}
+
 #[derive(Clone, Debug, Eq, PartialEq, Hash)]
 pub struct Font {
     pub family: SharedString,

crates/gpui3/src/text_system/line.rs 🔗

@@ -159,7 +159,13 @@ impl Line {
                     }
 
                     if glyph.is_emoji {
-                        todo!()
+                        cx.paint_emoji(
+                            glyph_origin,
+                            layout.order,
+                            run.font_id,
+                            glyph.id,
+                            self.layout.font_size,
+                        )?;
                     } else {
                         cx.paint_glyph(
                             glyph_origin,

crates/gpui3/src/window.rs 🔗

@@ -2,8 +2,8 @@ use crate::{
     px, AnyView, AppContext, AvailableSpace, BorrowAppContext, Bounds, Context, Corners,
     DevicePixels, Effect, Element, EntityId, FontId, GlyphId, Handle, Hsla, IsZero, LayerId,
     LayoutId, MainThread, MainThreadOnly, MonochromeSprite, Pixels, PlatformAtlas, PlatformWindow,
-    Point, Reference, RenderGlyphParams, RenderSvgParams, ScaledPixels, Scene, SharedString, Size,
-    Style, TaffyLayoutEngine, WeakHandle, WindowOptions, SUBPIXEL_VARIANTS,
+    Point, PolychromeSprite, Reference, RenderGlyphParams, RenderSvgParams, ScaledPixels, Scene,
+    SharedString, Size, Style, TaffyLayoutEngine, WeakHandle, WindowOptions, SUBPIXEL_VARIANTS,
 };
 use anyhow::Result;
 use futures::Future;
@@ -17,6 +17,7 @@ pub struct Window {
     handle: AnyWindowHandle,
     platform_window: MainThreadOnly<Box<dyn PlatformWindow>>,
     monochrome_sprite_atlas: Arc<dyn PlatformAtlas>,
+    polychrome_sprite_atlas: Arc<dyn PlatformAtlas>,
     rem_size: Pixels,
     content_size: Size<Pixels>,
     layout_engine: TaffyLayoutEngine,
@@ -36,6 +37,7 @@ impl Window {
     ) -> Self {
         let platform_window = cx.platform().open_window(handle, options);
         let monochrome_sprite_atlas = platform_window.monochrome_sprite_atlas();
+        let polychrome_sprite_atlas = platform_window.polychrome_sprite_atlas();
         let mouse_position = platform_window.mouse_position();
         let content_size = platform_window.content_size();
         let scale_factor = platform_window.scale_factor();
@@ -59,6 +61,7 @@ impl Window {
             handle,
             platform_window,
             monochrome_sprite_atlas,
+            polychrome_sprite_atlas,
             rem_size: px(16.),
             content_size,
             layout_engine: TaffyLayoutEngine::new(),
@@ -300,6 +303,52 @@ impl<'a, 'w> WindowContext<'a, 'w> {
         Ok(())
     }
 
+    pub fn paint_emoji(
+        &mut self,
+        origin: Point<Pixels>,
+        order: u32,
+        font_id: FontId,
+        glyph_id: GlyphId,
+        font_size: Pixels,
+    ) -> Result<()> {
+        let scale_factor = self.scale_factor();
+        let glyph_origin = origin.scale(scale_factor);
+        let params = RenderGlyphParams {
+            font_id,
+            glyph_id,
+            font_size,
+            subpixel_variant: Default::default(),
+            scale_factor,
+        };
+
+        let raster_bounds = self.text_system().raster_bounds(&params)?;
+        if !raster_bounds.is_zero() {
+            let layer_id = self.current_layer_id();
+            let tile = self
+                .window
+                .polychrome_sprite_atlas
+                .get_or_insert_with(&params.clone().into(), &mut || {
+                    self.text_system().rasterize_glyph(&params)
+                })?;
+            let bounds = Bounds {
+                origin: glyph_origin.map(|px| px.floor()) + raster_bounds.origin.map(Into::into),
+                size: tile.bounds.size.map(Into::into),
+            };
+            let content_mask = self.content_mask().scale(scale_factor);
+
+            self.window.scene.insert(
+                layer_id,
+                PolychromeSprite {
+                    order,
+                    bounds,
+                    content_mask,
+                    tile,
+                },
+            );
+        }
+        Ok(())
+    }
+
     pub(crate) fn draw(&mut self) -> Result<()> {
         let unit_entity = self.unit_entity.clone();
         self.update_entity(&unit_entity, |_, cx| {