WIP: atlas perf improv

This commit is contained in:
Thorsten Ball 2024-01-17 16:50:37 +01:00
parent 51127460b2
commit 7299e8d09d
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 assets;
mod atlas;
mod color;
mod element;
mod elements;
@ -51,6 +52,7 @@ pub use anyhow::Result;
pub use app::*;
pub(crate) use arena::*;
pub use assets::*;
pub(crate) use atlas::*;
pub use color::*;
pub use ctor::ctor;
pub use element::*;

View file

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

View file

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

View file

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

View file

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