diff --git a/crates/gpui3/src/elements/text.rs b/crates/gpui3/src/elements/text.rs index 1c1bd89c0e..db94b96b77 100644 --- a/crates/gpui3/src/elements/text.rs +++ b/crates/gpui3/src/elements/text.rs @@ -32,7 +32,7 @@ pub struct Text { impl Element for Text { type State = S; - type FrameState = Arc>>; + type FrameState = Arc>>; fn layout( &mut self, @@ -54,7 +54,6 @@ impl Element for Text { let layout_id = cx.request_measured_layout(Default::default(), rem_size, { let frame_state = paint_state.clone(); move |_, _| { - dbg!("starting measurement"); let Some(line_layout) = text_system .layout_line( text.as_ref(), @@ -65,23 +64,21 @@ impl Element for Text { else { return Size::default(); }; - dbg!("bbbb"); let size = Size { width: line_layout.width(), height: line_height, }; - frame_state.lock().replace(TextLayout { + frame_state.lock().replace(TextFrameState { line: Arc::new(line_layout), line_height, }); - dbg!(size) + size } }); - dbg!("got to end of text layout"); Ok((layout_id?, paint_state)) } @@ -89,22 +86,20 @@ impl Element for Text { &mut self, layout: Layout, _: &mut Self::State, - paint_state: &mut Self::FrameState, + frame_state: &mut Self::FrameState, cx: &mut ViewContext, ) -> Result<()> { let line; let line_height; { - let paint_state = paint_state.lock(); - let paint_state = paint_state + let frame_state = frame_state.lock(); + let frame_state = frame_state .as_ref() .expect("measurement has not been performed"); - line = paint_state.line.clone(); - line_height = paint_state.line_height; + line = frame_state.line.clone(); + line_height = frame_state.line_height; } - let _text_style = cx.text_style(); - // todo!("We haven't added visible bounds to the new element system yet, so this is a placeholder."); let visible_bounds = layout.bounds; line.paint(&layout, visible_bounds, line_height, cx)?; @@ -113,7 +108,7 @@ impl Element for Text { } } -pub struct TextLayout { +pub struct TextFrameState { line: Arc, line_height: Pixels, } diff --git a/crates/gpui3/src/platform.rs b/crates/gpui3/src/platform.rs index 81d02af083..2bd5b942e6 100644 --- a/crates/gpui3/src/platform.rs +++ b/crates/gpui3/src/platform.rs @@ -180,14 +180,30 @@ pub trait PlatformTextSystem: Send + Sync { ) -> Vec; } -pub trait PlatformSpriteSystem { +pub trait PlatformAtlas { fn get_or_insert_with( &self, key: Key, build: impl FnOnce() -> (Size, Vec), - ) -> MonochromeSprite; + ) -> AtlasTile; + + fn clear(&self); } +#[derive(Clone, Debug)] +#[repr(C)] +pub struct AtlasTile { + pub(crate) texture_id: AtlasTextureId, + pub(crate) tile_id: TileId, + pub(crate) bounds_in_atlas: Bounds, +} + +#[derive(Clone, Copy, Debug)] +#[repr(C)] +pub(crate) struct AtlasTextureId(pub(crate) usize); + +pub(crate) type TileId = etagere::AllocId; + pub trait PlatformInputHandler { fn selected_text_range(&self) -> Option>; fn marked_text_range(&self) -> Option>; diff --git a/crates/gpui3/src/platform/mac.rs b/crates/gpui3/src/platform/mac.rs index fec4274168..dcbf8f887f 100644 --- a/crates/gpui3/src/platform/mac.rs +++ b/crates/gpui3/src/platform/mac.rs @@ -2,11 +2,11 @@ ///! an origin at the bottom left of the main display. mod dispatcher; mod events; +mod metal_atlas; mod metal_renderer; mod open_type; mod platform; mod screen; -mod sprite; mod text_system; mod window; mod window_appearence; @@ -31,9 +31,9 @@ use std::{ }; pub use dispatcher::*; +pub use metal_atlas::*; pub use platform::*; pub use screen::*; -pub use sprite::*; pub use text_system::*; pub use window::*; diff --git a/crates/gpui3/src/platform/mac/metal_atlas.rs b/crates/gpui3/src/platform/mac/metal_atlas.rs new file mode 100644 index 0000000000..926d7df8b6 --- /dev/null +++ b/crates/gpui3/src/platform/mac/metal_atlas.rs @@ -0,0 +1,164 @@ +use crate::{AtlasTextureId, AtlasTile, Bounds, DevicePixels, PlatformAtlas, Point, Size}; +use collections::HashMap; +use etagere::BucketedAtlasAllocator; +use foreign_types::ForeignType; +use metal::{Device, TextureDescriptor, TextureDescriptorRef}; +use objc::{msg_send, sel, sel_impl}; +use parking_lot::{RwLock, RwLockUpgradableReadGuard}; +use std::hash::Hash; + +pub struct MetalAtlas(RwLock>); + +struct MetalAtlasState { + device: Device, + texture_descriptor: TextureDescriptor, + textures: Vec, + tiles_by_key: HashMap, +} + +impl PlatformAtlas for MetalAtlas +where + Key: Eq + Hash, +{ + fn get_or_insert_with( + &self, + key: Key, + build: impl FnOnce() -> (Size, Vec), + ) -> AtlasTile { + let lock = self.0.upgradable_read(); + if let Some(tile) = lock.tiles_by_key.get(&key) { + return tile.clone(); + } else { + let mut lock = RwLockUpgradableReadGuard::upgrade(lock); + let (size, bytes) = build(); + lock.textures + .iter_mut() + .rev() + .find_map(|texture| texture.allocate(size, &bytes)) + .unwrap_or_else(|| { + let texture = lock.push_texture(size); + texture + .allocate(size, &bytes) + .expect("could not allocate a tile in new texture") + }) + } + } + + fn clear(&self) { + self.0.write().tiles_by_key.clear(); + } +} + +impl MetalAtlasState { + fn push_texture(&mut self, min_size: Size) -> &mut MetalAtlasTexture { + let default_atlas_size = Size { + width: self.texture_descriptor.width().into(), + height: self.texture_descriptor.height().into(), + }; + let size; + let metal_texture; + + if min_size.width > default_atlas_size.width || min_size.height > default_atlas_size.height + { + let descriptor = unsafe { + let descriptor_ptr: *mut metal::MTLTextureDescriptor = + msg_send![self.texture_descriptor, copy]; + metal::TextureDescriptor::from_ptr(descriptor_ptr) + }; + descriptor.set_width(min_size.width.into()); + descriptor.set_height(min_size.height.into()); + + size = min_size; + metal_texture = self.device.new_texture(&descriptor); + } else { + size = default_atlas_size; + metal_texture = self.device.new_texture(&self.texture_descriptor); + } + + let atlas_texture = MetalAtlasTexture { + id: AtlasTextureId(self.textures.len()), + allocator: etagere::BucketedAtlasAllocator::new(size.into()), + metal_texture, + }; + self.textures.push(atlas_texture); + self.textures.last_mut().unwrap() + } +} + +struct MetalAtlasTexture { + id: AtlasTextureId, + allocator: BucketedAtlasAllocator, + metal_texture: metal::Texture, +} + +impl MetalAtlasTexture { + fn allocate(&mut self, size: Size, bytes: &[u8]) -> Option { + let size = size.into(); + let allocation = self.allocator.allocate(size)?; + let tile = AtlasTile { + texture_id: self.id, + tile_id: allocation.id, + bounds_in_atlas: allocation.rectangle.into(), + }; + let region = metal::MTLRegion::new_2d( + u32::from(tile.bounds_in_atlas.origin.x) as u64, + u32::from(tile.bounds_in_atlas.origin.y) as u64, + u32::from(tile.bounds_in_atlas.size.width) as u64, + u32::from(tile.bounds_in_atlas.size.height) as u64, + ); + self.metal_texture.replace_region( + region, + 0, + bytes.as_ptr() as *const _, + u32::from( + tile.bounds_in_atlas + .size + .width + .to_bytes(self.bytes_per_pixel()), + ) as u64, + ); + Some(tile) + } + + fn bytes_per_pixel(&self) -> u8 { + use metal::MTLPixelFormat::*; + match self.metal_texture.pixel_format() { + A8Unorm | R8Unorm => 1, + RGBA8Unorm | BGRA8Unorm => 4, + _ => unimplemented!(), + } + } +} + +impl From> for etagere::Size { + fn from(size: Size) -> Self { + etagere::Size::new(u32::from(size.width) as i32, u32::from(size.width) as i32) + } +} + +impl From for Point { + fn from(value: etagere::Point) -> Self { + Point { + x: DevicePixels::from(value.x as u32), + y: DevicePixels::from(value.y as u32), + } + } +} + +impl From for Size { + fn from(size: etagere::Size) -> Self { + Size { + width: DevicePixels::from(size.width as u32), + height: DevicePixels::from(size.height as u32), + } + } +} + +impl From for Bounds { + fn from(rectangle: etagere::Rectangle) -> Self { + Bounds { + origin: rectangle.min.into(), + size: rectangle.size().into(), + } + } +} diff --git a/crates/gpui3/src/platform/mac/sprite.rs b/crates/gpui3/src/platform/mac/sprite.rs deleted file mode 100644 index 8b13789179..0000000000 --- a/crates/gpui3/src/platform/mac/sprite.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/crates/gpui3/src/scene.rs b/crates/gpui3/src/scene.rs index 7d29c9f702..f3e26f0197 100644 --- a/crates/gpui3/src/scene.rs +++ b/crates/gpui3/src/scene.rs @@ -1,7 +1,7 @@ use std::{iter::Peekable, mem}; use super::{Bounds, Hsla, Pixels, Point}; -use crate::{Corners, DevicePixels, Edges}; +use crate::{AtlasTile, Corners, DevicePixels, Edges}; use bytemuck::{Pod, Zeroable}; // Exported to metal @@ -243,10 +243,8 @@ impl From for Primitive { pub struct MonochromeSprite { pub order: u32, pub bounds: Bounds, - pub atlas_id: AtlasId, - pub tile_id: TileId, - pub bounds_in_atlas: Bounds, - pub color: Option, + pub color: Hsla, + pub tile: AtlasTile, } impl MonochromeSprite {