Compare commits
1 commit
main
...
atlas-perf
Author | SHA1 | Date | |
---|---|---|---|
![]() |
7299e8d09d |
6 changed files with 92 additions and 104 deletions
33
crates/gpui/src/atlas.rs
Normal file
33
crates/gpui/src/atlas.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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::*;
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
|
@ -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) {
|
|
||||||
Ok(tile.clone())
|
|
||||||
} else {
|
|
||||||
let (size, bytes) = build()?;
|
|
||||||
let tile = lock.allocate(size, key.texture_kind());
|
let tile = lock.allocate(size, key.texture_kind());
|
||||||
let texture = lock.texture(tile.texture_id);
|
let texture = lock.texture(tile.texture_id);
|
||||||
texture.upload(tile.bounds, &bytes);
|
texture.upload(tile.bounds, &bytes);
|
||||||
lock.tiles_by_key.insert(key.clone(), tile.clone());
|
tile
|
||||||
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 {
|
||||||
|
|
|
@ -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()?;
|
|
||||||
let mut state = self.0.lock();
|
|
||||||
|
|
||||||
state.tiles.insert(
|
|
||||||
key.clone(),
|
|
||||||
crate::AtlasTile {
|
|
||||||
texture_id: AtlasTextureId {
|
texture_id: AtlasTextureId {
|
||||||
index: texture_id,
|
index: texture_id,
|
||||||
kind: crate::AtlasTextureKind::Path,
|
kind: crate::AtlasTextureKind::Path,
|
||||||
},
|
},
|
||||||
tile_id: TileId(tile_id),
|
tile_id: TileId(tile_id),
|
||||||
bounds: crate::Bounds {
|
bounds: Bounds {
|
||||||
origin: Point::default(),
|
origin: Point::default(),
|
||||||
size,
|
size,
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
);
|
|
||||||
|
|
||||||
Ok(state.tiles[key].clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn clear(&self) {
|
|
||||||
let mut state = self.0.lock();
|
|
||||||
state.tiles = HashMap::default();
|
|
||||||
state.next_id = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(¶ms.clone().into(), &mut || {
|
.get_or_insert_with(¶ms.clone().into(), || {
|
||||||
let (size, bytes) = self.text_system().rasterize_glyph(¶ms)?;
|
let (size, bytes) = self.app.text_system().rasterize_glyph(¶ms)?;
|
||||||
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(¶ms.clone().into(), &mut || {
|
.get_or_insert_with(¶ms.clone().into(), || {
|
||||||
let (size, bytes) = self.text_system().rasterize_glyph(¶ms)?;
|
let (size, bytes) = self.app.text_system().rasterize_glyph(¶ms)?;
|
||||||
Ok((size, Cow::Owned(bytes)))
|
Ok((size, Cow::Owned(bytes)))
|
||||||
})?;
|
})?;
|
||||||
let bounds = Bounds {
|
let bounds = Bounds {
|
||||||
|
@ -1354,11 +1356,11 @@ 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(¶ms.clone().into(), &mut || {
|
.get_or_insert_with(¶ms.clone().into(), || {
|
||||||
let bytes = self.svg_renderer.render(¶ms)?;
|
let bytes = self.app.svg_renderer.render(¶ms)?;
|
||||||
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);
|
||||||
|
@ -1396,7 +1398,7 @@ impl<'a> WindowContext<'a> {
|
||||||
let tile = self
|
let tile = self
|
||||||
.window
|
.window
|
||||||
.sprite_atlas
|
.sprite_atlas
|
||||||
.get_or_insert_with(¶ms.clone().into(), &mut || {
|
.get_or_insert_with(¶ms.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);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue