diff --git a/src/internal_types.rs b/src/internal_types.rs index 622675346a..536a71ca86 100644 --- a/src/internal_types.rs +++ b/src/internal_types.rs @@ -44,13 +44,6 @@ pub enum TextureSampler { Mask, } -pub struct ImageResource { - pub bytes: Vec, - pub width: u32, - pub height: u32, - pub format: ImageFormat, -} - pub enum VertexAttribute { Position, Color, diff --git a/src/render_backend.rs b/src/render_backend.rs index 44afeacf16..8195f43d15 100644 --- a/src/render_backend.rs +++ b/src/render_backend.rs @@ -5,7 +5,7 @@ use clipper::{self, ClipBuffers}; use device::{ProgramId, TextureId}; use euclid::{Rect, Point2D, Size2D, Matrix4}; use fnv::FnvHasher; -use internal_types::{ApiMsg, Frame, ImageResource, ResultMsg, DrawLayer, Primitive, ClearInfo}; +use internal_types::{ApiMsg, Frame, ResultMsg, DrawLayer, Primitive, ClearInfo}; use internal_types::{BorderRadiusRasterOp, BoxShadowCornerRasterOp, DrawListID, RasterItem}; use internal_types::{BatchUpdateList, BatchId, BatchUpdate, BatchUpdateOp, CompiledNode}; use internal_types::{PackedVertex, WorkVertex, DisplayList, DrawCommand, DrawCommandInfo}; @@ -1239,22 +1239,18 @@ impl RenderBackend { .add_font_template(id, FontTemplate::Native(native_font_handle)); } ApiMsg::AddImage(id, width, height, format, bytes) => { - let image = ImageResource { - bytes: bytes, - width: width, - height: height, - format: format, - }; - self.resource_cache.add_image_template(id, image); + self.resource_cache.add_image_template(id, + width, + height, + format, + bytes); } ApiMsg::UpdateImage(id, width, height, format, bytes) => { - let image = ImageResource { - bytes: bytes, - width: width, - height: height, - format: format, - }; - self.resource_cache.update_image_template(id, image); + self.resource_cache.update_image_template(id, + width, + height, + format, + bytes); } ApiMsg::AddDisplayList(id, pipeline_id, @@ -1672,8 +1668,8 @@ impl DrawCommandBuilder { let image_info = resource_cache.get_glyph(&glyph_key); if image_info.width > 0 && image_info.height > 0 { - let x0 = glyph.x + image_info.x0 as f32 / device_pixel_ratio - blur_offset; - let y0 = glyph.y - image_info.y0 as f32 / device_pixel_ratio - blur_offset; + let x0 = glyph.x + image_info.user_x0 as f32 / device_pixel_ratio - blur_offset; + let y0 = glyph.y - image_info.user_y0 as f32 / device_pixel_ratio - blur_offset; let x1 = x0 + image_info.width as f32 / device_pixel_ratio; let y1 = y0 + image_info.height as f32 / device_pixel_ratio; diff --git a/src/resource_cache.rs b/src/resource_cache.rs index 9bbb59533f..af2b0558b1 100644 --- a/src/resource_cache.rs +++ b/src/resource_cache.rs @@ -2,7 +2,7 @@ use app_units::Au; use device::{TextureId}; use euclid::Size2D; use fnv::FnvHasher; -use internal_types::{FontTemplate, ImageResource, GlyphKey, RasterItem, TiledImageKey}; +use internal_types::{FontTemplate, GlyphKey, RasterItem, TiledImageKey}; use internal_types::{TextureTarget, TextureUpdateList}; use platform::font::{FontContext, RasterizedGlyph}; use renderer::BLUR_INFLATION_FACTOR; @@ -10,27 +10,41 @@ use resource_list::ResourceList; use scoped_threadpool; use std::cell::RefCell; use std::collections::HashMap; +use std::collections::hash_map::Entry::{Occupied, Vacant}; use std::collections::hash_state::DefaultState; use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT}; use std::sync::atomic::Ordering::SeqCst; use std::thread; use texture_cache::{TextureCache, TextureCacheItem, TextureCacheItemId, TextureInsertOp}; -use types::{FontKey, ImageKey, ImageFormat}; +use types::{Epoch, FontKey, ImageKey, ImageFormat}; static FONT_CONTEXT_COUNT: AtomicUsize = ATOMIC_USIZE_INIT; thread_local!(pub static FONT_CONTEXT: RefCell = RefCell::new(FontContext::new())); +struct ImageResource { + bytes: Vec, + width: u32, + height: u32, + format: ImageFormat, + epoch: Epoch, +} + struct GlyphRasterJob { image_id: TextureCacheItemId, glyph_key: GlyphKey, result: Option, } +struct CachedImageInfo { + texture_cache_id: TextureCacheItemId, + epoch: Epoch, +} + pub struct ResourceCache { cached_glyphs: HashMap>, cached_rasters: HashMap>, - cached_images: HashMap>, + cached_images: HashMap>, cached_tiled_images: HashMap>, font_templates: HashMap>, @@ -85,11 +99,47 @@ impl ResourceCache { self.font_templates.insert(font_key, template); } - pub fn add_image_template(&mut self, image_key: ImageKey, resource: ImageResource) { + pub fn add_image_template(&mut self, + image_key: ImageKey, + width: u32, + height: u32, + format: ImageFormat, + bytes: Vec) { + let resource = ImageResource { + width: width, + height: height, + format: format, + bytes: bytes, + epoch: Epoch(0), + }; + self.image_templates.insert(image_key, resource); } - pub fn update_image_template(&mut self, image_key: ImageKey, resource: ImageResource) { + pub fn update_image_template(&mut self, + image_key: ImageKey, + width: u32, + height: u32, + format: ImageFormat, + bytes: Vec) { + let next_epoch = match self.image_templates.get(&image_key) { + Some(image) => { + let Epoch(current_epoch) = image.epoch; + Epoch(current_epoch + 1) + } + None => { + Epoch(0) + } + }; + + let resource = ImageResource { + width: width, + height: height, + format: format, + bytes: bytes, + epoch: next_epoch, + }; + self.image_templates.insert(image_key, resource); } @@ -105,19 +155,46 @@ impl ResourceCache { // Update texture cache with any images that aren't yet uploaded to GPU. resource_list.for_each_image(|image_key| { - if !self.cached_images.contains_key(&image_key) { - let image_id = self.texture_cache.new_item_id(); - let image_template = &self.image_templates[&image_key]; - // TODO: Can we avoid the clone of the bytes here? - self.texture_cache.insert(image_id, - 0, - 0, - image_template.width, - image_template.height, - image_template.format, - TextureInsertOp::Blit(image_template.bytes.clone())); - self.cached_images.insert(image_key, image_id); - } + let cached_images = &mut self.cached_images; + let image_template = &self.image_templates[&image_key]; + + match cached_images.entry(image_key) { + Occupied(entry) => { + if entry.get().epoch != image_template.epoch { + let image_id = entry.get().texture_cache_id; + + // TODO: Can we avoid the clone of the bytes here? + self.texture_cache.update(image_id, + image_template.width, + image_template.height, + image_template.format, + image_template.bytes.clone()); + + // Update the cached epoch + *entry.into_mut() = CachedImageInfo { + texture_cache_id: image_id, + epoch: image_template.epoch, + }; + } + } + Vacant(entry) => { + let image_id = self.texture_cache.new_item_id(); + + // TODO: Can we avoid the clone of the bytes here? + self.texture_cache.insert(image_id, + 0, + 0, + image_template.width, + image_template.height, + image_template.format, + TextureInsertOp::Blit(image_template.bytes.clone())); + + entry.insert(CachedImageInfo { + texture_cache_id: image_id, + epoch: image_template.epoch, + }); + } + }; }); // Update texture cache with any new image tile operations. @@ -235,8 +312,8 @@ impl ResourceCache { #[inline] pub fn get_image(&self, image_key: ImageKey) -> &TextureCacheItem { - let image_id = self.cached_images[&image_key]; - self.texture_cache.get(image_id) + let image_info = &self.cached_images[&image_key]; + self.texture_cache.get(image_info.texture_cache_id) } #[inline] diff --git a/src/texture_cache.rs b/src/texture_cache.rs index 8f0e48d3d6..f35c171073 100644 --- a/src/texture_cache.rs +++ b/src/texture_cache.rs @@ -154,8 +154,10 @@ pub struct TextureCacheItem { pub v0: f32, pub u1: f32, pub v1: f32, - pub x0: i32, - pub y0: i32, + pub user_x0: i32, + pub user_y0: i32, + pub page_x0: u32, + pub page_y0: u32, pub width: u32, pub height: u32, pub texture_id: TextureId, // todo(gw): can this ever get invalidated? (page defragmentation?) @@ -193,7 +195,8 @@ impl TextureCacheItem { fn new(texture_id: TextureId, texture_index: TextureIndex, format: ImageFormat, - x0: i32, y0: i32, + user_x0: i32, user_y0: i32, + page_x0: u32, page_y0: u32, width: u32, height: u32, u0: f32, v0: f32, u1: f32, v1: f32) @@ -205,8 +208,10 @@ impl TextureCacheItem { v0: v0, u1: u1, v1: v1, - x0: x0, - y0: y0, + user_x0: user_x0, + user_y0: user_y0, + page_x0: page_x0, + page_y0: page_y0, width: width, height: height, format: format, @@ -294,8 +299,10 @@ impl TextureCache { v0: 0.0, u1: 0.0, v1: 0.0, - x0: 0, - y0: 0, + user_x0: 0, + user_y0: 0, + page_x0: 0, + page_y0: 0, width: 0, height: 0, texture_id: TextureId::invalid(), @@ -407,6 +414,7 @@ impl TextureCache { TextureIndex(0), format, x0, y0, + 0, 0, width, height, 0.0, 0.0, 1.0, 1.0); @@ -439,6 +447,7 @@ impl TextureCache { page.texture_index, format, x0, y0, + tx0, ty0, width, height, u0, v0, u1, v1); @@ -517,6 +526,34 @@ impl TextureCache { self.pending_updates.push(update_op); } + pub fn update(&mut self, + image_id: TextureCacheItemId, + width: u32, + height: u32, + format: ImageFormat, + bytes: Vec) { + let existing_item = self.items.get(image_id); + + // TODO(gw): Handle updates to size/format! + debug_assert!(existing_item.width == width); + debug_assert!(existing_item.height == height); + debug_assert!(existing_item.format == format); + + let op = TextureUpdateOp::Update(existing_item.page_x0, + existing_item.page_y0, + width, + height, + TextureUpdateDetails::Blit(bytes)); + + let update_op = TextureUpdate { + id: existing_item.texture_id, + index: existing_item.texture_index, + op: op, + }; + + self.pending_updates.push(update_op); + } + pub fn insert(&mut self, image_id: TextureCacheItemId, x0: i32,