Checkpoint

This commit is contained in:
Antonio Scandurra 2023-10-03 15:23:49 +02:00
parent 12ba10bc2c
commit 08464ee26e
9 changed files with 121 additions and 62 deletions

View file

@ -6,8 +6,8 @@ mod mac;
mod test; mod test;
use crate::{ use crate::{
AnyWindowHandle, Bounds, DevicePixels, Font, FontId, FontMetrics, GlyphId, LineLayout, AnyWindowHandle, Bounds, DevicePixels, Font, FontId, FontMetrics, GlyphId, MonochromeSprite,
MonochromeSprite, Pixels, Point, Result, Scene, SharedString, Size, Pixels, Point, RasterizedGlyphId, Result, Scene, ShapedLine, SharedString, Size,
}; };
use anyhow::anyhow; use anyhow::anyhow;
use async_task::Runnable; use async_task::Runnable;
@ -146,6 +146,8 @@ pub trait PlatformWindow {
fn on_appearance_changed(&self, callback: Box<dyn FnMut()>); fn on_appearance_changed(&self, callback: Box<dyn FnMut()>);
fn is_topmost_for_position(&self, position: Point<Pixels>) -> bool; fn is_topmost_for_position(&self, position: Point<Pixels>) -> bool;
fn draw(&self, scene: Scene); fn draw(&self, scene: Scene);
fn glyph_atlas(&self) -> Arc<dyn PlatformAtlas<RasterizedGlyphId>>;
} }
pub trait PlatformDispatcher: Send + Sync { pub trait PlatformDispatcher: Send + Sync {
@ -170,7 +172,7 @@ pub trait PlatformTextSystem: Send + Sync {
scale_factor: f32, scale_factor: f32,
options: RasterizationOptions, options: RasterizationOptions,
) -> Option<(Bounds<u32>, Vec<u8>)>; ) -> Option<(Bounds<u32>, Vec<u8>)>;
fn layout_line(&self, text: &str, font_size: Pixels, runs: &[(usize, FontId)]) -> LineLayout; fn layout_line(&self, text: &str, font_size: Pixels, runs: &[(usize, FontId)]) -> ShapedLine;
fn wrap_line( fn wrap_line(
&self, &self,
text: &str, text: &str,
@ -180,11 +182,11 @@ pub trait PlatformTextSystem: Send + Sync {
) -> Vec<usize>; ) -> Vec<usize>;
} }
pub trait PlatformAtlas<Key> { pub trait PlatformAtlas<Key>: Send + Sync {
fn get_or_insert_with( fn get_or_insert_with(
&self, &self,
key: Key, key: Key,
build: impl FnOnce() -> (Size<DevicePixels>, Vec<u8>), build: &dyn Fn() -> (Size<DevicePixels>, Vec<u8>),
) -> AtlasTile; ) -> AtlasTile;
fn clear(&self); fn clear(&self);

View file

@ -1,35 +1,54 @@
use crate::{AtlasTextureId, AtlasTile, Bounds, DevicePixels, PlatformAtlas, Point, Size}; use crate::{AtlasTextureId, AtlasTile, Bounds, DevicePixels, PlatformAtlas, Point, Size};
use collections::HashMap; use collections::HashMap;
use derive_more::{Deref, DerefMut};
use etagere::BucketedAtlasAllocator; use etagere::BucketedAtlasAllocator;
use foreign_types::ForeignType; use foreign_types::ForeignType;
use metal::{Device, TextureDescriptor, TextureDescriptorRef}; use metal::{Device, TextureDescriptor};
use objc::{msg_send, sel, sel_impl}; use objc::{msg_send, sel, sel_impl};
use parking_lot::{RwLock, RwLockUpgradableReadGuard}; use parking_lot::Mutex;
use std::hash::Hash; use std::hash::Hash;
pub struct MetalAtlas<Key>(RwLock<MetalAtlasState<Key>>); pub struct MetalAtlas<Key>(Mutex<MetalAtlasState<Key>>);
impl<Key> MetalAtlas<Key> {
pub fn new(
size: Size<DevicePixels>,
pixel_format: metal::MTLPixelFormat,
device: Device,
) -> Self {
let texture_descriptor = metal::TextureDescriptor::new();
texture_descriptor.set_pixel_format(pixel_format);
texture_descriptor.set_width(size.width.into());
texture_descriptor.set_height(size.height.into());
MetalAtlas(Mutex::new(MetalAtlasState {
device: AssertSend(device),
texture_descriptor: AssertSend(texture_descriptor),
textures: Default::default(),
tiles_by_key: Default::default(),
}))
}
}
struct MetalAtlasState<Key> { struct MetalAtlasState<Key> {
device: Device, device: AssertSend<Device>,
texture_descriptor: TextureDescriptor, texture_descriptor: AssertSend<TextureDescriptor>,
textures: Vec<MetalAtlasTexture>, textures: Vec<MetalAtlasTexture>,
tiles_by_key: HashMap<Key, AtlasTile>, tiles_by_key: HashMap<Key, AtlasTile>,
} }
impl<Key> PlatformAtlas<Key> for MetalAtlas<Key> impl<Key> PlatformAtlas<Key> for MetalAtlas<Key>
where where
Key: Eq + Hash, Key: Eq + Hash + Send,
{ {
fn get_or_insert_with( fn get_or_insert_with(
&self, &self,
key: Key, key: Key,
build: impl FnOnce() -> (Size<DevicePixels>, Vec<u8>), build: &dyn Fn() -> (Size<DevicePixels>, Vec<u8>),
) -> AtlasTile { ) -> AtlasTile {
let lock = self.0.upgradable_read(); let mut lock = self.0.lock();
if let Some(tile) = lock.tiles_by_key.get(&key) { if let Some(tile) = lock.tiles_by_key.get(&key) {
return tile.clone(); return tile.clone();
} else { } else {
let mut lock = RwLockUpgradableReadGuard::upgrade(lock);
let (size, bytes) = build(); let (size, bytes) = build();
lock.textures lock.textures
.iter_mut() .iter_mut()
@ -45,7 +64,7 @@ where
} }
fn clear(&self) { fn clear(&self) {
self.0.write().tiles_by_key.clear(); self.0.lock().tiles_by_key.clear();
} }
} }
@ -62,7 +81,7 @@ impl<Key> MetalAtlasState<Key> {
{ {
let descriptor = unsafe { let descriptor = unsafe {
let descriptor_ptr: *mut metal::MTLTextureDescriptor = let descriptor_ptr: *mut metal::MTLTextureDescriptor =
msg_send![self.texture_descriptor, copy]; msg_send![*self.texture_descriptor, copy];
metal::TextureDescriptor::from_ptr(descriptor_ptr) metal::TextureDescriptor::from_ptr(descriptor_ptr)
}; };
descriptor.set_width(min_size.width.into()); descriptor.set_width(min_size.width.into());
@ -78,7 +97,7 @@ impl<Key> MetalAtlasState<Key> {
let atlas_texture = MetalAtlasTexture { let atlas_texture = MetalAtlasTexture {
id: AtlasTextureId(self.textures.len()), id: AtlasTextureId(self.textures.len()),
allocator: etagere::BucketedAtlasAllocator::new(size.into()), allocator: etagere::BucketedAtlasAllocator::new(size.into()),
metal_texture, metal_texture: AssertSend(metal_texture),
}; };
self.textures.push(atlas_texture); self.textures.push(atlas_texture);
self.textures.last_mut().unwrap() self.textures.last_mut().unwrap()
@ -88,7 +107,7 @@ impl<Key> MetalAtlasState<Key> {
struct MetalAtlasTexture { struct MetalAtlasTexture {
id: AtlasTextureId, id: AtlasTextureId,
allocator: BucketedAtlasAllocator, allocator: BucketedAtlasAllocator,
metal_texture: metal::Texture, metal_texture: AssertSend<metal::Texture>,
} }
impl MetalAtlasTexture { impl MetalAtlasTexture {
@ -162,3 +181,8 @@ impl From<etagere::Rectangle> for Bounds<DevicePixels> {
} }
} }
} }
#[derive(Deref, DerefMut)]
struct AssertSend<T>(T);
unsafe impl<T> Send for AssertSend<T> {}

View file

@ -1,4 +1,6 @@
use crate::{point, size, DevicePixels, MonochromeSprite, Quad, Scene, Size}; use crate::{
point, size, DevicePixels, MetalAtlas, MonochromeSprite, Quad, RasterizedGlyphId, Scene, Size,
};
use bytemuck::{Pod, Zeroable}; use bytemuck::{Pod, Zeroable};
use cocoa::{ use cocoa::{
base::{NO, YES}, base::{NO, YES},
@ -7,7 +9,7 @@ use cocoa::{
}; };
use metal::{CommandQueue, MTLPixelFormat, MTLResourceOptions, NSRange}; use metal::{CommandQueue, MTLPixelFormat, MTLResourceOptions, NSRange};
use objc::{self, msg_send, sel, sel_impl}; use objc::{self, msg_send, sel, sel_impl};
use std::{ffi::c_void, mem, ptr}; use std::{ffi::c_void, mem, ptr, sync::Arc};
const SHADERS_METALLIB: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/shaders.metallib")); const SHADERS_METALLIB: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/shaders.metallib"));
const INSTANCE_BUFFER_SIZE: usize = 8192 * 1024; // This is an arbitrary decision. There's probably a more optimal value. const INSTANCE_BUFFER_SIZE: usize = 8192 * 1024; // This is an arbitrary decision. There's probably a more optimal value.
@ -19,6 +21,7 @@ pub struct MetalRenderer {
quad_pipeline_state: metal::RenderPipelineState, quad_pipeline_state: metal::RenderPipelineState,
unit_vertices: metal::Buffer, unit_vertices: metal::Buffer,
instances: metal::Buffer, instances: metal::Buffer,
glyph_atlas: Arc<MetalAtlas<RasterizedGlyphId>>,
} }
impl MetalRenderer { impl MetalRenderer {
@ -88,6 +91,15 @@ impl MetalRenderer {
); );
let command_queue = device.new_command_queue(); let command_queue = device.new_command_queue();
let glyph_atlas = Arc::new(MetalAtlas::new(
Size {
width: DevicePixels(1024),
height: DevicePixels(1024),
},
MTLPixelFormat::A8Unorm,
device.clone(),
));
Self { Self {
device, device,
layer, layer,
@ -95,6 +107,7 @@ impl MetalRenderer {
quad_pipeline_state, quad_pipeline_state,
unit_vertices, unit_vertices,
instances, instances,
glyph_atlas,
} }
} }
@ -102,6 +115,10 @@ impl MetalRenderer {
&*self.layer &*self.layer
} }
pub fn glyph_atlas(&self) -> &Arc<MetalAtlas<RasterizedGlyphId>> {
&self.glyph_atlas
}
pub fn draw(&mut self, scene: &mut Scene) { pub fn draw(&mut self, scene: &mut Scene) {
let layer = self.layer.clone(); let layer = self.layer.clone();
let viewport_size = layer.drawable_size(); let viewport_size = layer.drawable_size();

View file

@ -1,7 +1,7 @@
use crate::{ use crate::{
point, px, size, Bounds, Font, FontFeatures, FontId, FontMetrics, FontStyle, FontWeight, Glyph, point, px, size, Bounds, Font, FontFeatures, FontId, FontMetrics, FontStyle, FontWeight,
GlyphId, LineLayout, Pixels, PlatformTextSystem, Point, RasterizationOptions, Result, Run, GlyphId, Pixels, PlatformTextSystem, Point, RasterizationOptions, Result, ShapedGlyph,
SharedString, Size, ShapedLine, ShapedRun, SharedString, Size,
}; };
use cocoa::appkit::{CGFloat, CGPoint}; use cocoa::appkit::{CGFloat, CGPoint};
use collections::HashMap; use collections::HashMap;
@ -161,7 +161,7 @@ impl PlatformTextSystem for MacTextSystem {
text: &str, text: &str,
font_size: Pixels, font_size: Pixels,
font_runs: &[(usize, FontId)], font_runs: &[(usize, FontId)],
) -> LineLayout { ) -> ShapedLine {
self.0.write().layout_line(text, font_size, font_runs) self.0.write().layout_line(text, font_size, font_runs)
} }
@ -348,7 +348,7 @@ impl MacTextSystemState {
text: &str, text: &str,
font_size: Pixels, font_size: Pixels,
font_runs: &[(usize, FontId)], font_runs: &[(usize, FontId)],
) -> LineLayout { ) -> ShapedLine {
// Construct the attributed string, converting UTF8 ranges to UTF16 ranges. // Construct the attributed string, converting UTF8 ranges to UTF16 ranges.
let mut string = CFMutableAttributedString::new(); let mut string = CFMutableAttributedString::new();
{ {
@ -409,7 +409,7 @@ impl MacTextSystemState {
{ {
let glyph_utf16_ix = usize::try_from(*glyph_utf16_ix).unwrap(); let glyph_utf16_ix = usize::try_from(*glyph_utf16_ix).unwrap();
ix_converter.advance_to_utf16_ix(glyph_utf16_ix); ix_converter.advance_to_utf16_ix(glyph_utf16_ix);
glyphs.push(Glyph { glyphs.push(ShapedGlyph {
id: (*glyph_id).into(), id: (*glyph_id).into(),
position: point(position.x as f32, position.y as f32).map(px), position: point(position.x as f32, position.y as f32).map(px),
index: ix_converter.utf8_ix, index: ix_converter.utf8_ix,
@ -417,11 +417,11 @@ impl MacTextSystemState {
}); });
} }
runs.push(Run { font_id, glyphs }) runs.push(ShapedRun { font_id, glyphs })
} }
let typographic_bounds = line.get_typographic_bounds(); let typographic_bounds = line.get_typographic_bounds();
LineLayout { ShapedLine {
width: typographic_bounds.width.into(), width: typographic_bounds.width.into(),
ascent: typographic_bounds.ascent.into(), ascent: typographic_bounds.ascent.into(),
descent: typographic_bounds.descent.into(), descent: typographic_bounds.descent.into(),

View file

@ -1,10 +1,10 @@
use super::{ns_string, MetalRenderer, NSRange}; use super::{ns_string, MetalRenderer, NSRange};
use crate::{ use crate::{
point, px, size, AnyWindowHandle, Bounds, Event, KeyDownEvent, Keystroke, MacScreen, Modifiers, point, px, size, AnyWindowHandle, Bounds, Event, KeyDownEvent, Keystroke, MacScreen,
ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMovedEvent, MouseUpEvent, NSRectExt, MetalAtlas, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMovedEvent,
Pixels, Platform, PlatformDispatcher, PlatformInputHandler, PlatformScreen, PlatformWindow, MouseUpEvent, NSRectExt, Pixels, Platform, PlatformAtlas, PlatformDispatcher,
Point, Scene, Size, Timer, WindowAppearance, WindowBounds, WindowKind, WindowOptions, PlatformInputHandler, PlatformScreen, PlatformWindow, Point, RasterizedGlyphId, Scene, Size,
WindowPromptLevel, Timer, WindowAppearance, WindowBounds, WindowKind, WindowOptions, WindowPromptLevel,
}; };
use block::ConcreteBlock; use block::ConcreteBlock;
use cocoa::{ use cocoa::{
@ -20,6 +20,7 @@ use core_graphics::display::CGRect;
use ctor::ctor; use ctor::ctor;
use foreign_types::ForeignTypeRef; use foreign_types::ForeignTypeRef;
use futures::channel::oneshot; use futures::channel::oneshot;
use metal::{MTLPixelFormat, TextureDescriptor};
use objc::{ use objc::{
class, class,
declare::ClassDecl, declare::ClassDecl,
@ -885,6 +886,10 @@ impl PlatformWindow for MacWindow {
let _: () = msg_send![this.native_window.contentView(), setNeedsDisplay: YES]; let _: () = msg_send![this.native_window.contentView(), setNeedsDisplay: YES];
} }
} }
fn glyph_atlas(&self) -> Arc<dyn PlatformAtlas<RasterizedGlyphId>> {
self.0.lock().renderer.glyph_atlas().clone()
}
} }
fn get_scale_factor(native_window: id) -> f32 { fn get_scale_factor(native_window: id) -> f32 {

View file

@ -346,28 +346,35 @@ impl From<u32> for GlyphId {
} }
} }
#[derive(Default, Debug)]
pub struct ShapedLine {
pub font_size: Pixels,
pub width: Pixels,
pub ascent: Pixels,
pub descent: Pixels,
pub runs: Vec<ShapedRun>,
pub len: usize,
}
#[derive(Debug)]
pub struct ShapedRun {
pub font_id: FontId,
pub glyphs: Vec<ShapedGlyph>,
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Glyph { pub struct ShapedGlyph {
pub id: GlyphId, pub id: GlyphId,
pub position: Point<Pixels>, pub position: Point<Pixels>,
pub index: usize, pub index: usize,
pub is_emoji: bool, pub is_emoji: bool,
} }
#[derive(Default, Debug)] #[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct LineLayout { pub struct RasterizedGlyphId {
pub font_size: Pixels, font_id: FontId,
pub width: Pixels, glyph_id: GlyphId,
pub ascent: Pixels, font_size: Pixels,
pub descent: Pixels,
pub runs: Vec<Run>,
pub len: usize,
}
#[derive(Debug)]
pub struct Run {
pub font_id: FontId,
pub glyphs: Vec<Glyph>,
} }
#[derive(Clone, Debug, Eq, PartialEq, Hash)] #[derive(Clone, Debug, Eq, PartialEq, Hash)]

View file

@ -1,6 +1,6 @@
use crate::{ use crate::{
black, point, px, Bounds, FontId, Hsla, Layout, LineLayout, Pixels, Point, Run, RunStyle, black, point, px, Bounds, FontId, Hsla, Layout, Pixels, Point, RunStyle, ShapedBoundary,
ShapedBoundary, UnderlineStyle, WindowContext, ShapedLine, ShapedRun, UnderlineStyle, WindowContext,
}; };
use anyhow::Result; use anyhow::Result;
use smallvec::SmallVec; use smallvec::SmallVec;
@ -8,7 +8,7 @@ use std::sync::Arc;
#[derive(Default, Debug, Clone)] #[derive(Default, Debug, Clone)]
pub struct Line { pub struct Line {
layout: Arc<LineLayout>, layout: Arc<ShapedLine>,
style_runs: SmallVec<[StyleRun; 32]>, style_runs: SmallVec<[StyleRun; 32]>,
} }
@ -20,7 +20,7 @@ struct StyleRun {
} }
impl Line { impl Line {
pub fn new(layout: Arc<LineLayout>, runs: &[(usize, RunStyle)]) -> Self { pub fn new(layout: Arc<ShapedLine>, runs: &[(usize, RunStyle)]) -> Self {
let mut style_runs = SmallVec::new(); let mut style_runs = SmallVec::new();
for (len, style) in runs { for (len, style) in runs {
style_runs.push(StyleRun { style_runs.push(StyleRun {
@ -32,7 +32,7 @@ impl Line {
Self { layout, style_runs } Self { layout, style_runs }
} }
pub fn runs(&self) -> &[Run] { pub fn runs(&self) -> &[ShapedRun] {
&self.layout.runs &self.layout.runs
} }

View file

@ -1,4 +1,4 @@
use crate::{FontId, Glyph, LineLayout, Pixels, PlatformTextSystem, Run}; use crate::{FontId, Pixels, PlatformTextSystem, ShapedGlyph, ShapedLine, ShapedRun};
use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard}; use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
use smallvec::SmallVec; use smallvec::SmallVec;
use std::{ use std::{
@ -9,8 +9,8 @@ use std::{
}; };
pub(crate) struct TextLayoutCache { pub(crate) struct TextLayoutCache {
prev_frame: Mutex<HashMap<CacheKeyValue, Arc<LineLayout>>>, prev_frame: Mutex<HashMap<CacheKeyValue, Arc<ShapedLine>>>,
curr_frame: RwLock<HashMap<CacheKeyValue, Arc<LineLayout>>>, curr_frame: RwLock<HashMap<CacheKeyValue, Arc<ShapedLine>>>,
platform_text_system: Arc<dyn PlatformTextSystem>, platform_text_system: Arc<dyn PlatformTextSystem>,
} }
@ -35,7 +35,7 @@ impl TextLayoutCache {
text: &'a str, text: &'a str,
font_size: Pixels, font_size: Pixels,
runs: &[(usize, FontId)], runs: &[(usize, FontId)],
) -> Arc<LineLayout> { ) -> Arc<ShapedLine> {
let key = &CacheKeyRef { let key = &CacheKeyRef {
text, text,
font_size, font_size,
@ -146,8 +146,8 @@ pub struct ShapedBoundary {
pub glyph_ix: usize, pub glyph_ix: usize,
} }
impl Run { impl ShapedRun {
pub fn glyphs(&self) -> &[Glyph] { pub fn glyphs(&self) -> &[ShapedGlyph] {
&self.glyphs &self.glyphs
} }
} }

View file

@ -1,7 +1,8 @@
use crate::{ use crate::{
px, AnyView, AppContext, AvailableSpace, Bounds, Context, Effect, Element, EntityId, Handle, px, AnyView, AppContext, AvailableSpace, Bounds, Context, Effect, Element, EntityId, FontId,
LayoutId, MainThread, MainThreadOnly, Pixels, PlatformWindow, Point, Reference, Scene, Size, GlyphId, Handle, LayoutId, MainThread, MainThreadOnly, Pixels, PlatformAtlas, PlatformWindow,
StackContext, StackingOrder, Style, TaffyLayoutEngine, WeakHandle, WindowOptions, Point, RasterizedGlyphId, Reference, Scene, Size, StackContext, StackingOrder, Style,
TaffyLayoutEngine, WeakHandle, WindowOptions,
}; };
use anyhow::Result; use anyhow::Result;
use futures::Future; use futures::Future;
@ -14,6 +15,7 @@ pub struct AnyWindow {}
pub struct Window { pub struct Window {
handle: AnyWindowHandle, handle: AnyWindowHandle,
platform_window: MainThreadOnly<Box<dyn PlatformWindow>>, platform_window: MainThreadOnly<Box<dyn PlatformWindow>>,
glyph_atlas: Arc<dyn PlatformAtlas<RasterizedGlyphId>>,
rem_size: Pixels, rem_size: Pixels,
content_size: Size<Pixels>, content_size: Size<Pixels>,
layout_engine: TaffyLayoutEngine, layout_engine: TaffyLayoutEngine,
@ -31,6 +33,7 @@ impl Window {
cx: &mut MainThread<AppContext>, cx: &mut MainThread<AppContext>,
) -> Self { ) -> Self {
let platform_window = cx.platform().open_window(handle, options); let platform_window = cx.platform().open_window(handle, options);
let glyph_atlas = platform_window.glyph_atlas();
let mouse_position = platform_window.mouse_position(); let mouse_position = platform_window.mouse_position();
let content_size = platform_window.content_size(); let content_size = platform_window.content_size();
let scale_factor = platform_window.scale_factor(); let scale_factor = platform_window.scale_factor();
@ -53,6 +56,7 @@ impl Window {
Window { Window {
handle, handle,
platform_window, platform_window,
glyph_atlas,
rem_size: px(16.), rem_size: px(16.),
content_size, content_size,
layout_engine: TaffyLayoutEngine::new(), layout_engine: TaffyLayoutEngine::new(),