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> {
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,
}

View File

@ -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>>;

View File

@ -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::*;

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 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 {