Checkpoint

This commit is contained in:
Antonio Scandurra 2023-10-03 14:48:08 +02:00
parent dcaf4c905f
commit 12ba10bc2c
6 changed files with 196 additions and 24 deletions

View file

@ -32,7 +32,7 @@ pub struct Text<S> {
impl<S: 'static> Element for Text<S> { impl<S: 'static> Element for Text<S> {
type State = S; type State = S;
type FrameState = Arc<Mutex<Option<TextLayout>>>; type FrameState = Arc<Mutex<Option<TextFrameState>>>;
fn layout( fn layout(
&mut self, &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 layout_id = cx.request_measured_layout(Default::default(), rem_size, {
let frame_state = paint_state.clone(); let frame_state = paint_state.clone();
move |_, _| { move |_, _| {
dbg!("starting measurement");
let Some(line_layout) = text_system let Some(line_layout) = text_system
.layout_line( .layout_line(
text.as_ref(), text.as_ref(),
@ -65,23 +64,21 @@ impl<S: 'static> Element for Text<S> {
else { else {
return Size::default(); return Size::default();
}; };
dbg!("bbbb");
let size = Size { let size = Size {
width: line_layout.width(), width: line_layout.width(),
height: line_height, height: line_height,
}; };
frame_state.lock().replace(TextLayout { frame_state.lock().replace(TextFrameState {
line: Arc::new(line_layout), line: Arc::new(line_layout),
line_height, line_height,
}); });
dbg!(size) size
} }
}); });
dbg!("got to end of text layout");
Ok((layout_id?, paint_state)) Ok((layout_id?, paint_state))
} }
@ -89,22 +86,20 @@ impl<S: 'static> Element for Text<S> {
&mut self, &mut self,
layout: Layout, layout: Layout,
_: &mut Self::State, _: &mut Self::State,
paint_state: &mut Self::FrameState, frame_state: &mut Self::FrameState,
cx: &mut ViewContext<S>, cx: &mut ViewContext<S>,
) -> Result<()> { ) -> Result<()> {
let line; let line;
let line_height; let line_height;
{ {
let paint_state = paint_state.lock(); let frame_state = frame_state.lock();
let paint_state = paint_state let frame_state = frame_state
.as_ref() .as_ref()
.expect("measurement has not been performed"); .expect("measurement has not been performed");
line = paint_state.line.clone(); line = frame_state.line.clone();
line_height = paint_state.line_height; 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."); // todo!("We haven't added visible bounds to the new element system yet, so this is a placeholder.");
let visible_bounds = layout.bounds; let visible_bounds = layout.bounds;
line.paint(&layout, visible_bounds, line_height, cx)?; 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: Arc<Line>,
line_height: Pixels, line_height: Pixels,
} }

View file

@ -180,14 +180,30 @@ pub trait PlatformTextSystem: Send + Sync {
) -> Vec<usize>; ) -> Vec<usize>;
} }
pub trait PlatformSpriteSystem<Key> { pub trait PlatformAtlas<Key> {
fn get_or_insert_with( fn get_or_insert_with(
&self, &self,
key: Key, key: Key,
build: impl FnOnce() -> (Size<DevicePixels>, Vec<u8>), 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 { pub trait PlatformInputHandler {
fn selected_text_range(&self) -> Option<Range<usize>>; fn selected_text_range(&self) -> Option<Range<usize>>;
fn marked_text_range(&self) -> Option<Range<usize>>; fn marked_text_range(&self) -> Option<Range<usize>>;

View file

@ -2,11 +2,11 @@
///! an origin at the bottom left of the main display. ///! an origin at the bottom left of the main display.
mod dispatcher; mod dispatcher;
mod events; mod events;
mod metal_atlas;
mod metal_renderer; mod metal_renderer;
mod open_type; mod open_type;
mod platform; mod platform;
mod screen; mod screen;
mod sprite;
mod text_system; mod text_system;
mod window; mod window;
mod window_appearence; mod window_appearence;
@ -31,9 +31,9 @@ use std::{
}; };
pub use dispatcher::*; pub use dispatcher::*;
pub use metal_atlas::*;
pub use platform::*; pub use platform::*;
pub use screen::*; pub use screen::*;
pub use sprite::*;
pub use text_system::*; pub use text_system::*;
pub use window::*; pub use window::*;

View file

@ -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(),
}
}
}

View file

@ -1 +0,0 @@

View file

@ -1,7 +1,7 @@
use std::{iter::Peekable, mem}; use std::{iter::Peekable, mem};
use super::{Bounds, Hsla, Pixels, Point}; use super::{Bounds, Hsla, Pixels, Point};
use crate::{Corners, DevicePixels, Edges}; use crate::{AtlasTile, Corners, DevicePixels, Edges};
use bytemuck::{Pod, Zeroable}; use bytemuck::{Pod, Zeroable};
// Exported to metal // Exported to metal
@ -243,10 +243,8 @@ impl From<Quad> for Primitive {
pub struct MonochromeSprite { pub struct MonochromeSprite {
pub order: u32, pub order: u32,
pub bounds: Bounds<Pixels>, pub bounds: Bounds<Pixels>,
pub atlas_id: AtlasId, pub color: Hsla,
pub tile_id: TileId, pub tile: AtlasTile,
pub bounds_in_atlas: Bounds<DevicePixels>,
pub color: Option<Hsla>,
} }
impl MonochromeSprite { impl MonochromeSprite {