Detailed changes
@@ -32,7 +32,7 @@ pub struct Text<S> {
impl<S: 'static> Element for Text<S> {
type State = S;
- type FrameState = Arc<Mutex<Option<TextLayout>>>;
+ type FrameState = Arc<Mutex<Option<TextFrameState>>>;
fn layout(
&mut self,
@@ -54,7 +54,6 @@ impl<S: 'static> Element for Text<S> {
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<S: 'static> Element for Text<S> {
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<S: 'static> Element for Text<S> {
&mut self,
layout: Layout,
_: &mut Self::State,
- paint_state: &mut Self::FrameState,
+ frame_state: &mut Self::FrameState,
cx: &mut ViewContext<S>,
) -> 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<S: 'static> Element for Text<S> {
}
}
-pub struct TextLayout {
+pub struct TextFrameState {
line: Arc<Line>,
line_height: Pixels,
}
@@ -180,14 +180,30 @@ pub trait PlatformTextSystem: Send + Sync {
) -> Vec<usize>;
}
-pub trait PlatformSpriteSystem<Key> {
+pub trait PlatformAtlas<Key> {
fn get_or_insert_with(
&self,
key: Key,
build: impl FnOnce() -> (Size<DevicePixels>, Vec<u8>),
- ) -> 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<DevicePixels>,
}
+#[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<Range<usize>>;
fn marked_text_range(&self) -> Option<Range<usize>>;
@@ -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::*;
@@ -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<Key>(RwLock<MetalAtlasState<Key>>);
+
+struct MetalAtlasState<Key> {
+ device: Device,
+ texture_descriptor: TextureDescriptor,
+ textures: Vec<MetalAtlasTexture>,
+ tiles_by_key: HashMap<Key, AtlasTile>,
+}
+
+impl<Key> PlatformAtlas<Key> for MetalAtlas<Key>
+where
+ Key: Eq + Hash,
+{
+ fn get_or_insert_with(
+ &self,
+ key: Key,
+ build: impl FnOnce() -> (Size<DevicePixels>, Vec<u8>),
+ ) -> 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<Key> MetalAtlasState<Key> {
+ fn push_texture(&mut self, min_size: Size<DevicePixels>) -> &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<DevicePixels>, bytes: &[u8]) -> Option<AtlasTile> {
+ 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<Size<DevicePixels>> for etagere::Size {
+ fn from(size: Size<DevicePixels>) -> Self {
+ etagere::Size::new(u32::from(size.width) as i32, u32::from(size.width) as i32)
+ }
+}
+
+impl From<etagere::Point> for Point<DevicePixels> {
+ fn from(value: etagere::Point) -> Self {
+ Point {
+ x: DevicePixels::from(value.x as u32),
+ y: DevicePixels::from(value.y as u32),
+ }
+ }
+}
+
+impl From<etagere::Size> for Size<DevicePixels> {
+ fn from(size: etagere::Size) -> Self {
+ Size {
+ width: DevicePixels::from(size.width as u32),
+ height: DevicePixels::from(size.height as u32),
+ }
+ }
+}
+
+impl From<etagere::Rectangle> for Bounds<DevicePixels> {
+ fn from(rectangle: etagere::Rectangle) -> Self {
+ Bounds {
+ origin: rectangle.min.into(),
+ size: rectangle.size().into(),
+ }
+ }
+}
@@ -1 +0,0 @@
-
@@ -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<Quad> for Primitive {
pub struct MonochromeSprite {
pub order: u32,
pub bounds: Bounds<Pixels>,
- pub atlas_id: AtlasId,
- pub tile_id: TileId,
- pub bounds_in_atlas: Bounds<DevicePixels>,
- pub color: Option<Hsla>,
+ pub color: Hsla,
+ pub tile: AtlasTile,
}
impl MonochromeSprite {