Add some basic image loading caching for images loaded from the file system

Place them in a HashMap that's indexed by the path. This needs further
tuning, for example to also be applied to embedded images, and the cache
shouldn't hold a strong reference. But it makes the berlin page
playable.
This commit is contained in:
Simon Hausmann 2020-11-20 17:41:29 +01:00
parent 3d85e45ec3
commit fdeda0d0ed

View file

@ -24,7 +24,7 @@ use sixtyfps_corelib::{
SharedArray, SharedArray,
}; };
use smallvec::{smallvec, SmallVec}; use smallvec::{smallvec, SmallVec};
use std::cell::RefCell; use std::{cell::RefCell, collections::HashMap};
extern crate alloc; extern crate alloc;
use alloc::rc::Rc; use alloc::rc::Rc;
@ -76,7 +76,7 @@ enum GLRenderingPrimitive {
Texture { Texture {
vertices: GLArrayBuffer<Vertex>, vertices: GLArrayBuffer<Vertex>,
texture_vertices: GLArrayBuffer<Vertex>, texture_vertices: GLArrayBuffer<Vertex>,
texture: texture::AtlasAllocation, texture: Rc<texture::AtlasAllocation>,
}, },
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
DynamicPrimitive { DynamicPrimitive {
@ -102,6 +102,8 @@ struct NormalRectangle {
indices: GLIndexBuffer<u16>, indices: GLIndexBuffer<u16>,
} }
type TextureCacheKey = String;
pub struct GLRenderer { pub struct GLRenderer {
context: Rc<glow::Context>, context: Rc<glow::Context>,
path_shader: PathShader, path_shader: PathShader,
@ -119,6 +121,7 @@ pub struct GLRenderer {
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
windowed_context: Option<glutin::WindowedContext<glutin::NotCurrent>>, windowed_context: Option<glutin::WindowedContext<glutin::NotCurrent>>,
normal_rectangle: Option<NormalRectangle>, normal_rectangle: Option<NormalRectangle>,
texture_cache: Rc<RefCell<HashMap<TextureCacheKey, Rc<texture::AtlasAllocation>>>>,
} }
pub struct GLRenderingPrimitivesBuilder { pub struct GLRenderingPrimitivesBuilder {
@ -136,6 +139,8 @@ pub struct GLRenderingPrimitivesBuilder {
Rc<winit::event_loop::EventLoopProxy<sixtyfps_corelib::eventloop::CustomEvent>>, Rc<winit::event_loop::EventLoopProxy<sixtyfps_corelib::eventloop::CustomEvent>>,
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
windowed_context: glutin::WindowedContext<glutin::PossiblyCurrent>, windowed_context: glutin::WindowedContext<glutin::PossiblyCurrent>,
texture_cache: Rc<RefCell<HashMap<TextureCacheKey, Rc<texture::AtlasAllocation>>>>,
} }
pub struct GLFrame { pub struct GLFrame {
@ -272,6 +277,7 @@ impl GLRenderer {
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
windowed_context: Some(unsafe { windowed_context.make_not_current().unwrap() }), windowed_context: Some(unsafe { windowed_context.make_not_current().unwrap() }),
normal_rectangle: None, normal_rectangle: None,
texture_cache: Default::default(),
} }
} }
} }
@ -322,6 +328,8 @@ impl GraphicsBackend for GLRenderer {
event_loop_proxy: self.event_loop_proxy.clone(), event_loop_proxy: self.event_loop_proxy.clone(),
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
windowed_context: current_windowed_context, windowed_context: current_windowed_context,
texture_cache: self.texture_cache.clone(),
} }
} }
@ -420,17 +428,15 @@ impl RenderingPrimitivesBuilder for GLRenderingPrimitivesBuilder {
let mut image_path = std::env::current_exe().unwrap(); let mut image_path = std::env::current_exe().unwrap();
image_path.pop(); // pop of executable name image_path.pop(); // pop of executable name
image_path.push(&*path.clone()); image_path.push(&*path.clone());
let image = image::open(image_path.as_path()).unwrap().into_rgba();
let image = image::ImageBuffer::<image::Rgba<u8>, &[u8]>::from_raw( let atlas_allocation = self
image.width(), .cached_texture(image_path.to_string_lossy().to_string(), || {
image.height(), image::open(image_path.as_path()).unwrap().into_rgba()
&image, });
)
.unwrap(); smallvec![GLRenderingPrimitivesBuilder::create_texture(
smallvec![GLRenderingPrimitivesBuilder::create_image(
&self.context, &self.context,
&mut *self.texture_atlas.borrow_mut(), atlas_allocation,
image,
source_clip_rect source_clip_rect
)] )]
} }
@ -634,16 +640,29 @@ impl GLRenderingPrimitivesBuilder {
image: impl texture::UploadableAtlasImage, image: impl texture::UploadableAtlasImage,
source_rect: &IntRect, source_rect: &IntRect,
) -> GLRenderingPrimitive { ) -> GLRenderingPrimitive {
let rect = let atlas_allocation = atlas.allocate_image_in_atlas(&context, image);
Rect::new(Point::new(0.0, 0.0), Size::new(image.width() as f32, image.height() as f32));
Self::create_texture(context, Rc::new(atlas_allocation), source_rect)
}
fn create_texture(
context: &Rc<glow::Context>,
atlas_allocation: Rc<texture::AtlasAllocation>,
source_rect: &IntRect,
) -> GLRenderingPrimitive {
let rect = Rect::new(
Point::new(0.0, 0.0),
Size::new(
atlas_allocation.texture_coordinates.width() as f32,
atlas_allocation.texture_coordinates.height() as f32,
),
);
let vertex1 = Vertex { _pos: [rect.min_x(), rect.min_y()] }; let vertex1 = Vertex { _pos: [rect.min_x(), rect.min_y()] };
let vertex2 = Vertex { _pos: [rect.max_x(), rect.min_y()] }; let vertex2 = Vertex { _pos: [rect.max_x(), rect.min_y()] };
let vertex3 = Vertex { _pos: [rect.max_x(), rect.max_y()] }; let vertex3 = Vertex { _pos: [rect.max_x(), rect.max_y()] };
let vertex4 = Vertex { _pos: [rect.min_x(), rect.max_y()] }; let vertex4 = Vertex { _pos: [rect.min_x(), rect.max_y()] };
let atlas_allocation = atlas.allocate_image_in_atlas(&context, image);
let vertices = GLArrayBuffer::new( let vertices = GLArrayBuffer::new(
&context, &context,
&vec![vertex1, vertex2, vertex3, vertex1, vertex3, vertex4], &vec![vertex1, vertex2, vertex3, vertex1, vertex3, vertex4],
@ -656,6 +675,24 @@ impl GLRenderingPrimitivesBuilder {
GLRenderingPrimitive::Texture { vertices, texture_vertices, texture: atlas_allocation } GLRenderingPrimitive::Texture { vertices, texture_vertices, texture: atlas_allocation }
} }
fn cached_texture<Img: texture::UploadableAtlasImage>(
&self,
key: TextureCacheKey,
create_fn: impl Fn() -> Img,
) -> Rc<texture::AtlasAllocation> {
self.texture_cache
.borrow_mut()
.entry(key)
.or_insert_with(|| {
Rc::new(
self.texture_atlas
.borrow_mut()
.allocate_image_in_atlas(&self.context, create_fn()),
)
})
.clone()
}
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
fn create_glyph_runs( fn create_glyph_runs(
&mut self, &mut self,