diff --git a/examples/blob.rs b/examples/blob.rs index d8e387607c..384df8ac06 100644 --- a/examples/blob.rs +++ b/examples/blob.rs @@ -17,7 +17,8 @@ use rayon::prelude::*; use std::collections::HashMap; use std::sync::Arc; use webrender::api::{self, DisplayListBuilder, DocumentId, PipelineId, RenderApi, Transaction}; -use webrender::api::{ColorF, DeviceIntRect, DeviceIntPoint}; +use webrender::api::ColorF; +use webrender::euclid::size2; // This example shows how to implement a very basic BlobImageHandler that can only render // a checkerboard pattern. @@ -28,8 +29,8 @@ type ImageRenderingCommands = api::ColorU; // Serialize/deserialize the blob. // For real usecases you should probably use serde rather than doing it by hand. -fn serialize_blob(color: api::ColorU) -> Vec { - vec![color.r, color.g, color.b, color.a] +fn serialize_blob(color: api::ColorU) -> Arc> { + Arc::new(vec![color.r, color.g, color.b, color.a]) } fn deserialize_blob(blob: &[u8]) -> Result { @@ -50,9 +51,13 @@ fn render_blob( ) -> api::BlobImageResult { let color = *commands; + // Note: This implementation ignores the dirty rect which isn't incorrect + // but is a missed optimization. + // Allocate storage for the result. Right now the resource cache expects the // tiles to have have no stride or offset. - let mut texels = Vec::with_capacity((descriptor.size.width * descriptor.size.height * 4) as usize); + let bpp = 4; + let mut texels = Vec::with_capacity((descriptor.rect.size.area() * bpp) as usize); // Generate a per-tile pattern to see it in the demo. For a real use case it would not // make sense for the rendered content to depend on its tile. @@ -61,12 +66,15 @@ fn render_blob( None => true, }; - for y in 0 .. descriptor.size.height { - for x in 0 .. descriptor.size.width { + let [w, h] = descriptor.rect.size.to_array(); + let offset = descriptor.rect.origin; + + for y in 0..h { + for x in 0..w { // Apply the tile's offset. This is important: all drawing commands should be // translated by this offset to give correct results with tiled blob images. - let x2 = x + descriptor.offset.x as i32; - let y2 = y + descriptor.offset.y as i32; + let x2 = x + offset.x; + let y2 = y + offset.y; // Render a simple checkerboard pattern let checker = if (x2 % 20 >= 10) != (y2 % 20 >= 10) { @@ -98,10 +106,7 @@ fn render_blob( Ok(api::RasterizedBlobImage { data: Arc::new(texels), - rasterized_rect: DeviceIntRect { - origin: DeviceIntPoint::origin(), - size: descriptor.size, - }, + rasterized_rect: size2(w, h).into(), }) } @@ -117,7 +122,7 @@ struct CheckerboardRenderer { // case the command list is a simple 32 bits value and would be cheap to clone before sending // to the workers. But in a more realistic scenario the commands would typically be bigger // and more expensive to clone, so let's pretend it is also the case here. - image_cmds: HashMap>, + image_cmds: HashMap>, } impl CheckerboardRenderer { @@ -130,19 +135,19 @@ impl CheckerboardRenderer { } impl api::BlobImageHandler for CheckerboardRenderer { - fn add(&mut self, key: api::ImageKey, cmds: Arc, _: Option) { + fn add(&mut self, key: api::BlobImageKey, cmds: Arc, _: Option) { self.image_cmds .insert(key, Arc::new(deserialize_blob(&cmds[..]).unwrap())); } - fn update(&mut self, key: api::ImageKey, cmds: Arc, _dirty_rect: Option) { + fn update(&mut self, key: api::BlobImageKey, cmds: Arc, _dirty_rect: &api::BlobDirtyRect) { // Here, updating is just replacing the current version of the commands with // the new one (no incremental updates). self.image_cmds .insert(key, Arc::new(deserialize_blob(&cmds[..]).unwrap())); } - fn delete(&mut self, key: api::ImageKey) { + fn delete(&mut self, key: api::BlobImageKey) { self.image_cmds.remove(&key); } @@ -165,7 +170,7 @@ impl api::BlobImageHandler for CheckerboardRenderer { struct Rasterizer { workers: Arc, - image_cmds: HashMap>, + image_cmds: HashMap>, } impl api::AsyncBlobImageRasterizer for Rasterizer { @@ -198,19 +203,19 @@ impl Example for App { _pipeline_id: PipelineId, _document_id: DocumentId, ) { - let blob_img1 = api.generate_image_key(); - txn.add_image( + let blob_img1 = api.generate_blob_image_key(); + txn.add_blob_image( blob_img1, api::ImageDescriptor::new(500, 500, api::ImageFormat::BGRA8, true, false), - api::ImageData::new_blob_image(serialize_blob(api::ColorU::new(50, 50, 150, 255))), + serialize_blob(api::ColorU::new(50, 50, 150, 255)), Some(128), ); - let blob_img2 = api.generate_image_key(); - txn.add_image( + let blob_img2 = api.generate_blob_image_key(); + txn.add_blob_image( blob_img2, api::ImageDescriptor::new(200, 200, api::ImageFormat::BGRA8, true, false), - api::ImageData::new_blob_image(serialize_blob(api::ColorU::new(50, 150, 50, 255))), + serialize_blob(api::ColorU::new(50, 150, 50, 255)), None, ); @@ -232,7 +237,7 @@ impl Example for App { api::LayoutSize::new(0.0, 0.0), api::ImageRendering::Auto, api::AlphaType::PremultipliedAlpha, - blob_img1, + blob_img1.as_image(), ColorF::WHITE, ); @@ -243,7 +248,7 @@ impl Example for App { api::LayoutSize::new(0.0, 0.0), api::ImageRendering::Auto, api::AlphaType::PremultipliedAlpha, - blob_img2, + blob_img2.as_image(), ColorF::WHITE, ); diff --git a/examples/image_resize.rs b/examples/image_resize.rs index d683ac1097..7a667b7eac 100644 --- a/examples/image_resize.rs +++ b/examples/image_resize.rs @@ -105,7 +105,7 @@ impl Example for App { self.image_key, ImageDescriptor::new(64, 64, ImageFormat::BGRA8, true, false), ImageData::new(image_data), - None, + &DirtyRect::All, ); let mut txn = Transaction::new(); txn.generate_frame(); diff --git a/examples/texture_cache_stress.rs b/examples/texture_cache_stress.rs index 7a2012f65b..2eee1b3a8f 100644 --- a/examples/texture_cache_stress.rs +++ b/examples/texture_cache_stress.rs @@ -243,7 +243,7 @@ impl Example for App { image_key, ImageDescriptor::new(size, size, ImageFormat::BGRA8, true, false), ImageData::new(self.image_generator.take()), - None, + &DirtyRect::All, ); }, winit::VirtualKeyCode::E => { diff --git a/webrender/src/glyph_rasterizer/no_pathfinder.rs b/webrender/src/glyph_rasterizer/no_pathfinder.rs index e94496564a..5a5335140b 100644 --- a/webrender/src/glyph_rasterizer/no_pathfinder.rs +++ b/webrender/src/glyph_rasterizer/no_pathfinder.rs @@ -5,7 +5,7 @@ //! Module only available when pathfinder is deactivated when webrender is //! compiled regularly (i.e. any configuration without feature = "pathfinder") -use api::{ImageData, ImageDescriptor, ImageFormat}; +use api::{ImageDescriptor, ImageFormat, DirtyRect}; use device::TextureFilter; use euclid::size2; use gpu_types::UvRectKind; @@ -15,6 +15,7 @@ use platform::font::FontContext; use glyph_rasterizer::{FontInstance, FontContexts, GlyphKey}; use glyph_rasterizer::{GlyphRasterizer, GlyphRasterJob, GlyphRasterJobs, GlyphRasterResult}; use glyph_cache::{GlyphCache, CachedGlyphInfo, GlyphCacheEntry}; +use resource_cache::CachedImageData; use texture_cache::{TextureCache, TextureCacheHandle, Eviction}; use gpu_cache::GpuCache; use render_task::{RenderTaskTree, RenderTaskCache}; @@ -182,9 +183,9 @@ impl GlyphRasterizer { offset: 0, }, TextureFilter::Linear, - Some(ImageData::Raw(Arc::new(glyph.bytes))), + Some(CachedImageData::Raw(Arc::new(glyph.bytes))), [glyph.left, -glyph.top, glyph.scale], - None, + DirtyRect::All, gpu_cache, Some(glyph_key_cache.eviction_notice()), UvRectKind::Rect, diff --git a/webrender/src/lib.rs b/webrender/src/lib.rs index 8ebd63f5e3..fc6582219c 100644 --- a/webrender/src/lib.rs +++ b/webrender/src/lib.rs @@ -205,4 +205,3 @@ pub use renderer::MAX_VERTEX_TEXTURE_WIDTH; pub use shade::{Shaders, WrShaders}; pub use webrender_api as api; pub use webrender_api::euclid; -pub use resource_cache::intersect_for_tile; \ No newline at end of file diff --git a/webrender/src/picture.rs b/webrender/src/picture.rs index 16d68ceb8f..861fa55363 100644 --- a/webrender/src/picture.rs +++ b/webrender/src/picture.rs @@ -4,7 +4,7 @@ use api::{DeviceRect, FilterOp, MixBlendMode, PipelineId, PremultipliedColorF, PictureRect, PicturePoint}; use api::{DeviceIntRect, DevicePoint, LayoutRect, PictureToRasterTransform, LayoutPixel, PropertyBinding, PropertyBindingId}; -use api::{DevicePixelScale, RasterRect, RasterSpace, PictureSize, DeviceIntPoint, ColorF, ImageKey}; +use api::{DevicePixelScale, RasterRect, RasterSpace, PictureSize, DeviceIntPoint, ColorF, ImageKey, DirtyRect}; use api::{PicturePixel, RasterPixel, WorldPixel, WorldRect, ImageFormat, ImageDescriptor}; use box_shadow::{BLUR_SAMPLE_SCALE}; use clip::{ClipNodeCollector, ClipStore, ClipChainId, ClipChainNode}; @@ -1843,7 +1843,7 @@ impl PicturePrimitive { TextureFilter::Linear, None, [0.0; 3], - None, + DirtyRect::All, frame_state.gpu_cache, None, UvRectKind::Rect, diff --git a/webrender/src/render_backend.rs b/webrender/src/render_backend.rs index b7146c153c..0fc3123afd 100644 --- a/webrender/src/render_backend.rs +++ b/webrender/src/render_backend.rs @@ -15,7 +15,7 @@ use api::{DevicePixelScale, DeviceIntPoint, DeviceIntRect, DeviceIntSize}; use api::{DocumentId, DocumentLayer, ExternalScrollId, FrameMsg, HitTestFlags, HitTestResult}; use api::{IdNamespace, LayoutPoint, PipelineId, RenderNotifier, SceneMsg, ScrollClamping}; use api::{MemoryReport, VoidPtrToSizeFn}; -use api::{ScrollLocation, ScrollNodeState, TransactionMsg, ResourceUpdate, ImageKey}; +use api::{ScrollLocation, ScrollNodeState, TransactionMsg, ResourceUpdate, BlobImageKey}; use api::{NotificationRequest, Checkpoint}; use api::channel::{MsgReceiver, Payload}; #[cfg(feature = "capture")] @@ -1393,19 +1393,15 @@ impl RenderBackend { } } -fn get_blob_image_updates(updates: &[ResourceUpdate]) -> Vec { +fn get_blob_image_updates(updates: &[ResourceUpdate]) -> Vec { let mut requests = Vec::new(); for update in updates { match *update { - ResourceUpdate::AddImage(ref img) => { - if img.data.is_blob() { - requests.push(img.key); - } + ResourceUpdate::AddBlobImage(ref img) => { + requests.push(img.key); } - ResourceUpdate::UpdateImage(ref img) => { - if img.data.is_blob() { - requests.push(img.key); - } + ResourceUpdate::UpdateBlobImage(ref img) => { + requests.push(img.key); } _ => {} } diff --git a/webrender/src/render_task.rs b/webrender/src/render_task.rs index fcee8159f1..39a731503f 100644 --- a/webrender/src/render_task.rs +++ b/webrender/src/render_task.rs @@ -4,7 +4,7 @@ use api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceSize, DeviceIntSideOffsets}; use api::{DevicePixelScale, ImageDescriptor, ImageFormat}; -use api::{LineStyle, LineOrientation, LayoutSize, ColorF}; +use api::{LineStyle, LineOrientation, LayoutSize, ColorF, DirtyRect}; #[cfg(feature = "pathfinder")] use api::FontRenderMode; use border::{BorderCornerCacheKey, BorderEdgeCacheKey}; @@ -1255,7 +1255,7 @@ impl RenderTaskCache { TextureFilter::Linear, None, entry.user_data.unwrap_or([0.0; 3]), - None, + DirtyRect::All, gpu_cache, None, render_task.uv_rect_kind(), diff --git a/webrender/src/resource_cache.rs b/webrender/src/resource_cache.rs index 9bd845c67e..d395b08ca8 100644 --- a/webrender/src/resource_cache.rs +++ b/webrender/src/resource_cache.rs @@ -4,14 +4,14 @@ use api::{AddFont, BlobImageResources, AsyncBlobImageRasterizer, ResourceUpdate}; use api::{BlobImageDescriptor, BlobImageHandler, BlobImageRequest, RasterizedBlobImage}; -use api::{ClearCache, ColorF, DevicePoint, DeviceIntPoint, DeviceIntRect, DeviceIntSize}; +use api::{ClearCache, ColorF, DeviceIntPoint, DeviceIntRect, DeviceIntSize}; use api::{DebugFlags, FontInstanceKey, FontKey, FontTemplate, GlyphIndex}; use api::{ExternalImageData, ExternalImageType, BlobImageResult, BlobImageParams}; use api::{FontInstanceData, FontInstanceOptions, FontInstancePlatformOptions, FontVariation}; use api::{GlyphDimensions, IdNamespace}; -use api::{ImageData, ImageDescriptor, ImageKey, ImageRendering}; -use api::{MemoryReport, VoidPtrToSizeFn}; -use api::{TileOffset, TileSize, TileRange, BlobImageData}; +use api::{ImageData, ImageDescriptor, ImageKey, ImageRendering, ImageDirtyRect, DirtyRect}; +use api::{BlobImageKey, BlobDirtyRect, MemoryReport, VoidPtrToSizeFn}; +use api::{TileOffset, TileSize, TileRange, BlobImageData, LayoutIntRect, LayoutIntSize}; use app_units::Au; #[cfg(feature = "capture")] use capture::ExternalCaptureImage; @@ -86,6 +86,57 @@ impl CacheItem { } } +/// Represents the backing store of an image in the cache. +/// This storage can take several forms. +#[derive(Clone, Debug)] +pub enum CachedImageData { + /// A simple series of bytes, provided by the embedding and owned by WebRender. + /// The format is stored out-of-band, currently in ImageDescriptor. + Raw(Arc>), + /// An series of commands that can be rasterized into an image via an + /// embedding-provided callback. + /// + /// The commands are stored elsewhere and this variant is used as a placeholder. + Blob, + /// An image owned by the embedding, and referenced by WebRender. This may + /// take the form of a texture or a heap-allocated buffer. + External(ExternalImageData), +} + +impl From for CachedImageData { + fn from(img_data: ImageData) -> Self { + match img_data { + ImageData::Raw(data) => CachedImageData::Raw(data), + ImageData::External(data) => CachedImageData::External(data), + } + } +} + +impl CachedImageData { + /// Returns true if this represents a blob. + #[inline] + pub fn is_blob(&self) -> bool { + match *self { + CachedImageData::Blob => true, + _ => false, + } + } + + /// Returns true if this variant of CachedImageData should go through the texture + /// cache. + #[inline] + pub fn uses_texture_cache(&self) -> bool { + match *self { + CachedImageData::External(ref ext_data) => match ext_data.image_type { + ExternalImageType::TextureHandle(_) => false, + ExternalImageType::Buffer => true, + }, + CachedImageData::Blob => true, + CachedImageData::Raw(_) => true, + } + } +} + #[derive(Debug)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] @@ -113,12 +164,12 @@ enum RasterizedBlob { struct BlobImageTemplate { descriptor: ImageDescriptor, tiling: Option, - dirty_rect: Option, + dirty_rect: BlobDirtyRect, viewport_tiles: Option, } struct ImageResource { - data: ImageData, + data: CachedImageData, descriptor: ImageDescriptor, tiling: Option, viewport_tiles: Option, @@ -157,7 +208,7 @@ impl ImageTemplates { #[cfg_attr(feature = "replay", derive(Deserialize))] struct CachedImageInfo { texture_cache_handle: TextureCacheHandle, - dirty_rect: Option, + dirty_rect: ImageDirtyRect, manual_eviction: bool, } @@ -175,46 +226,6 @@ pub struct ResourceClassCache { pub user_data: U, } -pub fn intersect_for_tile( - dirty: DeviceIntRect, - clipped_tile_size: DeviceIntSize, - tile_size: TileSize, - tile_offset: TileOffset, - -) -> Option { - dirty.intersection(&DeviceIntRect::new( - DeviceIntPoint::new( - tile_offset.x as i32 * tile_size as i32, - tile_offset.y as i32 * tile_size as i32 - ), - clipped_tile_size, - )).map(|mut r| { - // we can't translate by a negative size so do it manually - r.origin.x -= tile_offset.x as i32 * tile_size as i32; - r.origin.y -= tile_offset.y as i32 * tile_size as i32; - r - }) -} - -fn merge_dirty_rect( - prev_dirty_rect: &Option, - dirty_rect: &Option, - descriptor: &ImageDescriptor, -) -> Option { - // It is important to never assume an empty dirty rect implies a full reupload here, - // although we are able to do so elsewhere. We store the descriptor's full rect instead - // There are update sequences which could cause us to forget the correct dirty regions - // regions if we cleared the dirty rect when we received None, e.g.: - // 1) Update with no dirty rect. We want to reupload everything. - // 2) Update with dirty rect B. We still want to reupload everything, not just B. - // 3) Perform the upload some time later. - match (dirty_rect, prev_dirty_rect) { - (&Some(ref rect), &Some(ref prev_rect)) => Some(rect.union(&prev_rect)), - (&Some(ref rect), &None) => Some(*rect), - (&None, _) => Some(descriptor.full_rect()), - } -} - impl ResourceClassCache where K: Clone + Hash + Eq + Debug, @@ -307,7 +318,7 @@ impl ImageRequest { impl Into for ImageRequest { fn into(self) -> BlobImageRequest { BlobImageRequest { - key: self.key, + key: BlobImageKey(self.key), tile: self.tile, } } @@ -387,11 +398,6 @@ impl BlobImageResources for Resources { None => None, } } - fn get_image(&self, key: ImageKey) -> Option<(&ImageData, &ImageDescriptor)> { - self.image_templates - .get(key) - .map(|resource| (&resource.data, &resource.descriptor)) - } } pub type GlyphDimensionsCache = FastHashMap<(FontInstance, GlyphIndex), Option>; @@ -422,8 +428,8 @@ pub struct ResourceCache { pending_image_requests: FastHashSet, blob_image_handler: Option>, - rasterized_blob_images: FastHashMap, - blob_image_templates: FastHashMap, + rasterized_blob_images: FastHashMap, + blob_image_templates: FastHashMap, // If while building a frame we encounter blobs that we didn't already // rasterize, add them to this list and rasterize them synchronously. @@ -461,11 +467,11 @@ impl ResourceCache { self.texture_cache.max_texture_size() } - fn should_tile(limit: i32, descriptor: &ImageDescriptor, data: &ImageData) -> bool { + fn should_tile(limit: i32, descriptor: &ImageDescriptor, data: &CachedImageData) -> bool { let size_check = descriptor.size.width > limit || descriptor.size.height > limit; match *data { - ImageData::Raw(_) | ImageData::Blob(_) => size_check, - ImageData::External(info) => { + CachedImageData::Raw(_) | CachedImageData::Blob => size_check, + CachedImageData::External(info) => { // External handles already represent existing textures so it does // not make sense to tile them into smaller ones. info.image_type == ExternalImageType::Buffer && size_check @@ -513,10 +519,28 @@ impl ResourceCache { if let ImageData::Raw(ref bytes) = img.data { profile_counters.image_templates.inc(bytes.len()); } - self.add_image_template(img.key, img.descriptor, img.data, img.tiling); + self.add_image_template(img.key, img.descriptor, img.data.into(), img.tiling); } ResourceUpdate::UpdateImage(img) => { - self.update_image_template(img.key, img.descriptor, img.data, img.dirty_rect); + self.update_image_template(img.key, img.descriptor, img.data.into(), &img.dirty_rect); + } + ResourceUpdate::AddBlobImage(img) => { + self.add_image_template( + img.key.as_image(), + img.descriptor, + CachedImageData::Blob, + img.tiling, + ); + } + ResourceUpdate::UpdateBlobImage(img) => { + self.update_image_template( + img.key.as_image(), + img.descriptor, + CachedImageData::Blob, + &to_image_dirty_rect( + &img.dirty_rect + ), + ); } ResourceUpdate::DeleteImage(img) => { self.delete_image_template(img); @@ -527,7 +551,7 @@ impl ResourceCache { ResourceUpdate::DeleteFontInstance(font) => { self.delete_font_instance(font); } - ResourceUpdate::SetImageVisibleArea(key, area) => { + ResourceUpdate::SetBlobImageVisibleArea(key, area) => { self.discard_tiles_outside_visible_area(key, &area); } ResourceUpdate::AddFont(_) | @@ -545,27 +569,23 @@ impl ResourceCache { ) { for update in updates.iter() { match *update { - ResourceUpdate::AddImage(ref img) => { - if let ImageData::Blob(ref blob_data) = img.data { - self.add_blob_image( - img.key, - &img.descriptor, - img.tiling, - Arc::clone(blob_data), - ); - } + ResourceUpdate::AddBlobImage(ref img) => { + self.add_blob_image( + img.key, + &img.descriptor, + img.tiling, + Arc::clone(&img.data), + ); } - ResourceUpdate::UpdateImage(ref img) => { - if let ImageData::Blob(ref blob_data) = img.data { - self.update_blob_image( - img.key, - &img.descriptor, - &img.dirty_rect, - Arc::clone(blob_data) - ); - } + ResourceUpdate::UpdateBlobImage(ref img) => { + self.update_blob_image( + img.key, + &img.descriptor, + &img.dirty_rect, + Arc::clone(&img.data), + ); } - ResourceUpdate::SetImageVisibleArea(ref key, ref area) => { + ResourceUpdate::SetBlobImageVisibleArea(ref key, ref area) => { if let Some(template) = self.blob_image_templates.get_mut(&key) { if let Some(tile_size) = template.tiling { template.viewport_tiles = Some(compute_tile_range( @@ -730,7 +750,7 @@ impl ResourceCache { &mut self, image_key: ImageKey, descriptor: ImageDescriptor, - data: ImageData, + data: CachedImageData, mut tiling: Option, ) { if tiling.is_none() && Self::should_tile(self.max_texture_size(), &descriptor, &data) { @@ -753,8 +773,8 @@ impl ResourceCache { &mut self, image_key: ImageKey, descriptor: ImageDescriptor, - data: ImageData, - dirty_rect: Option, + data: CachedImageData, + dirty_rect: &ImageDirtyRect, ) { let max_texture_size = self.max_texture_size(); let image = match self.resources.image_templates.get_mut(image_key) { @@ -771,24 +791,27 @@ impl ResourceCache { // updated independently. match self.cached_images.try_get_mut(&image_key) { Some(&mut ImageResult::UntiledAuto(ref mut entry)) => { - entry.dirty_rect = merge_dirty_rect(&entry.dirty_rect, &dirty_rect, &descriptor); + entry.dirty_rect = entry.dirty_rect.union(dirty_rect); } Some(&mut ImageResult::Multi(ref mut entries)) => { + let tile_size = tiling.unwrap(); for (key, entry) in entries.iter_mut() { - let merged_rect = merge_dirty_rect(&entry.dirty_rect, &dirty_rect, &descriptor); - - entry.dirty_rect = match (key.tile, merged_rect) { - (Some(tile), Some(rect)) => { - let tile_size = image.tiling.unwrap(); - let clipped_tile_size = compute_tile_size(&descriptor, tile_size, tile); - - rect.intersection(&DeviceIntRect::new( - DeviceIntPoint::new(tile.x as i32, tile.y as i32) * tile_size as i32, - clipped_tile_size, - )) + // We want the dirty rect relative to the tile and not the whole image. + let local_dirty_rect = match key.tile { + Some(tile) => { + dirty_rect.map(|mut rect|{ + let tile_offset = DeviceIntPoint::new( + tile.x as i32, + tile.y as i32, + ) * tile_size as i32; + rect.origin -= tile_offset.to_vector(); + + rect + }) } - _ => merged_rect, + None => *dirty_rect, }; + entry.dirty_rect = entry.dirty_rect.union(&local_dirty_rect); } } _ => {} @@ -805,7 +828,7 @@ impl ResourceCache { // Happens before scene building. pub fn add_blob_image( &mut self, - key: ImageKey, + key: BlobImageKey, descriptor: &ImageDescriptor, mut tiling: Option, data: Arc, @@ -820,12 +843,7 @@ impl ResourceCache { BlobImageTemplate { descriptor: *descriptor, tiling, - dirty_rect: Some( - DeviceIntRect::new( - DeviceIntPoint::zero(), - descriptor.size, - ) - ), + dirty_rect: DirtyRect::All, viewport_tiles: None, }, ); @@ -834,12 +852,12 @@ impl ResourceCache { // Happens before scene building. pub fn update_blob_image( &mut self, - key: ImageKey, + key: BlobImageKey, descriptor: &ImageDescriptor, - dirty_rect: &Option, + dirty_rect: &BlobDirtyRect, data: Arc, ) { - self.blob_image_handler.as_mut().unwrap().update(key, data, *dirty_rect); + self.blob_image_handler.as_mut().unwrap().update(key, data, dirty_rect); let max_texture_size = self.max_texture_size(); @@ -852,11 +870,7 @@ impl ResourceCache { *image = BlobImageTemplate { descriptor: *descriptor, tiling, - dirty_rect: match (*dirty_rect, image.dirty_rect) { - (Some(rect), Some(prev_rect)) => Some(rect.union(&prev_rect)), - (Some(rect), None) => Some(rect), - (None, _) => None, - }, + dirty_rect: dirty_rect.union(&image.dirty_rect), viewport_tiles: image.viewport_tiles, }; } @@ -872,9 +886,10 @@ impl ResourceCache { match value { Some(image) => if image.data.is_blob() { - self.blob_image_handler.as_mut().unwrap().delete(image_key); - self.blob_image_templates.remove(&image_key); - self.rasterized_blob_images.remove(&image_key); + let blob_key = BlobImageKey(image_key); + self.blob_image_handler.as_mut().unwrap().delete(blob_key); + self.blob_image_templates.remove(&blob_key); + self.rasterized_blob_images.remove(&blob_key); }, None => { warn!("Delete the non-exist key"); @@ -890,11 +905,11 @@ impl ResourceCache { ) -> bool { match self.cached_images.try_get(&image_key) { Some(ImageResult::UntiledAuto(ref info)) => { - info.dirty_rect.is_some() + !info.dirty_rect.is_empty() } Some(ImageResult::Multi(ref entries)) => { for (_, entry) in &entries.resources { - if entry.dirty_rect.is_some() { + if !entry.dirty_rect.is_empty() { return true; } } @@ -953,7 +968,7 @@ impl ResourceCache { &mut ImageResult::UntiledAuto(ref mut entry) => { Some(mem::replace(entry, CachedImageInfo { texture_cache_handle: TextureCacheHandle::invalid(), - dirty_rect: None, + dirty_rect: DirtyRect::All, manual_eviction: false, })) } @@ -976,7 +991,7 @@ impl ResourceCache { entry.insert(if request.is_untiled_auto() { ImageResult::UntiledAuto(CachedImageInfo { texture_cache_handle: TextureCacheHandle::invalid(), - dirty_rect: Some(template.descriptor.full_rect()), + dirty_rect: DirtyRect::All, manual_eviction: false, }) } else { @@ -993,7 +1008,7 @@ impl ResourceCache { entries.entry(request.into()) .or_insert(CachedImageInfo { texture_cache_handle: TextureCacheHandle::invalid(), - dirty_rect: Some(template.descriptor.full_rect()), + dirty_rect: DirtyRect::All, manual_eviction: false, }) }, @@ -1002,7 +1017,7 @@ impl ResourceCache { let needs_upload = self.texture_cache.request(&entry.texture_cache_handle, gpu_cache); - if !needs_upload && entry.dirty_rect.is_none() { + if !needs_upload && entry.dirty_rect.is_empty() { return } @@ -1021,38 +1036,31 @@ impl ResourceCache { // For some reason the blob image is missing. We'll fall back to // rasterizing it on the render backend thread. if missing { - let descriptor = match template.tiling { - Some(tile_size) => { - let tile = request.tile.unwrap(); - BlobImageDescriptor { - offset: DevicePoint::new( - tile.x as f32 * tile_size as f32, - tile.y as f32 * tile_size as f32, - ), - size: compute_tile_size( - &template.descriptor, - tile_size, - tile, - ), - format: template.descriptor.format, - } - } - None => { - BlobImageDescriptor { - offset: DevicePoint::origin(), - size: template.descriptor.size, - format: template.descriptor.format, + let descriptor = BlobImageDescriptor { + rect: match template.tiling { + Some(tile_size) => { + let tile = request.tile.unwrap(); + LayoutIntRect { + origin: point2(tile.x, tile.y) * tile_size as i32, + size: blob_size(compute_tile_size( + &template.descriptor, + tile_size, + tile, + )), + } } - } + None => blob_size(template.descriptor.size).into(), + }, + format: template.descriptor.format, }; - assert!(descriptor.size.width != 0 && descriptor.size.height != 0); + assert!(!descriptor.rect.is_empty()); self.missing_blob_images.push( BlobImageParams { request, descriptor, - dirty_rect: None, + dirty_rect: DirtyRect::All, } ); } @@ -1061,7 +1069,7 @@ impl ResourceCache { pub fn create_blob_scene_builder_requests( &mut self, - keys: &[ImageKey] + keys: &[BlobImageKey] ) -> (Option>, Vec) { if self.blob_image_handler.is_none() { return (None, Vec::new()); @@ -1085,8 +1093,9 @@ impl ResourceCache { ) }); + let image_dirty_rect = to_image_dirty_rect(&template.dirty_rect); // Don't request tiles that weren't invalidated. - if let Some(dirty_rect) = template.dirty_rect { + if let DirtyRect::Partial(dirty_rect) = image_dirty_rect { let dirty_rect = DeviceIntRect { origin: point2( dirty_rect.origin.x, @@ -1122,15 +1131,14 @@ impl ResourceCache { for_each_tile_in_range(&tiles, |tile| { let descriptor = BlobImageDescriptor { - offset: DevicePoint::new( - tile.x as f32 * tile_size as f32, - tile.y as f32 * tile_size as f32, - ), - size: compute_tile_size( - &template.descriptor, - tile_size, - tile, - ), + rect: LayoutIntRect { + origin: point2(tile.x, tile.y) * tile_size as i32, + size: blob_size(compute_tile_size( + &template.descriptor, + tile_size, + tile, + )), + }, format: template.descriptor.format, }; @@ -1144,12 +1152,12 @@ impl ResourceCache { tile: Some(tile), }, descriptor, - dirty_rect: None, + dirty_rect: DirtyRect::All, } ); }); } else { - let mut needs_upload = match self.cached_images.try_get(&key) { + let mut needs_upload = match self.cached_images.try_get(&key.as_image()) { Some(&ImageResult::UntiledAuto(ref entry)) => { self.texture_cache.needs_upload(&entry.texture_cache_handle) } @@ -1166,7 +1174,7 @@ impl ResourceCache { // // We do the latter here but it's not ideal and might want to revisit and do // the former instead. - match self.rasterized_blob_images.get(&key) { + match self.rasterized_blob_images.get(key) { Some(RasterizedBlob::NonTiled(ref queue)) => { if queue.len() > 2 { needs_upload = true; @@ -1177,7 +1185,7 @@ impl ResourceCache { let dirty_rect = if needs_upload { // The texture cache entry has been evicted, treat it as all dirty. - None + DirtyRect::All } else { template.dirty_rect }; @@ -1189,15 +1197,14 @@ impl ResourceCache { tile: None, }, descriptor: BlobImageDescriptor { - offset: DevicePoint::zero(), - size: template.descriptor.size, + rect: blob_size(template.descriptor.size).into(), format: template.descriptor.format, }, dirty_rect, } ); } - template.dirty_rect = None; + template.dirty_rect = DirtyRect::empty(); } let handler = self.blob_image_handler.as_mut().unwrap(); handler.prepare_resources(&self.resources, &blob_request_params); @@ -1206,7 +1213,7 @@ impl ResourceCache { fn discard_tiles_outside_visible_area( &mut self, - key: ImageKey, + key: BlobImageKey, area: &DeviceIntRect ) { let template = match self.blob_image_templates.get(&key) { @@ -1234,7 +1241,7 @@ impl ResourceCache { tiles.retain(|tile, _| { tile_range.contains(tile) }); let texture_cache = &mut self.texture_cache; - match self.cached_images.try_get_mut(&key) { + match self.cached_images.try_get_mut(&key.as_image()) { Some(&mut ImageResult::Multi(ref mut entries)) => { entries.retain(|key, entry| { if key.tile.is_none() || tile_range.contains(&key.tile.unwrap()) { @@ -1426,13 +1433,13 @@ impl ResourceCache { image_template.map(|image_template| { let external_image = match image_template.data { - ImageData::External(ext_image) => match ext_image.image_type { + CachedImageData::External(ext_image) => match ext_image.image_type { ExternalImageType::TextureHandle(_) => Some(ext_image), // external buffer uses resource_cache. ExternalImageType::Buffer => None, }, // raw and blob image are all using resource_cache. - ImageData::Raw(..) | ImageData::Blob(..) => None, + CachedImageData::Raw(..) | CachedImageData::Blob => None, }; ImageProperties { @@ -1511,29 +1518,29 @@ impl ResourceCache { let image_template = self.resources.image_templates.get_mut(request.key).unwrap(); debug_assert!(image_template.data.uses_texture_cache()); - let mut updates: SmallVec<[(ImageData, Option); 1]> = SmallVec::new(); + let mut updates: SmallVec<[(CachedImageData, Option); 1]> = SmallVec::new(); match image_template.data { - ImageData::Raw(..) | ImageData::External(..) => { + CachedImageData::Raw(..) | CachedImageData::External(..) => { // Safe to clone here since the Raw image data is an // Arc, and the external image data is small. updates.push((image_template.data.clone(), None)); } - ImageData::Blob(..) => { + CachedImageData::Blob => { - let blob_image = self.rasterized_blob_images.get_mut(&request.key).unwrap(); + let blob_image = self.rasterized_blob_images.get_mut(&BlobImageKey(request.key)).unwrap(); match (blob_image, request.tile) { (RasterizedBlob::Tiled(ref tiles), Some(tile)) => { let img = &tiles[&tile]; updates.push(( - ImageData::Raw(Arc::clone(&img.data)), + CachedImageData::Raw(Arc::clone(&img.data)), Some(img.rasterized_rect) )); } (RasterizedBlob::NonTiled(ref mut queue), None) => { for img in queue.drain(..) { updates.push(( - ImageData::Raw(img.data), + CachedImageData::Raw(img.data), Some(img.rasterized_rect) )); } @@ -1554,23 +1561,12 @@ impl ResourceCache { }; let mut descriptor = image_template.descriptor.clone(); - let mut local_dirty_rect; + let mut dirty_rect = entry.dirty_rect.replace_with_empty(); if let Some(tile) = request.tile { let tile_size = image_template.tiling.unwrap(); let clipped_tile_size = compute_tile_size(&descriptor, tile_size, tile); - local_dirty_rect = if let Some(rect) = entry.dirty_rect.take() { - // We should either have a dirty rect, or we are re-uploading where the dirty - // rect is ignored anyway. - let intersection = intersect_for_tile(rect, clipped_tile_size, tile_size, tile); - debug_assert!(intersection.is_some() || - self.texture_cache.needs_upload(&entry.texture_cache_handle)); - intersection - } else { - None - }; - // The tiled image could be stored on the CPU as one large image or be // already broken up into tiles. This affects the way we compute the stride // and offset. @@ -1585,15 +1581,13 @@ impl ResourceCache { } descriptor.size = clipped_tile_size; - } else { - local_dirty_rect = entry.dirty_rect.take(); } // If we are uploading the dirty region of a blob image we might have several // rects to upload so we use each of these rasterized rects rather than the // overall dirty rect of the image. - if blob_rasterized_rect.is_some() { - local_dirty_rect = blob_rasterized_rect; + if let Some(rect) = blob_rasterized_rect { + dirty_rect = DirtyRect::Partial(rect); } let filter = match request.rendering { @@ -1636,7 +1630,7 @@ impl ResourceCache { filter, Some(image_data), [0.0; 3], - local_dirty_rect, + dirty_rect, gpu_cache, None, UvRectKind::Rect, @@ -1714,9 +1708,8 @@ impl ResourceCache { // Measure images. for (_, image) in self.resources.image_templates.images.iter() { report.images += match image.data { - ImageData::Raw(ref v) => unsafe { op(v.as_ptr() as *const c_void) }, - ImageData::Blob(ref v) => unsafe { op(v.as_ptr() as *const c_void) }, - ImageData::External(..) => 0, + CachedImageData::Raw(ref v) => unsafe { op(v.as_ptr() as *const c_void) }, + CachedImageData::Blob | CachedImageData::External(..) => 0, } } @@ -1747,11 +1740,11 @@ impl ResourceCache { self.delete_image_template(key); } + let blob_f = |key: &BlobImageKey| { f(&key.as_image()) }; debug_assert!(!self.resources.image_templates.images.keys().any(&f)); debug_assert!(!self.cached_images.resources.keys().any(&f)); - debug_assert!(!self.blob_image_templates.keys().any(&f)); - debug_assert!(!self.rasterized_blob_images.keys().any(&f)); - + debug_assert!(!self.blob_image_templates.keys().any(&blob_f)); + debug_assert!(!self.rasterized_blob_images.keys().any(&blob_f)); } } @@ -1855,6 +1848,19 @@ pub struct PlainCacheOwn { #[cfg(feature = "replay")] const NATIVE_FONT: &'static [u8] = include_bytes!("../res/Proggy.ttf"); +// This currently only casts the unit but will soon apply an offset +fn to_image_dirty_rect(blob_dirty_rect: &BlobDirtyRect) -> ImageDirtyRect { + match *blob_dirty_rect { + DirtyRect::Partial(rect) => DirtyRect::Partial( + DeviceIntRect { + origin: DeviceIntPoint::new(rect.origin.x, rect.origin.y), + size: DeviceIntSize::new(rect.size.width, rect.size.height), + } + ), + DirtyRect::All => DirtyRect::All, + } +} + impl ResourceCache { #[cfg(feature = "capture")] pub fn save_capture( @@ -1913,7 +1919,7 @@ impl ResourceCache { for (&key, template) in res.image_templates.images.iter() { let desc = &template.descriptor; match template.data { - ImageData::Raw(ref arc) => { + CachedImageData::Raw(ref arc) => { let image_id = image_paths.len() + 1; let entry = match image_paths.entry(arc.as_ptr()) { Entry::Occupied(_) => continue, @@ -1935,22 +1941,21 @@ impl ResourceCache { .unwrap(); entry.insert(short_path); } - ImageData::Blob(_) => { + CachedImageData::Blob => { assert_eq!(template.tiling, None); let blob_request_params = &[ BlobImageParams { request: BlobImageRequest { - key, + key: BlobImageKey(key), //TODO: support tiled blob images // https://github.com/servo/webrender/issues/2236 tile: None, }, descriptor: BlobImageDescriptor { - size: desc.size, - offset: DevicePoint::zero(), + rect: blob_size(desc.size).into(), format: desc.format, }, - dirty_rect: None, + dirty_rect: DirtyRect::All, } ]; @@ -1980,7 +1985,7 @@ impl ResourceCache { .unwrap(); other_paths.insert(key, short_path); } - ImageData::External(ref ext) => { + CachedImageData::External(ref ext) => { let short_path = format!("externals/{}", external_images.len() + 1); other_paths.insert(key, short_path.clone()); external_images.push(ExternalCaptureImage { @@ -2015,7 +2020,7 @@ impl ResourceCache { .map(|(key, template)| { (*key, PlainImageTemplate { data: match template.data { - ImageData::Raw(ref arc) => image_paths[&arc.as_ptr()].clone(), + CachedImageData::Raw(ref arc) => image_paths[&arc.as_ptr()].clone(), _ => other_paths[key].clone(), }, descriptor: template.descriptor.clone(), @@ -2121,7 +2126,7 @@ impl ResourceCache { Some(plain) => { let ext_data = plain.external; external_images.push(plain); - ImageData::External(ext_data) + CachedImageData::External(ext_data) } None => { let arc = match raw_map.entry(template.data) { @@ -2138,7 +2143,7 @@ impl ResourceCache { .clone() } }; - ImageData::Raw(arc) + CachedImageData::Raw(arc) } }; @@ -2153,3 +2158,12 @@ impl ResourceCache { external_images } } + +/// For now the blob's coordinate space have the same pixel sizes as the +/// rendered texture's device space (only a translation is applied). +/// So going from one to the other is only a matter of casting away the unit +/// for sizes. +#[inline] +fn blob_size(device_size: DeviceIntSize) -> LayoutIntSize { + size2(device_size.width, device_size.height) +} diff --git a/webrender/src/texture_cache.rs b/webrender/src/texture_cache.rs index d3590c21b3..ac5359886a 100644 --- a/webrender/src/texture_cache.rs +++ b/webrender/src/texture_cache.rs @@ -2,8 +2,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use api::{DebugFlags, DeviceIntPoint, DeviceIntRect, DeviceIntSize}; -use api::{ExternalImageType, ImageData, ImageFormat}; +use api::{DebugFlags, DeviceIntPoint, DeviceIntRect, DeviceIntSize, DirtyRect, ImageDirtyRect}; +use api::{ExternalImageType, ImageFormat}; use api::ImageDescriptor; use device::{TextureFilter, total_gpu_bytes_allocated}; use freelist::{FreeList, FreeListHandle, UpsertResult, WeakFreeListHandle}; @@ -13,7 +13,7 @@ use internal_types::{CacheTextureId, LayerIndex, TextureUpdateList, TextureUpdat use internal_types::{TextureSource, TextureCacheAllocInfo, TextureCacheUpdate}; use profiler::{ResourceProfileCounter, TextureCacheProfileCounters}; use render_backend::{FrameId, FrameStamp}; -use resource_cache::CacheItem; +use resource_cache::{CacheItem, CachedImageData}; use std::cell::Cell; use std::cmp; use std::mem; @@ -673,9 +673,9 @@ impl TextureCache { handle: &mut TextureCacheHandle, descriptor: ImageDescriptor, filter: TextureFilter, - data: Option, + data: Option, user_data: [f32; 3], - mut dirty_rect: Option, + mut dirty_rect: ImageDirtyRect, gpu_cache: &mut GpuCache, eviction_notice: Option<&EvictionNotice>, uv_rect_kind: UvRectKind, @@ -702,7 +702,7 @@ impl TextureCache { self.allocate(¶ms, handle); // If we reallocated, we need to upload the whole item again. - dirty_rect = None; + dirty_rect = DirtyRect::All; } let entry = self.entries.get_opt_mut(handle) @@ -742,7 +742,7 @@ impl TextureCache { entry.size, entry.texture_id, layer_index as i32, - dirty_rect, + &dirty_rect, ); self.pending_updates.push_update(op); } @@ -1385,19 +1385,19 @@ impl TextureCacheUpdate { // rendering thread in order to do an upload to the right // location in the texture cache. fn new_update( - data: ImageData, + data: CachedImageData, descriptor: &ImageDescriptor, origin: DeviceIntPoint, size: DeviceIntSize, texture_id: CacheTextureId, layer_index: i32, - dirty_rect: Option, + dirty_rect: &ImageDirtyRect, ) -> TextureCacheUpdate { let source = match data { - ImageData::Blob(..) => { + CachedImageData::Blob => { panic!("The vector image should have been rasterized."); } - ImageData::External(ext_image) => match ext_image.image_type { + CachedImageData::External(ext_image) => match ext_image.image_type { ExternalImageType::TextureHandle(_) => { panic!("External texture handle should not go through texture_cache."); } @@ -1406,7 +1406,7 @@ impl TextureCacheUpdate { channel_index: ext_image.channel_index, }, }, - ImageData::Raw(bytes) => { + CachedImageData::Raw(bytes) => { let finish = descriptor.offset + descriptor.size.width * descriptor.format.bytes_per_pixel() + (descriptor.size.height - 1) * descriptor.compute_stride(); @@ -1416,8 +1416,8 @@ impl TextureCacheUpdate { } }; - let update_op = match dirty_rect { - Some(dirty) => { + let update_op = match *dirty_rect { + DirtyRect::Partial(dirty) => { // the dirty rectangle doesn't have to be within the area but has to intersect it, at least let stride = descriptor.compute_stride(); let offset = descriptor.offset + dirty.origin.y * stride + dirty.origin.x * descriptor.format.bytes_per_pixel(); @@ -1437,7 +1437,7 @@ impl TextureCacheUpdate { layer_index, } } - None => { + DirtyRect::All => { TextureCacheUpdate { id: texture_id, rect: DeviceIntRect::new(origin, size), diff --git a/webrender_api/src/api.rs b/webrender_api/src/api.rs index 52033d934b..4cec01fcd4 100644 --- a/webrender_api/src/api.rs +++ b/webrender_api/src/api.rs @@ -11,11 +11,13 @@ use std::fmt; use std::marker::PhantomData; use std::os::raw::c_void; use std::path::PathBuf; +use std::sync::Arc; use std::u32; use {BuiltDisplayList, BuiltDisplayListDescriptor, ColorF, DeviceIntPoint, DeviceIntRect}; use {DeviceIntSize, ExternalScrollId, FontInstanceKey, FontInstanceOptions}; use {FontInstancePlatformOptions, FontKey, FontVariation, GlyphDimensions, GlyphIndex, ImageData}; -use {ImageDescriptor, ImageKey, ItemTag, LayoutPoint, LayoutSize, LayoutTransform, LayoutVector2D}; +use {ImageDescriptor, ItemTag, LayoutPoint, LayoutSize, LayoutTransform, LayoutVector2D}; +use {BlobDirtyRect, ImageDirtyRect, ImageKey, BlobImageKey, BlobImageData}; use {NativeFontHandle, WorldPoint}; pub type TileSize = u16; @@ -26,8 +28,10 @@ pub type DocumentLayer = i8; pub enum ResourceUpdate { AddImage(AddImage), UpdateImage(UpdateImage), + AddBlobImage(AddBlobImage), + UpdateBlobImage(UpdateBlobImage), DeleteImage(ImageKey), - SetImageVisibleArea(ImageKey, DeviceIntRect), + SetBlobImageVisibleArea(BlobImageKey, DeviceIntRect), AddFont(AddFont), DeleteFont(FontKey), AddFontInstance(AddFontInstance), @@ -321,13 +325,13 @@ impl Transaction { key: ImageKey, descriptor: ImageDescriptor, data: ImageData, - dirty_rect: Option, + dirty_rect: &ImageDirtyRect, ) { self.resource_updates.push(ResourceUpdate::UpdateImage(UpdateImage { key, descriptor, data, - dirty_rect, + dirty_rect: *dirty_rect, })); } @@ -335,8 +339,46 @@ impl Transaction { self.resource_updates.push(ResourceUpdate::DeleteImage(key)); } - pub fn set_image_visible_area(&mut self, key: ImageKey, area: DeviceIntRect) { - self.resource_updates.push(ResourceUpdate::SetImageVisibleArea(key, area)) + pub fn add_blob_image( + &mut self, + key: BlobImageKey, + descriptor: ImageDescriptor, + data: Arc, + tiling: Option, + ) { + self.resource_updates.push( + ResourceUpdate::AddBlobImage(AddBlobImage { + key, + descriptor, + data, + tiling, + }) + ); + } + + pub fn update_blob_image( + &mut self, + key: BlobImageKey, + descriptor: ImageDescriptor, + data: Arc, + dirty_rect: &BlobDirtyRect, + ) { + self.resource_updates.push( + ResourceUpdate::UpdateBlobImage(UpdateBlobImage { + key, + descriptor, + data, + dirty_rect: *dirty_rect, + }) + ); + } + + pub fn delete_blob_image(&mut self, key: BlobImageKey) { + self.resource_updates.push(ResourceUpdate::DeleteImage(key.as_image())); + } + + pub fn set_blob_image_visible_area(&mut self, key: BlobImageKey, area: DeviceIntRect) { + self.resource_updates.push(ResourceUpdate::SetBlobImageVisibleArea(key, area)) } pub fn add_raw_font(&mut self, key: FontKey, bytes: Vec, index: u32) { @@ -464,7 +506,25 @@ pub struct UpdateImage { pub key: ImageKey, pub descriptor: ImageDescriptor, pub data: ImageData, - pub dirty_rect: Option, + pub dirty_rect: ImageDirtyRect, +} + +#[derive(Clone, Deserialize, Serialize)] +pub struct AddBlobImage { + pub key: BlobImageKey, + pub descriptor: ImageDescriptor, + //#[serde(with = "serde_image_data_raw")] + pub data: Arc, + pub tiling: Option, +} + +#[derive(Clone, Deserialize, Serialize)] +pub struct UpdateBlobImage { + pub key: BlobImageKey, + pub descriptor: ImageDescriptor, + //#[serde(with = "serde_image_data_raw")] + pub data: Arc, + pub dirty_rect: BlobDirtyRect, } #[derive(Clone, Deserialize, Serialize)] @@ -1011,6 +1071,11 @@ impl RenderApi { ImageKey::new(self.namespace_id, new_id) } + /// Creates a `BlobImageKey`. + pub fn generate_blob_image_key(&self) -> BlobImageKey { + BlobImageKey(self.generate_image_key()) + } + /// Add/remove/update resources such as images and fonts. pub fn update_resources(&self, resources: Vec) { if resources.is_empty() { diff --git a/webrender_api/src/image.rs b/webrender_api/src/image.rs index 8f52c1bbd2..d40d4cc5a5 100644 --- a/webrender_api/src/image.rs +++ b/webrender_api/src/image.rs @@ -8,9 +8,10 @@ extern crate serde_bytes; use font::{FontInstanceKey, FontInstanceData, FontKey, FontTemplate}; use std::sync::Arc; -use {DevicePoint, DeviceIntPoint, DeviceIntRect, DeviceIntSize}; -use {IdNamespace, TileOffset, TileSize}; -use euclid::size2; +use {DeviceIntPoint, DeviceIntRect, DeviceIntSize, LayoutIntRect}; +use {BlobDirtyRect, IdNamespace, TileOffset, TileSize}; +use euclid::{size2, TypedRect, num::Zero}; +use std::ops::{Add, Sub}; /// An opaque identifier describing an image registered with WebRender. /// This is used as a handle to reference images, and is used as the @@ -29,6 +30,20 @@ impl ImageKey { } } +/// An opaque identifier describing a blob image registered with WebRender. +/// This is used as a handle to reference blob images, and can be used as an +/// image in display items. +#[repr(C)] +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct BlobImageKey(pub ImageKey); + +impl BlobImageKey { + /// Interpret this blob image as an image for a display item. + pub fn as_image(&self) -> ImageKey { + self.0 + } +} + /// An arbitrary identifier for an external image provided by the /// application. It must be a unique identifier for each external /// image. @@ -228,9 +243,6 @@ pub enum ImageData { /// A simple series of bytes, provided by the embedding and owned by WebRender. /// The format is stored out-of-band, currently in ImageDescriptor. Raw(#[serde(with = "serde_image_data_raw")] Arc>), - /// An series of commands that can be rasterized into an image via an - /// embedding-provided callback. - Blob(#[serde(with = "serde_image_data_raw")] Arc), /// An image owned by the embedding, and referenced by WebRender. This may /// take the form of a texture or a heap-allocated buffer. External(ExternalImageData), @@ -261,34 +273,6 @@ impl ImageData { pub fn new_shared(bytes: Arc>) -> Self { ImageData::Raw(bytes) } - - /// Mints a new Blob ImageData. - pub fn new_blob_image(commands: BlobImageData) -> Self { - ImageData::Blob(Arc::new(commands)) - } - - /// Returns true if this ImageData represents a blob. - #[inline] - pub fn is_blob(&self) -> bool { - match *self { - ImageData::Blob(_) => true, - _ => false, - } - } - - /// Returns true if this variant of ImageData should go through the texture - /// cache. - #[inline] - pub fn uses_texture_cache(&self) -> bool { - match *self { - ImageData::External(ref ext_data) => match ext_data.image_type { - ExternalImageType::TextureHandle(_) => false, - ExternalImageType::Buffer => true, - }, - ImageData::Blob(_) => true, - ImageData::Raw(_) => true, - } - } } /// The resources exposed by the resource cache available for use by the blob rasterizer. @@ -297,8 +281,6 @@ pub trait BlobImageResources { fn get_font_data(&self, key: FontKey) -> &FontTemplate; /// Returns the `FontInstanceData` for the given key, if found. fn get_font_instance_data(&self, key: FontInstanceKey) -> Option; - /// Returns the image metadata and backing store for the given key, if found. - fn get_image(&self, key: ImageKey) -> Option<(&ImageData, &ImageDescriptor)>; } /// A handler on the render backend that can create rasterizer objects which will @@ -319,13 +301,13 @@ pub trait BlobImageHandler: Send { ); /// Register a blob image. - fn add(&mut self, key: ImageKey, data: Arc, tiling: Option); + fn add(&mut self, key: BlobImageKey, data: Arc, tiling: Option); /// Update an already registered blob image. - fn update(&mut self, key: ImageKey, data: Arc, dirty_rect: Option); + fn update(&mut self, key: BlobImageKey, data: Arc, dirty_rect: &BlobDirtyRect); /// Delete an already registered blob image. - fn delete(&mut self, key: ImageKey); + fn delete(&mut self, key: BlobImageKey); /// A hook to let the handler clean up any state related to a font which the resource /// cache is about to delete. @@ -365,7 +347,100 @@ pub struct BlobImageParams { /// the entire image when only a portion is updated. /// /// If set to None the entire image is rasterized. - pub dirty_rect: Option, + pub dirty_rect: BlobDirtyRect, +} + +/// The possible states of a Dirty rect. +/// +/// This exists because people kept getting confused with `Option`. +#[derive(Debug, Serialize, Deserialize)] +pub enum DirtyRect { + /// Everything is Dirty, equivalent to Partial(image_bounds) + All, + /// Some specific amount is dirty + Partial(TypedRect) +} + +impl DirtyRect +where + T: Copy + Clone + + PartialOrd + PartialEq + + Add + + Sub + + Zero +{ + /// Creates an empty DirtyRect (indicating nothing is invalid) + pub fn empty() -> Self { + DirtyRect::Partial(TypedRect::zero()) + } + + /// Returns whether the dirty rect is empty + pub fn is_empty(&self) -> bool { + match self { + DirtyRect::All => false, + DirtyRect::Partial(rect) => rect.is_empty(), + } + } + + /// Replaces self with the empty rect and returns the old value. + pub fn replace_with_empty(&mut self) -> Self { + ::std::mem::replace(self, DirtyRect::empty()) + } + + /// Maps over the contents of Partial. + pub fn map(self, func: F) -> Self + where F: FnOnce(TypedRect) -> TypedRect, + { + use DirtyRect::*; + + match self { + All => All, + Partial(rect) => Partial(func(rect)), + } + } + + /// Unions the dirty rects. + pub fn union(&self, other: &Self) -> Self { + use DirtyRect::*; + + match (*self, *other) { + (All, _) | (_, All) => All, + (Partial(rect1), Partial(rect2)) => Partial(rect1.union(&rect2)), + } + } + + /// Intersects the dirty rects. + pub fn intersection(&self, other: &Self) -> Self { + use DirtyRect::*; + + match (*self, *other) { + (All, rect) | (rect, All) => rect, + (Partial(rect1), Partial(rect2)) => Partial(rect1.intersection(&rect2) + .unwrap_or(TypedRect::zero())) + } + } + + /// Converts the dirty rect into a subrect of the given one via intersection. + pub fn to_subrect_of(&self, rect: &TypedRect) -> TypedRect { + use DirtyRect::*; + + match *self { + All => *rect, + Partial(dirty_rect) => dirty_rect.intersection(rect) + .unwrap_or(TypedRect::zero()), + } + } +} + +impl Copy for DirtyRect {} +impl Clone for DirtyRect { + fn clone(&self) -> Self { *self } +} + +impl From> for DirtyRect { + fn from(rect: TypedRect) -> Self { + DirtyRect::Partial(rect) + } } /// Backing store for blob image command streams. @@ -378,11 +453,9 @@ pub type BlobImageResult = Result; #[repr(C)] #[derive(Copy, Clone, Debug)] pub struct BlobImageDescriptor { - /// Size in device pixels of the blob's output image. - pub size: DeviceIntSize, - /// When tiling, offset point in device pixels of this tile in the full - /// image. Generally (0, 0) outside of tiling. - pub offset: DevicePoint, + /// Surface of the image or tile to render in the same coordinate space as + /// the drawing commands. + pub rect: LayoutIntRect, /// Format for the data in the backing store. pub format: ImageFormat, } @@ -390,7 +463,8 @@ pub struct BlobImageDescriptor { /// Representation of a rasterized blob image. This is obtained by passing /// `BlobImageData` to the embedding via the rasterization callback. pub struct RasterizedBlobImage { - /// The bounding rectangle for this blob image. + /// The rectangle that was rasterized in device pixels, relative to the + /// image or tile. pub rasterized_rect: DeviceIntRect, /// Backing store. The format is stored out of band in `BlobImageDescriptor`. pub data: Arc>, @@ -412,7 +486,7 @@ pub enum BlobImageError { #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct BlobImageRequest { /// Unique handle to the image. - pub key: ImageKey, + pub key: BlobImageKey, /// Tiling offset in number of tiles, if applicable. /// /// `None` if the image will not be tiled. diff --git a/webrender_api/src/units.rs b/webrender_api/src/units.rs index a2a2dbb395..b1f31afecb 100644 --- a/webrender_api/src/units.rs +++ b/webrender_api/src/units.rs @@ -13,12 +13,13 @@ //! in the context of coordinate systems. use app_units::Au; -use euclid::{Length, TypedRect, TypedScale, TypedSize2D, TypedTransform3D}; +use euclid::{Length, TypedRect, TypedScale, TypedSize2D, TypedTransform3D, TypedTranslation2D}; use euclid::{TypedPoint2D, TypedPoint3D, TypedVector2D, TypedVector3D, TypedSideOffsets2D}; +use DirtyRect; /// Geometry in the coordinate system of the render target (screen or intermediate /// surface) in physical pixels. -#[derive(Hash, Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] +#[derive(Hash, Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)] pub struct DevicePixel; pub type DeviceIntRect = TypedRect; @@ -75,6 +76,10 @@ pub type LayoutVector3D = TypedVector3D; pub type LayoutSize = TypedSize2D; pub type LayoutSideOffsets = TypedSideOffsets2D; +pub type LayoutIntRect = TypedRect; +pub type LayoutIntPoint = TypedPoint2D; +pub type LayoutIntSize = TypedSize2D; + /// Geometry in the document's coordinate space (logical pixels). #[derive(Hash, Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] pub struct WorldPixel; @@ -119,6 +124,11 @@ pub type LayoutRectAu = TypedRect; pub type LayoutSizeAu = TypedSize2D; pub type LayoutVector2DAu = TypedVector2D; +pub type ImageDirtyRect = DirtyRect; +pub type BlobDirtyRect = DirtyRect; + +pub type BlobToDeviceTranslation = TypedTranslation2D; + /// Stores two coordinates in texel space. The coordinates /// are stored in texel coordinates because the texture atlas /// may grow. Storing them as texel coords and normalizing diff --git a/wrench/src/blob.rs b/wrench/src/blob.rs index 4dc1afb0f5..6c6e269b5d 100644 --- a/wrench/src/blob.rs +++ b/wrench/src/blob.rs @@ -8,13 +8,11 @@ use std::collections::HashMap; use std::sync::Arc; use std::sync::Mutex; use webrender::api::*; -use webrender::intersect_for_tile; -use euclid::size2; // Serialize/deserialize the blob. -pub fn serialize_blob(color: ColorU) -> Vec { - vec![color.r, color.g, color.b, color.a] +pub fn serialize_blob(color: ColorU) -> Arc> { + Arc::new(vec![color.r, color.g, color.b, color.a]) } fn deserialize_blob(blob: &[u8]) -> Result { @@ -38,12 +36,11 @@ fn render_blob( color: ColorU, descriptor: &BlobImageDescriptor, tile: Option<(TileSize, TileOffset)>, - dirty_rect: Option, + dirty_rect: &BlobDirtyRect, ) -> BlobImageResult { // Allocate storage for the result. Right now the resource cache expects the // tiles to have have no stride or offset. - let buf_size = descriptor.size.width * - descriptor.size.height * + let buf_size = descriptor.rect.size.area() * descriptor.format.bytes_per_pixel(); let mut texels = vec![0u8; (buf_size) as usize]; @@ -54,23 +51,19 @@ fn render_blob( None => true, }; - let mut dirty_rect = dirty_rect.unwrap_or(DeviceIntRect::new( - descriptor.offset.to_i32(), - descriptor.size, - )); + let dirty_rect = dirty_rect.to_subrect_of(&descriptor.rect); - if let Some((tile_size, tile)) = tile { - dirty_rect = intersect_for_tile(dirty_rect, size2(tile_size as i32, tile_size as i32), - tile_size, tile) - .expect("empty rects should be culled by webrender"); - } + // We want the dirty rect local to the tile rather than the whole image. + let tx: BlobToDeviceTranslation = (-descriptor.rect.origin.to_vector()).into(); + + let rasterized_rect = tx.transform_rect(&dirty_rect); - for y in dirty_rect.min_y() .. dirty_rect.max_y() { - for x in dirty_rect.min_x() .. dirty_rect.max_x() { + for y in rasterized_rect.min_y() .. rasterized_rect.max_y() { + for x in rasterized_rect.min_x() .. rasterized_rect.max_x() { // Apply the tile's offset. This is important: all drawing commands should be // translated by this offset to give correct results with tiled blob images. - let x2 = x + descriptor.offset.x as i32; - let y2 = y + descriptor.offset.y as i32; + let x2 = x + descriptor.rect.origin.x; + let y2 = y + descriptor.rect.origin.y; // Render a simple checkerboard pattern let checker = if (x2 % 20 >= 10) != (y2 % 20 >= 10) { @@ -84,13 +77,14 @@ fn render_blob( match descriptor.format { ImageFormat::BGRA8 => { let a = color.a * checker + tc; - texels[((y * descriptor.size.width + x) * 4 + 0) as usize] = premul(color.b * checker + tc, a); - texels[((y * descriptor.size.width + x) * 4 + 1) as usize] = premul(color.g * checker + tc, a); - texels[((y * descriptor.size.width + x) * 4 + 2) as usize] = premul(color.r * checker + tc, a); - texels[((y * descriptor.size.width + x) * 4 + 3) as usize] = a; + let pixel_offset = ((y * descriptor.rect.size.width + x) * 4) as usize; + texels[pixel_offset + 0] = premul(color.b * checker + tc, a); + texels[pixel_offset + 1] = premul(color.g * checker + tc, a); + texels[pixel_offset + 2] = premul(color.r * checker + tc, a); + texels[pixel_offset + 3] = a; } ImageFormat::R8 => { - texels[(y * descriptor.size.width + x) as usize] = color.a * checker + tc; + texels[(y * descriptor.rect.size.width + x) as usize] = color.a * checker + tc; } _ => { return Err(BlobImageError::Other( @@ -103,7 +97,7 @@ fn render_blob( Ok(RasterizedBlobImage { data: Arc::new(texels), - rasterized_rect: dirty_rect, + rasterized_rect, }) } @@ -120,7 +114,7 @@ impl BlobCallbacks { } pub struct CheckerboardRenderer { - image_cmds: HashMap)>, + image_cmds: HashMap)>, callbacks: Arc>, } @@ -134,18 +128,18 @@ impl CheckerboardRenderer { } impl BlobImageHandler for CheckerboardRenderer { - fn add(&mut self, key: ImageKey, cmds: Arc, tile_size: Option) { + fn add(&mut self, key: BlobImageKey, cmds: Arc, tile_size: Option) { self.image_cmds .insert(key, (deserialize_blob(&cmds[..]).unwrap(), tile_size)); } - fn update(&mut self, key: ImageKey, cmds: Arc, _dirty_rect: Option) { + fn update(&mut self, key: BlobImageKey, cmds: Arc, _dirty_rect: &BlobDirtyRect) { // Here, updating is just replacing the current version of the commands with // the new one (no incremental updates). self.image_cmds.get_mut(&key).unwrap().0 = deserialize_blob(&cmds[..]).unwrap(); } - fn delete(&mut self, key: ImageKey) { + fn delete(&mut self, key: BlobImageKey) { self.image_cmds.remove(&key); } @@ -175,11 +169,11 @@ struct Command { color: ColorU, descriptor: BlobImageDescriptor, tile: Option<(TileSize, TileOffset)>, - dirty_rect: Option + dirty_rect: BlobDirtyRect, } struct Rasterizer { - image_cmds: HashMap)>, + image_cmds: HashMap)>, } impl AsyncBlobImageRasterizer for Rasterizer { @@ -205,7 +199,7 @@ impl AsyncBlobImageRasterizer for Rasterizer { ).collect(); requests.iter().map(|cmd| { - (cmd.request, render_blob(cmd.color, &cmd.descriptor, cmd.tile, cmd.dirty_rect)) + (cmd.request, render_blob(cmd.color, &cmd.descriptor, cmd.tile, &cmd.dirty_rect)) }).collect() } } diff --git a/wrench/src/json_frame_writer.rs b/wrench/src/json_frame_writer.rs index 8e366cdcbe..9bbe32bd81 100644 --- a/wrench/src/json_frame_writer.rs +++ b/wrench/src/json_frame_writer.rs @@ -120,7 +120,7 @@ impl JsonFrameWriter { ); let bytes = match img.data { ImageData::Raw(ref v) => (**v).clone(), - ImageData::External(_) | ImageData::Blob(_) => { + ImageData::External(_) => { return; } }; @@ -154,6 +154,10 @@ impl JsonFrameWriter { } } } + ResourceUpdate::AddBlobImage(..) + | ResourceUpdate::UpdateBlobImage(..) => { + println!("Blob images not supported (ignoring command)."); + } ResourceUpdate::DeleteImage(img) => { self.images.remove(&img); } @@ -177,7 +181,7 @@ impl JsonFrameWriter { ); } ResourceUpdate::DeleteFontInstance(_) => {} - ResourceUpdate::SetImageVisibleArea(..) => {} + ResourceUpdate::SetBlobImageVisibleArea(..) => {} } } } diff --git a/wrench/src/rawtest.rs b/wrench/src/rawtest.rs index 300a8f0b0f..2a824c8bab 100644 --- a/wrench/src/rawtest.rs +++ b/wrench/src/rawtest.rs @@ -94,11 +94,11 @@ impl<'a> RawtestHarness<'a> { let layout_size = LayoutSize::new(800., 800.); let mut txn = Transaction::new(); - let blob_img = self.wrench.api.generate_image_key(); - txn.add_image( + let blob_img = self.wrench.api.generate_blob_image_key(); + txn.add_blob_image( blob_img, ImageDescriptor::new(151, 56, ImageFormat::BGRA8, true, false), - ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 50, 150, 255))), + blob::serialize_blob(ColorU::new(50, 50, 150, 255)), Some(128), ); @@ -113,7 +113,7 @@ impl<'a> RawtestHarness<'a> { size(151.0, 56.0), ImageRendering::Auto, AlphaType::PremultipliedAlpha, - blob_img, + blob_img.as_image(), ColorF::WHITE, ); @@ -127,7 +127,7 @@ impl<'a> RawtestHarness<'a> { // Leaving a tiled blob image in the resource cache // confuses the `test_capture`. TODO: remove this txn = Transaction::new(); - txn.delete_image(blob_img); + txn.delete_blob_image(blob_img); self.wrench.api.update_resources(txn.resource_updates); } @@ -149,11 +149,11 @@ impl<'a> RawtestHarness<'a> { let layout_size = LayoutSize::new(800., 800.); let mut txn = Transaction::new(); - let blob_img = self.wrench.api.generate_image_key(); - txn.add_image( + let blob_img = self.wrench.api.generate_blob_image_key(); + txn.add_blob_image( blob_img, ImageDescriptor::new(1510, 111256, ImageFormat::BGRA8, false, false), - ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 50, 150, 255))), + blob::serialize_blob(ColorU::new(50, 50, 150, 255)), Some(31), ); @@ -180,10 +180,10 @@ impl<'a> RawtestHarness<'a> { image_size, ImageRendering::Auto, AlphaType::PremultipliedAlpha, - blob_img, + blob_img.as_image(), ColorF::WHITE, ); - txn.set_image_visible_area( + txn.set_blob_image_visible_area( blob_img, DeviceIntRect { origin: point2(0, 111256 / 30), @@ -235,7 +235,7 @@ impl<'a> RawtestHarness<'a> { // Leaving a tiled blob image in the resource cache // confuses the `test_capture`. TODO: remove this txn = Transaction::new(); - txn.delete_image(blob_img); + txn.delete_blob_image(blob_img); self.wrench.api.update_resources(txn.resource_updates); *self.wrench.callbacks.lock().unwrap() = blob::BlobCallbacks::new(); @@ -265,8 +265,8 @@ impl<'a> RawtestHarness<'a> { let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size); let mut txn = Transaction::new(); - let blob_img1 = self.wrench.api.generate_image_key(); - txn.add_image( + let blob_img1 = self.wrench.api.generate_blob_image_key(); + txn.add_blob_image( blob_img1, ImageDescriptor::new( image_size.width as i32, @@ -275,7 +275,7 @@ impl<'a> RawtestHarness<'a> { false, false ), - ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 50, 150, 255))), + blob::serialize_blob(ColorU::new(50, 50, 150, 255)), Some(100), ); @@ -285,7 +285,7 @@ impl<'a> RawtestHarness<'a> { image_size, ImageRendering::Auto, AlphaType::PremultipliedAlpha, - blob_img1, + blob_img1.as_image(), ColorF::WHITE, ); @@ -295,8 +295,8 @@ impl<'a> RawtestHarness<'a> { let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size); let mut txn = Transaction::new(); - let blob_img2 = self.wrench.api.generate_image_key(); - txn.add_image( + let blob_img2 = self.wrench.api.generate_blob_image_key(); + txn.add_blob_image( blob_img2, ImageDescriptor::new( image_size.width as i32, @@ -305,12 +305,12 @@ impl<'a> RawtestHarness<'a> { false, false ), - ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 50, 150, 255))), + blob::serialize_blob(ColorU::new(50, 50, 150, 255)), Some(100), ); // Set a visible rectangle that is too small. // This will force sync rasterization of the missing tiles during frame building. - txn.set_image_visible_area(blob_img2, DeviceIntRect { + txn.set_blob_image_visible_area(blob_img2, DeviceIntRect { origin: point2(200, 200), size: size2(80, 80), }); @@ -321,7 +321,7 @@ impl<'a> RawtestHarness<'a> { image_size, ImageRendering::Auto, AlphaType::PremultipliedAlpha, - blob_img2, + blob_img2.as_image(), ColorF::WHITE, ); @@ -331,8 +331,8 @@ impl<'a> RawtestHarness<'a> { assert!(pixels1 == pixels2); txn = Transaction::new(); - txn.delete_image(blob_img1); - txn.delete_image(blob_img2); + txn.delete_blob_image(blob_img1); + txn.delete_blob_image(blob_img2); self.wrench.api.update_resources(txn.resource_updates); } @@ -354,11 +354,11 @@ impl<'a> RawtestHarness<'a> { let mut txn = Transaction::new(); let layout_size = LayoutSize::new(800., 800.); - let blob_img = self.wrench.api.generate_image_key(); - txn.add_image( + let blob_img = self.wrench.api.generate_blob_image_key(); + txn.add_blob_image( blob_img, ImageDescriptor::new(1510, 1510, ImageFormat::BGRA8, false, false), - ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 50, 150, 255))), + blob::serialize_blob(ColorU::new(50, 50, 150, 255)), None, ); @@ -375,7 +375,7 @@ impl<'a> RawtestHarness<'a> { image_size, ImageRendering::Auto, AlphaType::PremultipliedAlpha, - blob_img, + blob_img.as_image(), ColorF::WHITE, ); @@ -400,7 +400,7 @@ impl<'a> RawtestHarness<'a> { image_size, ImageRendering::Auto, AlphaType::PremultipliedAlpha, - blob_img, + blob_img.as_image(), ColorF::WHITE, ); @@ -410,11 +410,11 @@ impl<'a> RawtestHarness<'a> { let mut txn = Transaction::new(); - txn.update_image( + txn.update_blob_image( blob_img, ImageDescriptor::new(1510, 1510, ImageFormat::BGRA8, false, false), - ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 50, 150, 255))), - Some(rect(10, 10, 100, 100)), + blob::serialize_blob(ColorU::new(50, 50, 150, 255)), + &rect(10, 10, 100, 100).into(), ); let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size); @@ -430,7 +430,7 @@ impl<'a> RawtestHarness<'a> { image_size, ImageRendering::Auto, AlphaType::PremultipliedAlpha, - blob_img, + blob_img.as_image(), ColorF::WHITE, ); @@ -445,7 +445,7 @@ impl<'a> RawtestHarness<'a> { // Leaving a tiled blob image in the resource cache // confuses the `test_capture`. TODO: remove this txn = Transaction::new(); - txn.delete_image(blob_img); + txn.delete_blob_image(blob_img); self.wrench.api.update_resources(txn.resource_updates); } @@ -465,11 +465,11 @@ impl<'a> RawtestHarness<'a> { { let api = &self.wrench.api; - blob_img = api.generate_image_key(); - txn.add_image( + blob_img = api.generate_blob_image_key(); + txn.add_blob_image( blob_img, ImageDescriptor::new(500, 500, ImageFormat::BGRA8, false, false), - ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 50, 150, 255))), + blob::serialize_blob(ColorU::new(50, 50, 150, 255)), None, ); } @@ -491,7 +491,7 @@ impl<'a> RawtestHarness<'a> { size(0.0, 0.0), ImageRendering::Auto, AlphaType::PremultipliedAlpha, - blob_img, + blob_img.as_image(), ColorF::WHITE, ); @@ -514,7 +514,7 @@ impl<'a> RawtestHarness<'a> { size(0.0, 0.0), ImageRendering::Auto, AlphaType::PremultipliedAlpha, - blob_img, + blob_img.as_image(), ColorF::WHITE, ); @@ -553,18 +553,18 @@ impl<'a> RawtestHarness<'a> { let (blob_img, blob_img2) = { let api = &self.wrench.api; - blob_img = api.generate_image_key(); - txn.add_image( + blob_img = api.generate_blob_image_key(); + txn.add_blob_image( blob_img, ImageDescriptor::new(500, 500, ImageFormat::BGRA8, false, false), - ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 50, 150, 255))), + blob::serialize_blob(ColorU::new(50, 50, 150, 255)), None, ); - blob_img2 = api.generate_image_key(); - txn.add_image( + blob_img2 = api.generate_blob_image_key(); + txn.add_blob_image( blob_img2, ImageDescriptor::new(500, 500, ImageFormat::BGRA8, false, false), - ImageData::new_blob_image(blob::serialize_blob(ColorU::new(80, 50, 150, 255))), + blob::serialize_blob(ColorU::new(80, 50, 150, 255)), None, ); (blob_img, blob_img2) @@ -599,7 +599,7 @@ impl<'a> RawtestHarness<'a> { size(0.0, 0.0), ImageRendering::Auto, AlphaType::PremultipliedAlpha, - blob_img, + blob_img.as_image(), ColorF::WHITE, ); builder.push_image( @@ -608,7 +608,7 @@ impl<'a> RawtestHarness<'a> { size(0.0, 0.0), ImageRendering::Auto, AlphaType::PremultipliedAlpha, - blob_img2, + blob_img2.as_image(), ColorF::WHITE, ); }; @@ -623,17 +623,17 @@ impl<'a> RawtestHarness<'a> { // update and redraw both images let mut txn = Transaction::new(); - txn.update_image( + txn.update_blob_image( blob_img, ImageDescriptor::new(500, 500, ImageFormat::BGRA8, false, false), - ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 50, 150, 255))), - Some(rect(100, 100, 100, 100)), + blob::serialize_blob(ColorU::new(50, 50, 150, 255)), + &rect(100, 100, 100, 100).into(), ); - txn.update_image( + txn.update_blob_image( blob_img2, ImageDescriptor::new(500, 500, ImageFormat::BGRA8, false, false), - ImageData::new_blob_image(blob::serialize_blob(ColorU::new(59, 50, 150, 255))), - Some(rect(100, 100, 100, 100)), + blob::serialize_blob(ColorU::new(59, 50, 150, 255)), + &rect(100, 100, 100, 100).into(), ); let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size); @@ -644,11 +644,11 @@ impl<'a> RawtestHarness<'a> { // only update the first image let mut txn = Transaction::new(); - txn.update_image( + txn.update_blob_image( blob_img, ImageDescriptor::new(500, 500, ImageFormat::BGRA8, false, false), - ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 150, 150, 255))), - Some(rect(200, 200, 100, 100)), + blob::serialize_blob(ColorU::new(50, 150, 150, 255)), + &rect(200, 200, 100, 100).into(), ); let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size); @@ -679,11 +679,11 @@ impl<'a> RawtestHarness<'a> { let mut txn = Transaction::new(); let blob_img = { - let img = self.wrench.api.generate_image_key(); - txn.add_image( + let img = self.wrench.api.generate_blob_image_key(); + txn.add_blob_image( img, ImageDescriptor::new(500, 500, ImageFormat::BGRA8, false, false), - ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 50, 150, 255))), + blob::serialize_blob(ColorU::new(50, 50, 150, 255)), None, ); img @@ -699,7 +699,7 @@ impl<'a> RawtestHarness<'a> { size(0.0, 0.0), ImageRendering::Auto, AlphaType::PremultipliedAlpha, - blob_img, + blob_img.as_image(), ColorF::WHITE, ); @@ -710,11 +710,11 @@ impl<'a> RawtestHarness<'a> { // draw the blob image a second time after updating it with the same color let mut txn = Transaction::new(); - txn.update_image( + txn.update_blob_image( blob_img, ImageDescriptor::new(500, 500, ImageFormat::BGRA8, false, false), - ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 50, 150, 255))), - Some(rect(100, 100, 100, 100)), + blob::serialize_blob(ColorU::new(50, 50, 150, 255)), + &rect(100, 100, 100, 100).into(), ); // make a new display list that refers to the first image @@ -726,7 +726,7 @@ impl<'a> RawtestHarness<'a> { size(0.0, 0.0), ImageRendering::Auto, AlphaType::PremultipliedAlpha, - blob_img, + blob_img.as_image(), ColorF::WHITE, ); @@ -735,11 +735,11 @@ impl<'a> RawtestHarness<'a> { // draw the blob image a third time after updating it with a different color let mut txn = Transaction::new(); - txn.update_image( + txn.update_blob_image( blob_img, ImageDescriptor::new(500, 500, ImageFormat::BGRA8, false, false), - ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 150, 150, 255))), - Some(rect(200, 200, 100, 100)), + blob::serialize_blob(ColorU::new(50, 150, 150, 255)), + &rect(200, 200, 100, 100).into(), ); // make a new display list that refers to the first image @@ -751,7 +751,7 @@ impl<'a> RawtestHarness<'a> { size(0.0, 0.0), ImageRendering::Auto, AlphaType::PremultipliedAlpha, - blob_img, + blob_img.as_image(), ColorF::WHITE, ); diff --git a/wrench/src/ron_frame_writer.rs b/wrench/src/ron_frame_writer.rs index 1ec0acf7ae..c0ad978d87 100644 --- a/wrench/src/ron_frame_writer.rs +++ b/wrench/src/ron_frame_writer.rs @@ -93,7 +93,7 @@ impl RonFrameWriter { ResourceUpdate::AddImage(ref img) => { let bytes = match img.data { ImageData::Raw(ref v) => (**v).clone(), - ImageData::External(_) | ImageData::Blob(_) => { + ImageData::External(_) => { return; } }; @@ -118,7 +118,6 @@ impl RonFrameWriter { data.path = None; data.bytes = Some((**bytes).clone()); } else { - // Other existing image types only make sense within the gecko integration. println!( "Wrench only supports updating buffer images ({}).", "ignoring update commands" @@ -126,6 +125,10 @@ impl RonFrameWriter { } } } + ResourceUpdate::AddBlobImage(..) + | ResourceUpdate::UpdateBlobImage(..) => { + println!("Blob images not supported (ignoring command)."); + } ResourceUpdate::DeleteImage(img) => { self.images.remove(&img); } @@ -141,7 +144,7 @@ impl RonFrameWriter { ResourceUpdate::DeleteFont(_) => {} ResourceUpdate::AddFontInstance(_) => {} ResourceUpdate::DeleteFontInstance(_) => {} - ResourceUpdate::SetImageVisibleArea(..) => {} + ResourceUpdate::SetBlobImageVisibleArea(..) => {} } } } diff --git a/wrench/src/yaml_frame_writer.rs b/wrench/src/yaml_frame_writer.rs index 26f21a01ab..fd9c81b607 100644 --- a/wrench/src/yaml_frame_writer.rs +++ b/wrench/src/yaml_frame_writer.rs @@ -550,7 +550,7 @@ impl YamlFrameWriter { ); let bytes = match img.data { ImageData::Raw(ref v) => (**v).clone(), - ImageData::External(_) | ImageData::Blob(_) => { + ImageData::External(_) => { return; } }; @@ -586,6 +586,10 @@ impl YamlFrameWriter { } } } + ResourceUpdate::AddBlobImage(..) + | ResourceUpdate::UpdateBlobImage(..) => { + println!("Blob images not supported (ignoring command)."); + } ResourceUpdate::DeleteImage(img) => { self.images.remove(&img); } @@ -609,7 +613,7 @@ impl YamlFrameWriter { ); } ResourceUpdate::DeleteFontInstance(_) => {} - ResourceUpdate::SetImageVisibleArea(..) => {} + ResourceUpdate::SetBlobImageVisibleArea(..) => {} } } }