first pass of merging in warp (doesn't build)

This commit is contained in:
Ryan Ward
2026-07-01 16:08:58 -05:00
parent 2f64909469
commit 4770ac06b5
3662 changed files with 414574 additions and 89772 deletions
+70 -45
View File
@@ -1,40 +1,42 @@
use anyhow::{anyhow, Result};
use core::fmt;
use itertools::Itertools;
use std::{
collections::HashMap,
error,
hash::{DefaultHasher, Hash, Hasher},
rc::Rc,
sync::{Arc, LazyLock},
};
use strum_macros::EnumIter;
use std::collections::HashMap;
use std::error;
use std::hash::{DefaultHasher, Hash, Hasher};
use std::rc::Rc;
use std::sync::{Arc, LazyLock};
use crate::{
assets::asset_cache::{Asset, AssetCache, AssetSource, AssetState},
util::parse_u32,
Entity, SingletonEntity,
};
use image::{
codecs::{gif::GifDecoder, webp::WebPDecoder},
imageops::FilterType,
AnimationDecoder, DynamicImage, Frame, ImageBuffer, ImageFormat,
};
use anyhow::{anyhow, Result};
use image::codecs::gif::GifDecoder;
use image::codecs::webp::WebPDecoder;
use image::imageops::FilterType;
use image::{AnimationDecoder, DynamicImage, Frame, ImageBuffer, ImageFormat};
use itertools::Itertools;
use parking_lot::{RwLock, RwLockUpgradableReadGuard};
use pathfinder_geometry::vector::Vector2I;
use resvg::{
tiny_skia::{self, IntSize},
usvg,
};
use resvg::tiny_skia::{self, IntSize};
use resvg::usvg;
use strum_macros::EnumIter;
use crate::assets::asset_cache::{Asset, AssetCache, AssetSource, AssetState};
use crate::util::parse_u32;
use crate::{Entity, SingletonEntity};
const MIN_REFRESH_DELAY_MS: u32 = 50;
static SVG_FONT_DB: LazyLock<Arc<usvg::fontdb::Database>> = LazyLock::new(|| {
let mut fontdb = usvg::fontdb::Database::new();
fontdb.load_system_fonts();
load_svg_fallback_fonts(&mut fontdb);
Arc::new(fontdb)
});
fn load_svg_fallback_fonts(fontdb: &mut usvg::fontdb::Database) {
fontdb.load_font_data(
include_bytes!("../../../app/assets/bundled/fonts/roboto/Roboto-Regular.ttf").to_vec(),
);
fontdb.set_sans_serif_family("Roboto");
}
pub fn prewarm_svg_font_db() {
LazyLock::force(&SVG_FONT_DB);
}
@@ -791,14 +793,16 @@ impl FitTo {
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
struct RenderedImageCacheKey {
bounds: Vector2I,
fit_type: FitType,
animated_image_behavior: AnimatedImageBehavior,
}
type RenderedImageCache = HashMap<u64, HashMap<RenderedImageCacheKey, Rc<Image>>>;
#[derive(Default)]
#[derive(Clone, Default)]
pub struct ImageCache {
/// Map of images of any ImageType already scaled to a certain size.
/// Map of rendered images of any ImageType already materialized for a certain size and fit.
/// Uses the hashed AssetSource and rendered-image properties as a key.
images: RwLock<HashMap<u64, HashMap<RenderedImageCacheKey, Rc<Image>>>>,
images: Rc<RwLock<RenderedImageCache>>,
}
impl ImageCache {
@@ -816,16 +820,16 @@ impl ImageCache {
cache.remove(&cache_key);
}
/// Removes a single cached size entry for an asset.
/// Removes a single cached rendered entry for an asset.
///
/// When the removed `Rc<Image>` is the last strong holder of the inner
/// `Arc<StaticImage>`, that `Arc`'s strong count drops to zero. On the
/// next call to `TextureCache::end_frame()`, the corresponding GPU texture
/// will be evicted automatically via the `Weak<StaticImage>` it holds.
///
/// `bounds` must match the resolved bounds used as the cache key inside
/// `image()` (i.e., after any `max_dimension` adjustment), not the
/// originally requested bounds.
/// `bounds` and `fit_type` must match the resolved values used as the
/// cache key inside `image()` (i.e., after any `max_dimension`
/// adjustment), not only the originally requested bounds.
// Called by the debounce eviction pass added in the main changeset.
/// TODO(APP-3877): remove `#[allow(dead_code)]` once the debounce eviction pass wires this up.
#[allow(dead_code)]
@@ -833,6 +837,7 @@ impl ImageCache {
&self,
asset_source: &AssetSource,
bounds: Vector2I,
fit_type: FitType,
animated_image_behavior: AnimatedImageBehavior,
) {
let mut s = DefaultHasher::new();
@@ -841,6 +846,7 @@ impl ImageCache {
let rendered_key = RenderedImageCacheKey {
bounds,
fit_type,
animated_image_behavior,
};
@@ -868,16 +874,15 @@ impl ImageCache {
asset_source.hash(&mut s);
let cache_key = s.finish();
match asset_cache.load_asset::<ImageType>(asset_source) {
match asset_cache.load_asset::<ImageType>(asset_source.clone()) {
AssetState::Loading { handle } => AssetState::Loading { handle },
AssetState::Evicted => AssetState::Evicted,
AssetState::FailedToLoad(err) => AssetState::FailedToLoad(err),
AssetState::Loaded { data } => {
let (mut needs_resize, mut bounds) = match cache_option {
CacheOption::BySize => {
// Only store a resized copy of the source asset if a
// specific size was requested and it doesn't match the
// source asset's size.
// Only resize the source asset if a specific size was requested and it
// does not match the source asset's size.
let needs_resize = data.image_size() != Some(bounds);
(needs_resize, bounds)
}
@@ -917,19 +922,28 @@ impl ImageCache {
let rendered_image_cache_key = RenderedImageCacheKey {
bounds,
fit_type,
animated_image_behavior,
};
// SVG sources always have to be rasterized, even when rendered at their
// intrinsic size. Cache those materialized pixels alongside resized images.
let should_cache_rendered_image =
needs_resize || matches!(data.as_ref(), ImageType::Svg { .. });
// If it's already in the image cache at the target size,
// return it.
let cache = self.images.upgradable_read();
if let Some(inner_map) = cache.get(&cache_key) {
if let Some(image) = inner_map.get(&rendered_image_cache_key) {
return AssetState::Loaded {
data: image.clone(),
};
// If it is already in the image cache at the target size and fit, return it.
let cache = if should_cache_rendered_image {
let cache = self.images.upgradable_read();
if let Some(inner_map) = cache.get(&cache_key) {
if let Some(image) = inner_map.get(&rendered_image_cache_key) {
return AssetState::Loaded {
data: image.clone(),
};
}
}
}
Some(cache)
} else {
None
};
// Otherwise, create the correctly-sized image struct and
// insert it into the cache (if necessary).
@@ -938,7 +952,7 @@ impl ImageCache {
Ok(image) => Rc::new(image),
Err(err) => return AssetState::FailedToLoad(Rc::new(err)),
};
if needs_resize {
if let Some(cache) = cache {
let mut images_cache = RwLockUpgradableReadGuard::upgrade(cache);
images_cache
.entry(cache_key)
@@ -950,6 +964,17 @@ impl ImageCache {
}
}
}
pub fn image_size(
&self,
asset_source: AssetSource,
asset_cache: &AssetCache,
) -> Option<Vector2I> {
let AssetState::Loaded { data } = asset_cache.load_asset::<ImageType>(asset_source) else {
return None;
};
data.image_size()
}
}
impl Entity for ImageCache {