mirror of
https://github.com/zed-industries/zed.git
synced 2024-11-08 07:35:01 +03:00
Load embedded fonts directly from .rdata instead of cloning (#6932)
The fonts we embed in Zed binary (Zed Sans & Zed Mono) weigh about 30Mb in total and we are cloning them several times during startup and loading of embedded assets (once explicitly in Zed and then under the hood in font-kit). Moreover, after loading we have at least 2 copies of each font in our program; one in .rdata and the other on the heap for use by font-kit. This commit does away with that distinction (we're no longer allocating the font data) and slightly relaxes the interface of `TextSystem::add_fonts` by expecting one to pass `Cow<[u8]>` instead of `Arc<Vec<u8>>`. Additionally, `AssetSource::get` now returns `Cow<'static, [u8]>` instead of `Cow<'self, [u8]>`; all existing implementations conform with that change. Note that this optimization takes effect only in Release builds, as the library we use for asset embedding - rust-embed - embeds the assets only in Release mode and in Dev builds it simply loads data from disk. Thus it returns `Cow<[u8]>` in it's interface. Therefore, we still copy that memory around in Dev builds, but that's not really an issue. This patch makes no assumptions about the build profile we're running under, that's just an intrinsic property of rust-embed. Tl;dr: this should shave off about 30Mb of memory usage and a fair chunk (~30ms) of startup time. Release Notes: - Improved startup time and memory usage.
This commit is contained in:
parent
e22ffb6740
commit
8fbc88b708
@ -16,7 +16,7 @@ use rust_embed::RustEmbed;
|
|||||||
pub struct Assets;
|
pub struct Assets;
|
||||||
|
|
||||||
impl AssetSource for Assets {
|
impl AssetSource for Assets {
|
||||||
fn load(&self, path: &str) -> Result<std::borrow::Cow<[u8]>> {
|
fn load(&self, path: &str) -> Result<std::borrow::Cow<'static, [u8]>> {
|
||||||
Self::get(path)
|
Self::get(path)
|
||||||
.map(|f| f.data)
|
.map(|f| f.data)
|
||||||
.ok_or_else(|| anyhow!("could not find asset at path \"{}\"", path))
|
.ok_or_else(|| anyhow!("could not find asset at path \"{}\"", path))
|
||||||
|
@ -11,14 +11,14 @@ use std::{
|
|||||||
/// A source of assets for this app to use.
|
/// A source of assets for this app to use.
|
||||||
pub trait AssetSource: 'static + Send + Sync {
|
pub trait AssetSource: 'static + Send + Sync {
|
||||||
/// Load the given asset from the source path.
|
/// Load the given asset from the source path.
|
||||||
fn load(&self, path: &str) -> Result<Cow<[u8]>>;
|
fn load(&self, path: &str) -> Result<Cow<'static, [u8]>>;
|
||||||
|
|
||||||
/// List the assets at the given path.
|
/// List the assets at the given path.
|
||||||
fn list(&self, path: &str) -> Result<Vec<SharedString>>;
|
fn list(&self, path: &str) -> Result<Vec<SharedString>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AssetSource for () {
|
impl AssetSource for () {
|
||||||
fn load(&self, path: &str) -> Result<Cow<[u8]>> {
|
fn load(&self, path: &str) -> Result<Cow<'static, [u8]>> {
|
||||||
Err(anyhow!(
|
Err(anyhow!(
|
||||||
"get called on empty asset provider with \"{}\"",
|
"get called on empty asset provider with \"{}\"",
|
||||||
path
|
path
|
||||||
|
@ -204,7 +204,7 @@ pub trait PlatformDispatcher: Send + Sync {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) trait PlatformTextSystem: Send + Sync {
|
pub(crate) trait PlatformTextSystem: Send + Sync {
|
||||||
fn add_fonts(&self, fonts: &[Arc<Vec<u8>>]) -> Result<()>;
|
fn add_fonts(&self, fonts: Vec<Cow<'static, [u8]>>) -> Result<()>;
|
||||||
fn all_font_names(&self) -> Vec<String>;
|
fn all_font_names(&self) -> Vec<String>;
|
||||||
fn all_font_families(&self) -> Vec<String>;
|
fn all_font_families(&self) -> Vec<String>;
|
||||||
fn font_id(&self, descriptor: &Font) -> Result<FontId>;
|
fn font_id(&self, descriptor: &Font) -> Result<FontId>;
|
||||||
|
@ -34,7 +34,7 @@ use pathfinder_geometry::{
|
|||||||
vector::{Vector2F, Vector2I},
|
vector::{Vector2F, Vector2I},
|
||||||
};
|
};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::{char, cmp, convert::TryFrom, ffi::c_void, sync::Arc};
|
use std::{borrow::Cow, char, cmp, convert::TryFrom, ffi::c_void, sync::Arc};
|
||||||
|
|
||||||
use super::open_type;
|
use super::open_type;
|
||||||
|
|
||||||
@ -74,7 +74,7 @@ impl Default for MacTextSystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PlatformTextSystem for MacTextSystem {
|
impl PlatformTextSystem for MacTextSystem {
|
||||||
fn add_fonts(&self, fonts: &[Arc<Vec<u8>>]) -> Result<()> {
|
fn add_fonts(&self, fonts: Vec<Cow<'static, [u8]>>) -> Result<()> {
|
||||||
self.0.write().add_fonts(fonts)
|
self.0.write().add_fonts(fonts)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,12 +183,23 @@ impl PlatformTextSystem for MacTextSystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl MacTextSystemState {
|
impl MacTextSystemState {
|
||||||
fn add_fonts(&mut self, fonts: &[Arc<Vec<u8>>]) -> Result<()> {
|
fn add_fonts(&mut self, fonts: Vec<Cow<'static, [u8]>>) -> Result<()> {
|
||||||
self.memory_source.add_fonts(
|
let fonts = fonts
|
||||||
fonts
|
.into_iter()
|
||||||
.iter()
|
.map(|bytes| match bytes {
|
||||||
.map(|bytes| Handle::from_memory(bytes.clone(), 0)),
|
Cow::Borrowed(embedded_font) => {
|
||||||
)?;
|
let data_provider = unsafe {
|
||||||
|
core_graphics::data_provider::CGDataProvider::from_slice(embedded_font)
|
||||||
|
};
|
||||||
|
let font = core_graphics::font::CGFont::from_data_provider(data_provider)
|
||||||
|
.map_err(|_| anyhow!("Could not load an embedded font."))?;
|
||||||
|
let font = font_kit::loaders::core_text::Font::from_core_graphics_font(font);
|
||||||
|
Ok(Handle::from_native(&font))
|
||||||
|
}
|
||||||
|
Cow::Owned(bytes) => Ok(Handle::from_memory(Arc::new(bytes), 0)),
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>>>()?;
|
||||||
|
self.memory_source.add_fonts(fonts.into_iter())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ use itertools::Itertools;
|
|||||||
use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
|
use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
|
||||||
use smallvec::{smallvec, SmallVec};
|
use smallvec::{smallvec, SmallVec};
|
||||||
use std::{
|
use std::{
|
||||||
|
borrow::Cow,
|
||||||
cmp,
|
cmp,
|
||||||
fmt::{Debug, Display, Formatter},
|
fmt::{Debug, Display, Formatter},
|
||||||
hash::{Hash, Hasher},
|
hash::{Hash, Hasher},
|
||||||
@ -85,7 +86,7 @@ impl TextSystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Add a font's data to the text system.
|
/// Add a font's data to the text system.
|
||||||
pub fn add_fonts(&self, fonts: &[Arc<Vec<u8>>]) -> Result<()> {
|
pub fn add_fonts(&self, fonts: Vec<Cow<'static, [u8]>>) -> Result<()> {
|
||||||
self.platform_text_system.add_fonts(fonts)
|
self.platform_text_system.add_fonts(fonts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ use rust_embed::RustEmbed;
|
|||||||
pub struct Assets;
|
pub struct Assets;
|
||||||
|
|
||||||
impl AssetSource for Assets {
|
impl AssetSource for Assets {
|
||||||
fn load(&self, path: &str) -> Result<Cow<[u8]>> {
|
fn load(&self, path: &str) -> Result<Cow<'static, [u8]>> {
|
||||||
Self::get(path)
|
Self::get(path)
|
||||||
.map(|f| f.data)
|
.map(|f| f.data)
|
||||||
.ok_or_else(|| anyhow!("could not find asset at path \"{}\"", path))
|
.ok_or_else(|| anyhow!("could not find asset at path \"{}\"", path))
|
||||||
|
@ -2,8 +2,6 @@ mod assets;
|
|||||||
mod stories;
|
mod stories;
|
||||||
mod story_selector;
|
mod story_selector;
|
||||||
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use dialoguer::FuzzySelect;
|
use dialoguer::FuzzySelect;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
@ -128,10 +126,10 @@ fn load_embedded_fonts(cx: &AppContext) -> gpui::Result<()> {
|
|||||||
let mut embedded_fonts = Vec::new();
|
let mut embedded_fonts = Vec::new();
|
||||||
for font_path in font_paths {
|
for font_path in font_paths {
|
||||||
if font_path.ends_with(".ttf") {
|
if font_path.ends_with(".ttf") {
|
||||||
let font_bytes = cx.asset_source().load(&font_path)?.to_vec();
|
let font_bytes = cx.asset_source().load(&font_path)?;
|
||||||
embedded_fonts.push(Arc::from(font_bytes));
|
embedded_fonts.push(font_bytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cx.text_system().add_fonts(&embedded_fonts)
|
cx.text_system().add_fonts(embedded_fonts)
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ use rust_embed::RustEmbed;
|
|||||||
pub struct Assets;
|
pub struct Assets;
|
||||||
|
|
||||||
impl AssetSource for Assets {
|
impl AssetSource for Assets {
|
||||||
fn load(&self, path: &str) -> Result<Cow<[u8]>> {
|
fn load(&self, path: &str) -> Result<Cow<'static, [u8]>> {
|
||||||
Self::get(path)
|
Self::get(path)
|
||||||
.map(|f| f.data)
|
.map(|f| f.data)
|
||||||
.ok_or_else(|| anyhow!("could not find asset at path \"{}\"", path))
|
.ok_or_else(|| anyhow!("could not find asset at path \"{}\"", path))
|
||||||
|
@ -815,14 +815,14 @@ fn load_embedded_fonts(cx: &AppContext) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
scope.spawn(async {
|
scope.spawn(async {
|
||||||
let font_bytes = asset_source.load(font_path).unwrap().to_vec();
|
let font_bytes = asset_source.load(font_path).unwrap();
|
||||||
embedded_fonts.lock().push(Arc::from(font_bytes));
|
embedded_fonts.lock().push(font_bytes);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
cx.text_system()
|
cx.text_system()
|
||||||
.add_fonts(&embedded_fonts.into_inner())
|
.add_fonts(embedded_fonts.into_inner())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2676,17 +2676,9 @@ mod tests {
|
|||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_bundled_settings_and_themes(cx: &mut AppContext) {
|
fn test_bundled_settings_and_themes(cx: &mut AppContext) {
|
||||||
cx.text_system()
|
cx.text_system()
|
||||||
.add_fonts(&[
|
.add_fonts(vec![
|
||||||
Assets
|
Assets.load("fonts/zed-sans/zed-sans-extended.ttf").unwrap(),
|
||||||
.load("fonts/zed-sans/zed-sans-extended.ttf")
|
Assets.load("fonts/zed-mono/zed-mono-extended.ttf").unwrap(),
|
||||||
.unwrap()
|
|
||||||
.to_vec()
|
|
||||||
.into(),
|
|
||||||
Assets
|
|
||||||
.load("fonts/zed-mono/zed-mono-extended.ttf")
|
|
||||||
.unwrap()
|
|
||||||
.to_vec()
|
|
||||||
.into(),
|
|
||||||
])
|
])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let themes = ThemeRegistry::default();
|
let themes = ThemeRegistry::default();
|
||||||
|
Loading…
Reference in New Issue
Block a user