Compare commits

...
Sign in to create a new pull request.

1 commit

Author SHA1 Message Date
Thorsten Ball
7299e8d09d WIP: atlas perf improv 2024-01-17 16:50:37 +01:00
6 changed files with 92 additions and 104 deletions

33
crates/gpui/src/atlas.rs Normal file
View file

@ -0,0 +1,33 @@
use crate::{AtlasKey, AtlasTile, DevicePixels, PlatformAtlas, Size};
use anyhow::Result;
use collections::FxHashMap;
use std::{borrow::Cow, sync::Arc};
pub struct Atlas {
platform_atlas: Arc<dyn PlatformAtlas>,
tiles: FxHashMap<AtlasKey, AtlasTile>,
}
impl Atlas {
pub fn new(platform_atlas: Arc<dyn PlatformAtlas>) -> Self {
Self {
platform_atlas,
tiles: FxHashMap::default(),
}
}
pub fn get_or_insert_with<'a>(
&mut self,
key: &AtlasKey,
build: impl FnOnce() -> Result<(Size<DevicePixels>, Cow<'a, [u8]>)>,
) -> Result<AtlasTile> {
if let Some(tile) = self.tiles.get(key) {
Ok(tile.clone())
} else {
let (size, bytes) = build()?;
let tile = self.platform_atlas.insert(key, size, bytes);
self.tiles.insert(key.clone(), tile.clone());
Ok(tile)
}
}
}

View file

@ -4,6 +4,7 @@ mod app;
mod arena; mod arena;
mod assets; mod assets;
mod atlas;
mod color; mod color;
mod element; mod element;
mod elements; mod elements;
@ -51,6 +52,7 @@ pub use anyhow::Result;
pub use app::*; pub use app::*;
pub(crate) use arena::*; pub(crate) use arena::*;
pub use assets::*; pub use assets::*;
pub(crate) use atlas::*;
pub use color::*; pub use color::*;
pub use ctor::ctor; pub use ctor::ctor;
pub use element::*; pub use element::*;

View file

@ -263,13 +263,7 @@ impl From<RenderImageParams> for AtlasKey {
} }
pub trait PlatformAtlas: Send + Sync { pub trait PlatformAtlas: Send + Sync {
fn get_or_insert_with<'a>( fn insert(&self, key: &AtlasKey, size: Size<DevicePixels>, bytes: Cow<[u8]>) -> AtlasTile;
&self,
key: &AtlasKey,
build: &mut dyn FnMut() -> Result<(Size<DevicePixels>, Cow<'a, [u8]>)>,
) -> Result<AtlasTile>;
fn clear(&self);
} }
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]

View file

@ -2,8 +2,6 @@ use crate::{
AtlasKey, AtlasTextureId, AtlasTextureKind, AtlasTile, Bounds, DevicePixels, PlatformAtlas, AtlasKey, AtlasTextureId, AtlasTextureKind, AtlasTile, Bounds, DevicePixels, PlatformAtlas,
Point, Size, Point, Size,
}; };
use anyhow::Result;
use collections::FxHashMap;
use derive_more::{Deref, DerefMut}; use derive_more::{Deref, DerefMut};
use etagere::BucketedAtlasAllocator; use etagere::BucketedAtlasAllocator;
use metal::Device; use metal::Device;
@ -19,7 +17,6 @@ impl MetalAtlas {
monochrome_textures: Default::default(), monochrome_textures: Default::default(),
polychrome_textures: Default::default(), polychrome_textures: Default::default(),
path_textures: Default::default(), path_textures: Default::default(),
tiles_by_key: Default::default(),
})) }))
} }
@ -53,41 +50,29 @@ struct MetalAtlasState {
monochrome_textures: Vec<MetalAtlasTexture>, monochrome_textures: Vec<MetalAtlasTexture>,
polychrome_textures: Vec<MetalAtlasTexture>, polychrome_textures: Vec<MetalAtlasTexture>,
path_textures: Vec<MetalAtlasTexture>, path_textures: Vec<MetalAtlasTexture>,
tiles_by_key: FxHashMap<AtlasKey, AtlasTile>,
} }
impl PlatformAtlas for MetalAtlas { impl PlatformAtlas for MetalAtlas {
fn get_or_insert_with<'a>( fn insert(&self, key: &AtlasKey, size: Size<DevicePixels>, bytes: Cow<[u8]>) -> AtlasTile {
&self,
key: &AtlasKey,
build: &mut dyn FnMut() -> Result<(Size<DevicePixels>, Cow<'a, [u8]>)>,
) -> Result<AtlasTile> {
let mut lock = self.0.lock(); let mut lock = self.0.lock();
if let Some(tile) = lock.tiles_by_key.get(key) { let tile = lock.allocate(size, key.texture_kind());
Ok(tile.clone()) let texture = lock.texture(tile.texture_id);
} else { texture.upload(tile.bounds, &bytes);
let (size, bytes) = build()?; tile
let tile = lock.allocate(size, key.texture_kind());
let texture = lock.texture(tile.texture_id);
texture.upload(tile.bounds, &bytes);
lock.tiles_by_key.insert(key.clone(), tile.clone());
Ok(tile)
}
} }
fn clear(&self) { // fn clear(&self) {
let mut lock = self.0.lock(); // let mut lock = self.0.lock();
lock.tiles_by_key.clear(); // for texture in &mut lock.monochrome_textures {
for texture in &mut lock.monochrome_textures { // texture.clear();
texture.clear(); // }
} // for texture in &mut lock.polychrome_textures {
for texture in &mut lock.polychrome_textures { // texture.clear();
texture.clear(); // }
} // for texture in &mut lock.path_textures {
for texture in &mut lock.path_textures { // texture.clear();
texture.clear(); // }
} // }
}
} }
impl MetalAtlasState { impl MetalAtlasState {

View file

@ -1,11 +1,12 @@
use crate::{ use crate::{
px, AnyWindowHandle, AtlasKey, AtlasTextureId, AtlasTile, Bounds, InputEvent, KeyDownEvent, px, AnyWindowHandle, AtlasKey, AtlasTextureId, AtlasTile, Bounds, DevicePixels, InputEvent,
Keystroke, Pixels, PlatformAtlas, PlatformDisplay, PlatformInputHandler, PlatformWindow, Point, KeyDownEvent, Keystroke, Pixels, PlatformAtlas, PlatformDisplay, PlatformInputHandler,
Size, TestPlatform, TileId, WindowAppearance, WindowBounds, WindowOptions, PlatformWindow, Point, Size, TestPlatform, TileId, WindowAppearance, WindowBounds,
WindowOptions,
}; };
use collections::HashMap;
use parking_lot::Mutex; use parking_lot::Mutex;
use std::{ use std::{
borrow::Cow,
rc::{Rc, Weak}, rc::{Rc, Weak},
sync::{self, Arc}, sync::{self, Arc},
}; };
@ -271,64 +272,35 @@ impl PlatformWindow for TestWindow {
pub struct TestAtlasState { pub struct TestAtlasState {
next_id: u32, next_id: u32,
tiles: HashMap<AtlasKey, AtlasTile>,
} }
pub struct TestAtlas(Mutex<TestAtlasState>); pub struct TestAtlas(Mutex<TestAtlasState>);
impl TestAtlas { impl TestAtlas {
pub fn new() -> Self { pub fn new() -> Self {
TestAtlas(Mutex::new(TestAtlasState { TestAtlas(Mutex::new(TestAtlasState { next_id: 0 }))
next_id: 0,
tiles: HashMap::default(),
}))
} }
} }
impl PlatformAtlas for TestAtlas { impl PlatformAtlas for TestAtlas {
fn get_or_insert_with<'a>( fn insert(&self, _key: &AtlasKey, size: Size<DevicePixels>, _bytes: Cow<[u8]>) -> AtlasTile {
&self,
key: &crate::AtlasKey,
build: &mut dyn FnMut() -> anyhow::Result<(
Size<crate::DevicePixels>,
std::borrow::Cow<'a, [u8]>,
)>,
) -> anyhow::Result<crate::AtlasTile> {
let mut state = self.0.lock(); let mut state = self.0.lock();
if let Some(tile) = state.tiles.get(key) {
return Ok(tile.clone());
}
state.next_id += 1; state.next_id += 1;
let texture_id = state.next_id; let texture_id = state.next_id;
state.next_id += 1; state.next_id += 1;
let tile_id = state.next_id; let tile_id = state.next_id;
drop(state); AtlasTile {
let (size, _) = build()?; texture_id: AtlasTextureId {
let mut state = self.0.lock(); index: texture_id,
kind: crate::AtlasTextureKind::Path,
state.tiles.insert(
key.clone(),
crate::AtlasTile {
texture_id: AtlasTextureId {
index: texture_id,
kind: crate::AtlasTextureKind::Path,
},
tile_id: TileId(tile_id),
bounds: crate::Bounds {
origin: Point::default(),
size,
},
}, },
); tile_id: TileId(tile_id),
bounds: Bounds {
Ok(state.tiles[key].clone()) origin: Point::default(),
} size,
},
fn clear(&self) { }
let mut state = self.0.lock();
state.tiles = HashMap::default();
state.next_id = 0;
} }
} }

View file

@ -2,16 +2,16 @@
use crate::{ use crate::{
px, size, transparent_black, Action, AnyDrag, AnyTooltip, AnyView, AppContext, Arena, px, size, transparent_black, Action, AnyDrag, AnyTooltip, AnyView, AppContext, Arena,
AsyncWindowContext, AvailableSpace, Bounds, BoxShadow, Context, Corners, CursorStyle, AsyncWindowContext, Atlas, AvailableSpace, Bounds, BoxShadow, Context, Corners, CursorStyle,
DevicePixels, DispatchActionListener, DispatchNodeId, DispatchTree, DisplayId, Edges, Effect, DevicePixels, DispatchActionListener, DispatchNodeId, DispatchTree, DisplayId, Edges, Effect,
Entity, EntityId, EventEmitter, FileDropEvent, Flatten, FontId, GlobalElementId, GlyphId, Hsla, Entity, EntityId, EventEmitter, FileDropEvent, Flatten, FontId, GlobalElementId, GlyphId, Hsla,
ImageData, InputEvent, IsZero, KeyBinding, KeyContext, KeyDownEvent, KeystrokeEvent, LayoutId, ImageData, InputEvent, IsZero, KeyBinding, KeyContext, KeyDownEvent, KeystrokeEvent, LayoutId,
Model, ModelContext, Modifiers, MonochromeSprite, MouseButton, MouseMoveEvent, MouseUpEvent, Model, ModelContext, Modifiers, MonochromeSprite, MouseButton, MouseMoveEvent, MouseUpEvent,
Path, Pixels, PlatformAtlas, PlatformDisplay, PlatformInputHandler, PlatformWindow, Point, Path, Pixels, PlatformDisplay, PlatformInputHandler, PlatformWindow, Point, PolychromeSprite,
PolychromeSprite, PromptLevel, Quad, Render, RenderGlyphParams, RenderImageParams, PromptLevel, Quad, Render, RenderGlyphParams, RenderImageParams, RenderSvgParams, ScaledPixels,
RenderSvgParams, ScaledPixels, Scene, Shadow, SharedString, Size, Style, SubscriberSet, Scene, Shadow, SharedString, Size, Style, SubscriberSet, Subscription, Surface,
Subscription, Surface, TaffyLayoutEngine, Task, Underline, UnderlineStyle, View, VisualContext, TaffyLayoutEngine, Task, Underline, UnderlineStyle, View, VisualContext, WeakView,
WeakView, WindowBounds, WindowOptions, SUBPIXEL_VARIANTS, WindowBounds, WindowOptions, SUBPIXEL_VARIANTS,
}; };
use anyhow::{anyhow, Context as _, Result}; use anyhow::{anyhow, Context as _, Result};
use collections::{FxHashMap, FxHashSet}; use collections::{FxHashMap, FxHashSet};
@ -258,7 +258,7 @@ pub struct Window {
pub(crate) removed: bool, pub(crate) removed: bool,
pub(crate) platform_window: Box<dyn PlatformWindow>, pub(crate) platform_window: Box<dyn PlatformWindow>,
display_id: DisplayId, display_id: DisplayId,
sprite_atlas: Arc<dyn PlatformAtlas>, sprite_atlas: Atlas,
rem_size: Pixels, rem_size: Pixels,
viewport_size: Size<Pixels>, viewport_size: Size<Pixels>,
layout_engine: Option<TaffyLayoutEngine>, layout_engine: Option<TaffyLayoutEngine>,
@ -414,7 +414,7 @@ impl Window {
) -> Self { ) -> Self {
let platform_window = cx.platform.open_window(handle, options); let platform_window = cx.platform.open_window(handle, options);
let display_id = platform_window.display().id(); let display_id = platform_window.display().id();
let sprite_atlas = platform_window.sprite_atlas(); let sprite_atlas = Atlas::new(platform_window.sprite_atlas());
let mouse_position = platform_window.mouse_position(); let mouse_position = platform_window.mouse_position();
let modifiers = platform_window.modifiers(); let modifiers = platform_window.modifiers();
let content_size = platform_window.content_size(); let content_size = platform_window.content_size();
@ -424,7 +424,9 @@ impl Window {
platform_window.on_request_frame(Box::new({ platform_window.on_request_frame(Box::new({
let mut cx = cx.to_async(); let mut cx = cx.to_async();
move || { move || {
let t0 = std::time::Instant::now();
handle.update(&mut cx, |_, cx| cx.draw()).log_err(); handle.update(&mut cx, |_, cx| cx.draw()).log_err();
dbg!(t0.elapsed());
} }
})); }));
platform_window.on_resize(Box::new({ platform_window.on_resize(Box::new({
@ -1255,8 +1257,8 @@ impl<'a> WindowContext<'a> {
let tile = let tile =
self.window self.window
.sprite_atlas .sprite_atlas
.get_or_insert_with(&params.clone().into(), &mut || { .get_or_insert_with(&params.clone().into(), || {
let (size, bytes) = self.text_system().rasterize_glyph(&params)?; let (size, bytes) = self.app.text_system().rasterize_glyph(&params)?;
Ok((size, Cow::Owned(bytes))) Ok((size, Cow::Owned(bytes)))
})?; })?;
let bounds = Bounds { let bounds = Bounds {
@ -1308,8 +1310,8 @@ impl<'a> WindowContext<'a> {
let tile = let tile =
self.window self.window
.sprite_atlas .sprite_atlas
.get_or_insert_with(&params.clone().into(), &mut || { .get_or_insert_with(&params.clone().into(), || {
let (size, bytes) = self.text_system().rasterize_glyph(&params)?; let (size, bytes) = self.app.text_system().rasterize_glyph(&params)?;
Ok((size, Cow::Owned(bytes))) Ok((size, Cow::Owned(bytes)))
})?; })?;
let bounds = Bounds { let bounds = Bounds {
@ -1354,13 +1356,13 @@ impl<'a> WindowContext<'a> {
.map(|pixels| DevicePixels::from((pixels.0 * 2.).ceil() as i32)), .map(|pixels| DevicePixels::from((pixels.0 * 2.).ceil() as i32)),
}; };
let tile = let tile = self
self.window .window
.sprite_atlas .sprite_atlas
.get_or_insert_with(&params.clone().into(), &mut || { .get_or_insert_with(&params.clone().into(), || {
let bytes = self.svg_renderer.render(&params)?; let bytes = self.app.svg_renderer.render(&params)?;
Ok((params.size, Cow::Owned(bytes))) Ok((params.size, Cow::Owned(bytes)))
})?; })?;
let content_mask = self.content_mask().scale(scale_factor); let content_mask = self.content_mask().scale(scale_factor);
let view_id = self.parent_view_id(); let view_id = self.parent_view_id();
@ -1396,7 +1398,7 @@ impl<'a> WindowContext<'a> {
let tile = self let tile = self
.window .window
.sprite_atlas .sprite_atlas
.get_or_insert_with(&params.clone().into(), &mut || { .get_or_insert_with(&params.clone().into(), || {
Ok((data.size(), Cow::Borrowed(data.as_bytes()))) Ok((data.size(), Cow::Borrowed(data.as_bytes())))
})?; })?;
let content_mask = self.content_mask().scale(scale_factor); let content_mask = self.content_mask().scale(scale_factor);