From df1b17f9e31bb92c838e0608e0d090c56c974c11 Mon Sep 17 00:00:00 2001 From: Nicolas Silva Date: Mon, 5 Nov 2018 14:06:13 +0100 Subject: [PATCH 1/6] Store tile dirty rects in tile space. Before this PR each tile stores its own dirty rect relative to the entire image. This is somewhat confusing because this field is used to track partial uploads correctly and uploads reason about dirty rects relative to the tiles so it has to do some conversions. Storing relative to the tile is also truer to the "device space" the rects are tagged with. Last but not least the image-relative coordinates will make even less sense for tiled blob images as we start trating them as virtually infinite planes. --- webrender/src/resource_cache.rs | 67 ++++++++++++++------------------- 1 file changed, 29 insertions(+), 38 deletions(-) diff --git a/webrender/src/resource_cache.rs b/webrender/src/resource_cache.rs index 9bd845c67e..28112477b4 100644 --- a/webrender/src/resource_cache.rs +++ b/webrender/src/resource_cache.rs @@ -198,21 +198,21 @@ pub fn intersect_for_tile( fn merge_dirty_rect( prev_dirty_rect: &Option, - dirty_rect: &Option, - descriptor: &ImageDescriptor, + dirty_rect: &DeviceIntRect, + dst_size: &DeviceIntSize, ) -> 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 + // It is important to never assume a dirty rect equal to None implies a full reupload here, + // although we are able to do so elsewhere. We store the image or tile'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()), - } + let full_rect = DeviceIntRect::from(*dst_size); + match *prev_dirty_rect { + Some(ref prev_rect) => dirty_rect.union(&prev_rect), + None => *dirty_rect, + }.intersection(&full_rect) } impl ResourceClassCache @@ -756,6 +756,9 @@ impl ResourceCache { data: ImageData, dirty_rect: Option, ) { + let dirty_rect = dirty_rect.unwrap_or_else(|| { + descriptor.full_rect() + }); let max_texture_size = self.max_texture_size(); let image = match self.resources.image_templates.get_mut(image_key) { Some(res) => res, @@ -771,24 +774,25 @@ 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 = merge_dirty_rect(&entry.dirty_rect, &dirty_rect, &descriptor.size); } 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, - )) + let (local_dirty_rect, local_size) = match key.tile { + Some(tile) => { + let tile_offset = DeviceIntPoint::new(tile.x as i32, tile.y as i32) * tile_size as i32; + let mut tile_dirty_rect = dirty_rect; + tile_dirty_rect.origin -= tile_offset.to_vector(); + (tile_dirty_rect, compute_tile_size(&descriptor, tile_size, tile)) } - _ => merged_rect, + None => (dirty_rect, descriptor.size), }; + entry.dirty_rect = merge_dirty_rect( + &entry.dirty_rect, + &local_dirty_rect, + &local_size + ); } } _ => {} @@ -1554,23 +1558,12 @@ impl ResourceCache { }; let mut descriptor = image_template.descriptor.clone(); - let mut local_dirty_rect; + let mut dirty_rect = entry.dirty_rect.take(); 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 +1578,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; + dirty_rect = blob_rasterized_rect; } let filter = match request.rendering { @@ -1636,7 +1627,7 @@ impl ResourceCache { filter, Some(image_data), [0.0; 3], - local_dirty_rect, + dirty_rect, gpu_cache, None, UvRectKind::Rect, From 108fc446b033485b4d2937908f2eb8120db3dc30 Mon Sep 17 00:00:00 2001 From: Nicolas Silva Date: Mon, 5 Nov 2018 19:57:27 +0100 Subject: [PATCH 2/6] Introduce the DirtyRect type. Previously dirty rects were set up in a subtle way where early in the pipeline None is used to mean "no information" and therefore "all clean", but then later on is used to mean "all dirty" on the assumption that if a texture is still being handled, some dirty information must have been sent. None is therefore used by the internals as a way to signal things like missing cache entries without actually bothering to look up the dimensions of the image (note that "clean" can always be expressed without knowing dimensions, as it is always the same empty rect). I change the system to just consistently use a custom enum that has AllDirty and SomeDirty(empty_rect) to represent these two states. As a result the code seems much easier to understand. --- examples/blob.rs | 2 +- examples/image_resize.rs | 2 +- examples/texture_cache_stress.rs | 2 +- .../src/glyph_rasterizer/no_pathfinder.rs | 4 +- webrender/src/lib.rs | 1 - webrender/src/picture.rs | 4 +- webrender/src/render_task.rs | 4 +- webrender/src/resource_cache.rs | 126 ++++++------------ webrender/src/texture_cache.rs | 16 +-- webrender_api/src/api.rs | 8 +- webrender_api/src/image.rs | 88 +++++++++++- wrench/src/blob.rs | 33 +++-- wrench/src/rawtest.rs | 12 +- 13 files changed, 171 insertions(+), 131 deletions(-) diff --git a/examples/blob.rs b/examples/blob.rs index d8e387607c..4cb4c0efe5 100644 --- a/examples/blob.rs +++ b/examples/blob.rs @@ -135,7 +135,7 @@ impl api::BlobImageHandler for CheckerboardRenderer { .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::ImageKey, cmds: Arc, _dirty_rect: &api::DirtyRect) { // Here, updating is just replacing the current version of the commands with // the new one (no incremental updates). self.image_cmds diff --git a/examples/image_resize.rs b/examples/image_resize.rs index d683ac1097..132da17107 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::AllDirty, ); let mut txn = Transaction::new(); txn.generate_frame(); diff --git a/examples/texture_cache_stress.rs b/examples/texture_cache_stress.rs index 7a2012f65b..cffea57afd 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::AllDirty, ); }, winit::VirtualKeyCode::E => { diff --git a/webrender/src/glyph_rasterizer/no_pathfinder.rs b/webrender/src/glyph_rasterizer/no_pathfinder.rs index e94496564a..dc048330bf 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::{ImageData, ImageDescriptor, ImageFormat, DirtyRect}; use device::TextureFilter; use euclid::size2; use gpu_types::UvRectKind; @@ -184,7 +184,7 @@ impl GlyphRasterizer { TextureFilter::Linear, Some(ImageData::Raw(Arc::new(glyph.bytes))), [glyph.left, -glyph.top, glyph.scale], - None, + DirtyRect::AllDirty, 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..6a9b1096cf 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::AllDirty, frame_state.gpu_cache, None, UvRectKind::Rect, diff --git a/webrender/src/render_task.rs b/webrender/src/render_task.rs index fcee8159f1..029035338c 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::AllDirty, gpu_cache, None, render_task.uv_rect_kind(), diff --git a/webrender/src/resource_cache.rs b/webrender/src/resource_cache.rs index 28112477b4..d7c9a4c034 100644 --- a/webrender/src/resource_cache.rs +++ b/webrender/src/resource_cache.rs @@ -9,7 +9,7 @@ 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::{ImageData, ImageDescriptor, ImageKey, ImageRendering, DirtyRect}; use api::{MemoryReport, VoidPtrToSizeFn}; use api::{TileOffset, TileSize, TileRange, BlobImageData}; use app_units::Au; @@ -113,7 +113,7 @@ enum RasterizedBlob { struct BlobImageTemplate { descriptor: ImageDescriptor, tiling: Option, - dirty_rect: Option, + dirty_rect: DirtyRect, viewport_tiles: Option, } @@ -157,7 +157,7 @@ impl ImageTemplates { #[cfg_attr(feature = "replay", derive(Deserialize))] struct CachedImageInfo { texture_cache_handle: TextureCacheHandle, - dirty_rect: Option, + dirty_rect: DirtyRect, manual_eviction: bool, } @@ -175,46 +175,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: &DeviceIntRect, - dst_size: &DeviceIntSize, -) -> Option { - // It is important to never assume a dirty rect equal to None implies a full reupload here, - // although we are able to do so elsewhere. We store the image or tile'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. - let full_rect = DeviceIntRect::from(*dst_size); - match *prev_dirty_rect { - Some(ref prev_rect) => dirty_rect.union(&prev_rect), - None => *dirty_rect, - }.intersection(&full_rect) -} - impl ResourceClassCache where K: Clone + Hash + Eq + Debug, @@ -516,7 +476,7 @@ impl ResourceCache { self.add_image_template(img.key, img.descriptor, img.data, 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, &img.dirty_rect); } ResourceUpdate::DeleteImage(img) => { self.delete_image_template(img); @@ -754,11 +714,8 @@ impl ResourceCache { image_key: ImageKey, descriptor: ImageDescriptor, data: ImageData, - dirty_rect: Option, + dirty_rect: &DirtyRect, ) { - let dirty_rect = dirty_rect.unwrap_or_else(|| { - descriptor.full_rect() - }); let max_texture_size = self.max_texture_size(); let image = match self.resources.image_templates.get_mut(image_key) { Some(res) => res, @@ -774,25 +731,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.size); + 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 (local_dirty_rect, local_size) = match key.tile { + // We want the dirty rect relative to the tile and not the whole image. + let local_dirty_rect = match key.tile { Some(tile) => { - let tile_offset = DeviceIntPoint::new(tile.x as i32, tile.y as i32) * tile_size as i32; - let mut tile_dirty_rect = dirty_rect; - tile_dirty_rect.origin -= tile_offset.to_vector(); - (tile_dirty_rect, compute_tile_size(&descriptor, tile_size, 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 + }) } - None => (dirty_rect, descriptor.size), + None => *dirty_rect, }; - entry.dirty_rect = merge_dirty_rect( - &entry.dirty_rect, - &local_dirty_rect, - &local_size - ); + entry.dirty_rect = entry.dirty_rect.union(&local_dirty_rect); } } _ => {} @@ -824,12 +783,7 @@ impl ResourceCache { BlobImageTemplate { descriptor: *descriptor, tiling, - dirty_rect: Some( - DeviceIntRect::new( - DeviceIntPoint::zero(), - descriptor.size, - ) - ), + dirty_rect: DirtyRect::AllDirty, viewport_tiles: None, }, ); @@ -840,10 +794,10 @@ impl ResourceCache { &mut self, key: ImageKey, descriptor: &ImageDescriptor, - dirty_rect: &Option, + dirty_rect: &DirtyRect, 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(); @@ -856,11 +810,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, }; } @@ -894,11 +844,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; } } @@ -957,7 +907,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::AllDirty, manual_eviction: false, })) } @@ -980,7 +930,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::AllDirty, manual_eviction: false, }) } else { @@ -997,7 +947,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::AllDirty, manual_eviction: false, }) }, @@ -1006,7 +956,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 } @@ -1056,7 +1006,7 @@ impl ResourceCache { BlobImageParams { request, descriptor, - dirty_rect: None, + dirty_rect: DirtyRect::AllDirty, } ); } @@ -1090,7 +1040,7 @@ impl ResourceCache { }); // Don't request tiles that weren't invalidated. - if let Some(dirty_rect) = template.dirty_rect { + if let DirtyRect::SomeDirty(dirty_rect) = template.dirty_rect { let dirty_rect = DeviceIntRect { origin: point2( dirty_rect.origin.x, @@ -1148,7 +1098,7 @@ impl ResourceCache { tile: Some(tile), }, descriptor, - dirty_rect: None, + dirty_rect: DirtyRect::AllDirty, } ); }); @@ -1181,7 +1131,7 @@ impl ResourceCache { let dirty_rect = if needs_upload { // The texture cache entry has been evicted, treat it as all dirty. - None + DirtyRect::AllDirty } else { template.dirty_rect }; @@ -1201,7 +1151,7 @@ impl ResourceCache { } ); } - 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); @@ -1558,7 +1508,7 @@ impl ResourceCache { }; let mut descriptor = image_template.descriptor.clone(); - let mut dirty_rect = entry.dirty_rect.take(); + let mut dirty_rect = entry.dirty_rect.replace_with_empty(); if let Some(tile) = request.tile { let tile_size = image_template.tiling.unwrap(); @@ -1583,8 +1533,8 @@ impl ResourceCache { // 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() { - dirty_rect = blob_rasterized_rect; + if let Some(rect) = blob_rasterized_rect { + dirty_rect = DirtyRect::SomeDirty(rect); } let filter = match request.rendering { @@ -1941,7 +1891,7 @@ impl ResourceCache { offset: DevicePoint::zero(), format: desc.format, }, - dirty_rect: None, + dirty_rect: DirtyRect::AllDirty, } ]; diff --git a/webrender/src/texture_cache.rs b/webrender/src/texture_cache.rs index d3590c21b3..f2738dd046 100644 --- a/webrender/src/texture_cache.rs +++ b/webrender/src/texture_cache.rs @@ -2,7 +2,7 @@ * 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::{DebugFlags, DeviceIntPoint, DeviceIntRect, DeviceIntSize, DirtyRect}; use api::{ExternalImageType, ImageData, ImageFormat}; use api::ImageDescriptor; use device::{TextureFilter, total_gpu_bytes_allocated}; @@ -675,7 +675,7 @@ impl TextureCache { filter: TextureFilter, data: Option, user_data: [f32; 3], - mut dirty_rect: Option, + mut dirty_rect: DirtyRect, 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::AllDirty; } 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); } @@ -1391,7 +1391,7 @@ impl TextureCacheUpdate { size: DeviceIntSize, texture_id: CacheTextureId, layer_index: i32, - dirty_rect: Option, + dirty_rect: &DirtyRect, ) -> TextureCacheUpdate { let source = match data { ImageData::Blob(..) => { @@ -1416,8 +1416,8 @@ impl TextureCacheUpdate { } }; - let update_op = match dirty_rect { - Some(dirty) => { + let update_op = match *dirty_rect { + DirtyRect::SomeDirty(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::AllDirty => { 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..5f3f2957cc 100644 --- a/webrender_api/src/api.rs +++ b/webrender_api/src/api.rs @@ -16,7 +16,7 @@ use {BuiltDisplayList, BuiltDisplayListDescriptor, ColorF, DeviceIntPoint, Devic use {DeviceIntSize, ExternalScrollId, FontInstanceKey, FontInstanceOptions}; use {FontInstancePlatformOptions, FontKey, FontVariation, GlyphDimensions, GlyphIndex, ImageData}; use {ImageDescriptor, ImageKey, ItemTag, LayoutPoint, LayoutSize, LayoutTransform, LayoutVector2D}; -use {NativeFontHandle, WorldPoint}; +use {NativeFontHandle, WorldPoint, DirtyRect}; pub type TileSize = u16; /// Documents are rendered in the ascending order of their associated layer values. @@ -321,13 +321,13 @@ impl Transaction { key: ImageKey, descriptor: ImageDescriptor, data: ImageData, - dirty_rect: Option, + dirty_rect: &DirtyRect, ) { self.resource_updates.push(ResourceUpdate::UpdateImage(UpdateImage { key, descriptor, data, - dirty_rect, + dirty_rect: *dirty_rect, })); } @@ -464,7 +464,7 @@ pub struct UpdateImage { pub key: ImageKey, pub descriptor: ImageDescriptor, pub data: ImageData, - pub dirty_rect: Option, + pub dirty_rect: DirtyRect, } #[derive(Clone, Deserialize, Serialize)] diff --git a/webrender_api/src/image.rs b/webrender_api/src/image.rs index 8f52c1bbd2..a5f03d42c0 100644 --- a/webrender_api/src/image.rs +++ b/webrender_api/src/image.rs @@ -322,7 +322,7 @@ pub trait BlobImageHandler: Send { fn add(&mut self, key: ImageKey, 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: ImageKey, data: Arc, dirty_rect: &DirtyRect); /// Delete an already registered blob image. fn delete(&mut self, key: ImageKey); @@ -365,7 +365,91 @@ 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: DirtyRect, +} + +/// The possible states of a Dirty rect. +/// +/// This exists because people kept getting confused with `Option`. +#[repr(u8)] +#[derive(Copy, Clone, Debug, Serialize, Deserialize)] +pub enum DirtyRect { + /// Everything is Dirty, equivalent to SomeDirty(image_bounds) + AllDirty, + /// Some specific amount is dirty + SomeDirty(DeviceIntRect) +} + +impl DirtyRect { + /// Creates an empty DirtyRect (indicating nothing is invalid) + pub fn empty() -> Self { + DirtyRect::SomeDirty(DeviceIntRect::zero()) + } + + /// Returns whether the dirty rect is empty + pub fn is_empty(&self) -> bool { + use DirtyRect::*; + + match self { + AllDirty => false, + SomeDirty(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 SomeDirty. + pub fn map(self, func: F) -> Self + where F: FnOnce(DeviceIntRect) -> DeviceIntRect, + { + use DirtyRect::*; + + match self { + AllDirty => AllDirty, + SomeDirty(rect) => SomeDirty(func(rect)), + } + } + + /// Unions the dirty rects. + pub fn union(&self, other: &DirtyRect) -> DirtyRect { + use DirtyRect::*; + + match (*self, *other) { + (AllDirty, _) | (_, AllDirty) => AllDirty, + (SomeDirty(rect1), SomeDirty(rect2)) => SomeDirty(rect1.union(&rect2)), + } + } + + /// Intersects the dirty rects. + pub fn intersection(&self, other: &DirtyRect) -> DirtyRect { + use DirtyRect::*; + + match (*self, *other) { + (AllDirty, rect) | (rect, AllDirty) => rect, + (SomeDirty(rect1), SomeDirty(rect2)) => SomeDirty(rect1.intersection(&rect2) + .unwrap_or(DeviceIntRect::zero())) + } + } + + /// Converts the dirty rect into a subrect of the given one via intersection. + pub fn to_subrect_of(&self, rect: &DeviceIntRect) -> DeviceIntRect { + use DirtyRect::*; + + match *self { + AllDirty => *rect, + SomeDirty(dirty_rect) => dirty_rect.intersection(rect) + .unwrap_or(DeviceIntRect::zero()), + } + } +} + +impl From for DirtyRect { + fn from(rect: DeviceIntRect) -> Self { + DirtyRect::SomeDirty(rect) + } } /// Backing store for blob image command streams. diff --git a/wrench/src/blob.rs b/wrench/src/blob.rs index 4dc1afb0f5..7baecb4f9c 100644 --- a/wrench/src/blob.rs +++ b/wrench/src/blob.rs @@ -8,8 +8,6 @@ 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. @@ -38,7 +36,7 @@ fn render_blob( color: ColorU, descriptor: &BlobImageDescriptor, tile: Option<(TileSize, TileOffset)>, - dirty_rect: Option, + dirty_rect: &DirtyRect, ) -> BlobImageResult { // Allocate storage for the result. Right now the resource cache expects the // tiles to have have no stride or offset. @@ -54,19 +52,28 @@ fn render_blob( None => true, }; - let mut dirty_rect = dirty_rect.unwrap_or(DeviceIntRect::new( + let mut local_dirty_rect = dirty_rect.to_subrect_of(&DeviceIntRect::new( descriptor.offset.to_i32(), descriptor.size, )); + // We want the dirty rect local to the tile rather than the whole image. 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 can't translate by a negative size so do it manually. + // + // We check for emptiness here because empty rects may have zeroed out + // positions, in which case we will get a benign underflow that nonetheless + // causes a panic in debug builds. + if !local_dirty_rect.is_empty() { + local_dirty_rect.origin -= DeviceIntPoint::new( + tile.x * tile_size as i32, + tile.y * tile_size as i32, + ).to_vector(); + } } - 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 local_dirty_rect.min_y() .. local_dirty_rect.max_y() { + for x in local_dirty_rect.min_x() .. local_dirty_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; @@ -103,7 +110,7 @@ fn render_blob( Ok(RasterizedBlobImage { data: Arc::new(texels), - rasterized_rect: dirty_rect, + rasterized_rect: local_dirty_rect, }) } @@ -139,7 +146,7 @@ impl BlobImageHandler for CheckerboardRenderer { .insert(key, (deserialize_blob(&cmds[..]).unwrap(), tile_size)); } - fn update(&mut self, key: ImageKey, cmds: Arc, _dirty_rect: Option) { + fn update(&mut self, key: ImageKey, cmds: Arc, _dirty_rect: &DirtyRect) { // 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(); @@ -175,7 +182,7 @@ struct Command { color: ColorU, descriptor: BlobImageDescriptor, tile: Option<(TileSize, TileOffset)>, - dirty_rect: Option + dirty_rect: DirtyRect, } struct Rasterizer { @@ -205,7 +212,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/rawtest.rs b/wrench/src/rawtest.rs index 300a8f0b0f..ee986e0792 100644 --- a/wrench/src/rawtest.rs +++ b/wrench/src/rawtest.rs @@ -414,7 +414,7 @@ impl<'a> RawtestHarness<'a> { 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)), + &rect(10, 10, 100, 100).into(), ); let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size); @@ -627,13 +627,13 @@ impl<'a> RawtestHarness<'a> { 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)), + &rect(100, 100, 100, 100).into(), ); txn.update_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)), + &rect(100, 100, 100, 100).into(), ); let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size); @@ -648,7 +648,7 @@ impl<'a> RawtestHarness<'a> { 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)), + &rect(200, 200, 100, 100).into(), ); let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size); @@ -714,7 +714,7 @@ impl<'a> RawtestHarness<'a> { 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)), + &rect(100, 100, 100, 100).into(), ); // make a new display list that refers to the first image @@ -739,7 +739,7 @@ impl<'a> RawtestHarness<'a> { 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)), + &rect(200, 200, 100, 100).into(), ); // make a new display list that refers to the first image From f58ee4e2a411533321e73e25da6e0c58e60c1a63 Mon Sep 17 00:00:00 2001 From: Nicolas Silva Date: Wed, 7 Nov 2018 12:08:18 +0100 Subject: [PATCH 3/6] Make the DirtyRect type generic. --- examples/blob.rs | 2 +- webrender/src/resource_cache.rs | 10 +++--- webrender/src/texture_cache.rs | 6 ++-- webrender_api/src/api.rs | 6 ++-- webrender_api/src/image.rs | 54 +++++++++++++++++++-------------- webrender_api/src/units.rs | 5 ++- wrench/src/blob.rs | 6 ++-- 7 files changed, 51 insertions(+), 38 deletions(-) diff --git a/examples/blob.rs b/examples/blob.rs index 4cb4c0efe5..56b8eecfd4 100644 --- a/examples/blob.rs +++ b/examples/blob.rs @@ -135,7 +135,7 @@ impl api::BlobImageHandler for CheckerboardRenderer { .insert(key, Arc::new(deserialize_blob(&cmds[..]).unwrap())); } - fn update(&mut self, key: api::ImageKey, cmds: Arc, _dirty_rect: &api::DirtyRect) { + fn update(&mut self, key: api::ImageKey, cmds: Arc, _dirty_rect: &api::ImageDirtyRect) { // Here, updating is just replacing the current version of the commands with // the new one (no incremental updates). self.image_cmds diff --git a/webrender/src/resource_cache.rs b/webrender/src/resource_cache.rs index d7c9a4c034..77e9cb61bc 100644 --- a/webrender/src/resource_cache.rs +++ b/webrender/src/resource_cache.rs @@ -9,7 +9,7 @@ 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, DirtyRect}; +use api::{ImageData, ImageDescriptor, ImageKey, ImageRendering, ImageDirtyRect, DirtyRect}; use api::{MemoryReport, VoidPtrToSizeFn}; use api::{TileOffset, TileSize, TileRange, BlobImageData}; use app_units::Au; @@ -113,7 +113,7 @@ enum RasterizedBlob { struct BlobImageTemplate { descriptor: ImageDescriptor, tiling: Option, - dirty_rect: DirtyRect, + dirty_rect: ImageDirtyRect, viewport_tiles: Option, } @@ -157,7 +157,7 @@ impl ImageTemplates { #[cfg_attr(feature = "replay", derive(Deserialize))] struct CachedImageInfo { texture_cache_handle: TextureCacheHandle, - dirty_rect: DirtyRect, + dirty_rect: ImageDirtyRect, manual_eviction: bool, } @@ -714,7 +714,7 @@ impl ResourceCache { image_key: ImageKey, descriptor: ImageDescriptor, data: ImageData, - dirty_rect: &DirtyRect, + dirty_rect: &ImageDirtyRect, ) { let max_texture_size = self.max_texture_size(); let image = match self.resources.image_templates.get_mut(image_key) { @@ -794,7 +794,7 @@ impl ResourceCache { &mut self, key: ImageKey, descriptor: &ImageDescriptor, - dirty_rect: &DirtyRect, + dirty_rect: &ImageDirtyRect, data: Arc, ) { self.blob_image_handler.as_mut().unwrap().update(key, data, dirty_rect); diff --git a/webrender/src/texture_cache.rs b/webrender/src/texture_cache.rs index f2738dd046..5c56942264 100644 --- a/webrender/src/texture_cache.rs +++ b/webrender/src/texture_cache.rs @@ -2,7 +2,7 @@ * 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, DirtyRect}; +use api::{DebugFlags, DeviceIntPoint, DeviceIntRect, DeviceIntSize, DirtyRect, ImageDirtyRect}; use api::{ExternalImageType, ImageData, ImageFormat}; use api::ImageDescriptor; use device::{TextureFilter, total_gpu_bytes_allocated}; @@ -675,7 +675,7 @@ impl TextureCache { filter: TextureFilter, data: Option, user_data: [f32; 3], - mut dirty_rect: DirtyRect, + mut dirty_rect: ImageDirtyRect, gpu_cache: &mut GpuCache, eviction_notice: Option<&EvictionNotice>, uv_rect_kind: UvRectKind, @@ -1391,7 +1391,7 @@ impl TextureCacheUpdate { size: DeviceIntSize, texture_id: CacheTextureId, layer_index: i32, - dirty_rect: &DirtyRect, + dirty_rect: &ImageDirtyRect, ) -> TextureCacheUpdate { let source = match data { ImageData::Blob(..) => { diff --git a/webrender_api/src/api.rs b/webrender_api/src/api.rs index 5f3f2957cc..24781e0a5c 100644 --- a/webrender_api/src/api.rs +++ b/webrender_api/src/api.rs @@ -16,7 +16,7 @@ use {BuiltDisplayList, BuiltDisplayListDescriptor, ColorF, DeviceIntPoint, Devic use {DeviceIntSize, ExternalScrollId, FontInstanceKey, FontInstanceOptions}; use {FontInstancePlatformOptions, FontKey, FontVariation, GlyphDimensions, GlyphIndex, ImageData}; use {ImageDescriptor, ImageKey, ItemTag, LayoutPoint, LayoutSize, LayoutTransform, LayoutVector2D}; -use {NativeFontHandle, WorldPoint, DirtyRect}; +use {NativeFontHandle, WorldPoint, ImageDirtyRect}; pub type TileSize = u16; /// Documents are rendered in the ascending order of their associated layer values. @@ -321,7 +321,7 @@ impl Transaction { key: ImageKey, descriptor: ImageDescriptor, data: ImageData, - dirty_rect: &DirtyRect, + dirty_rect: &ImageDirtyRect, ) { self.resource_updates.push(ResourceUpdate::UpdateImage(UpdateImage { key, @@ -464,7 +464,7 @@ pub struct UpdateImage { pub key: ImageKey, pub descriptor: ImageDescriptor, pub data: ImageData, - pub dirty_rect: DirtyRect, + pub dirty_rect: ImageDirtyRect, } #[derive(Clone, Deserialize, Serialize)] diff --git a/webrender_api/src/image.rs b/webrender_api/src/image.rs index a5f03d42c0..5c75e77c44 100644 --- a/webrender_api/src/image.rs +++ b/webrender_api/src/image.rs @@ -9,8 +9,9 @@ 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 {ImageDirtyRect, 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 @@ -322,7 +323,7 @@ pub trait BlobImageHandler: Send { fn add(&mut self, key: ImageKey, data: Arc, tiling: Option); /// Update an already registered blob image. - fn update(&mut self, key: ImageKey, data: Arc, dirty_rect: &DirtyRect); + fn update(&mut self, key: ImageKey, data: Arc, dirty_rect: &ImageDirtyRect); /// Delete an already registered blob image. fn delete(&mut self, key: ImageKey); @@ -365,34 +366,38 @@ pub struct BlobImageParams { /// the entire image when only a portion is updated. /// /// If set to None the entire image is rasterized. - pub dirty_rect: DirtyRect, + pub dirty_rect: ImageDirtyRect, } /// The possible states of a Dirty rect. /// /// This exists because people kept getting confused with `Option`. -#[repr(u8)] -#[derive(Copy, Clone, Debug, Serialize, Deserialize)] -pub enum DirtyRect { +#[derive(Debug, Serialize, Deserialize)] +pub enum DirtyRect { /// Everything is Dirty, equivalent to SomeDirty(image_bounds) AllDirty, /// Some specific amount is dirty - SomeDirty(DeviceIntRect) + SomeDirty(TypedRect) } -impl DirtyRect { +impl DirtyRect +where + T: Copy + Clone + + PartialOrd + PartialEq + + Add + + Sub + + Zero +{ /// Creates an empty DirtyRect (indicating nothing is invalid) pub fn empty() -> Self { - DirtyRect::SomeDirty(DeviceIntRect::zero()) + DirtyRect::SomeDirty(TypedRect::zero()) } /// Returns whether the dirty rect is empty pub fn is_empty(&self) -> bool { - use DirtyRect::*; - match self { - AllDirty => false, - SomeDirty(rect) => rect.is_empty(), + DirtyRect::AllDirty => false, + DirtyRect::SomeDirty(rect) => rect.is_empty(), } } @@ -403,7 +408,7 @@ impl DirtyRect { /// Maps over the contents of SomeDirty. pub fn map(self, func: F) -> Self - where F: FnOnce(DeviceIntRect) -> DeviceIntRect, + where F: FnOnce(TypedRect) -> TypedRect, { use DirtyRect::*; @@ -414,7 +419,7 @@ impl DirtyRect { } /// Unions the dirty rects. - pub fn union(&self, other: &DirtyRect) -> DirtyRect { + pub fn union(&self, other: &Self) -> Self { use DirtyRect::*; match (*self, *other) { @@ -424,30 +429,35 @@ impl DirtyRect { } /// Intersects the dirty rects. - pub fn intersection(&self, other: &DirtyRect) -> DirtyRect { + pub fn intersection(&self, other: &Self) -> Self { use DirtyRect::*; match (*self, *other) { (AllDirty, rect) | (rect, AllDirty) => rect, (SomeDirty(rect1), SomeDirty(rect2)) => SomeDirty(rect1.intersection(&rect2) - .unwrap_or(DeviceIntRect::zero())) + .unwrap_or(TypedRect::zero())) } } /// Converts the dirty rect into a subrect of the given one via intersection. - pub fn to_subrect_of(&self, rect: &DeviceIntRect) -> DeviceIntRect { + pub fn to_subrect_of(&self, rect: &TypedRect) -> TypedRect { use DirtyRect::*; match *self { AllDirty => *rect, SomeDirty(dirty_rect) => dirty_rect.intersection(rect) - .unwrap_or(DeviceIntRect::zero()), + .unwrap_or(TypedRect::zero()), } } } -impl From for DirtyRect { - fn from(rect: DeviceIntRect) -> Self { +impl Copy for DirtyRect {} +impl Clone for DirtyRect { + fn clone(&self) -> Self { *self } +} + +impl From> for DirtyRect { + fn from(rect: TypedRect) -> Self { DirtyRect::SomeDirty(rect) } } diff --git a/webrender_api/src/units.rs b/webrender_api/src/units.rs index a2a2dbb395..09371a0470 100644 --- a/webrender_api/src/units.rs +++ b/webrender_api/src/units.rs @@ -15,10 +15,11 @@ use app_units::Au; use euclid::{Length, TypedRect, TypedScale, TypedSize2D, TypedTransform3D}; 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; @@ -119,6 +120,8 @@ pub type LayoutRectAu = TypedRect; pub type LayoutSizeAu = TypedSize2D; pub type LayoutVector2DAu = TypedVector2D; +pub type ImageDirtyRect = DirtyRect; + /// 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 7baecb4f9c..34316b2844 100644 --- a/wrench/src/blob.rs +++ b/wrench/src/blob.rs @@ -36,7 +36,7 @@ fn render_blob( color: ColorU, descriptor: &BlobImageDescriptor, tile: Option<(TileSize, TileOffset)>, - dirty_rect: &DirtyRect, + dirty_rect: &ImageDirtyRect, ) -> BlobImageResult { // Allocate storage for the result. Right now the resource cache expects the // tiles to have have no stride or offset. @@ -146,7 +146,7 @@ impl BlobImageHandler for CheckerboardRenderer { .insert(key, (deserialize_blob(&cmds[..]).unwrap(), tile_size)); } - fn update(&mut self, key: ImageKey, cmds: Arc, _dirty_rect: &DirtyRect) { + fn update(&mut self, key: ImageKey, cmds: Arc, _dirty_rect: &ImageDirtyRect) { // 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(); @@ -182,7 +182,7 @@ struct Command { color: ColorU, descriptor: BlobImageDescriptor, tile: Option<(TileSize, TileOffset)>, - dirty_rect: DirtyRect, + dirty_rect: ImageDirtyRect, } struct Rasterizer { From 3b444f839d0bc96b063e7cfca2cfb305377ca413 Mon Sep 17 00:00:00 2001 From: Nicolas Silva Date: Wed, 7 Nov 2018 14:08:02 +0100 Subject: [PATCH 4/6] Separate blob creation/update/deletion APIs from other types of images. Blobs will need some extra APIs that don't apply to regular images. This change will make it easier to add these extra functionalities without messing with the rest of the code. The new BlobImageKey type also adds a tiny bit of type safety to prevent some mixing blob and non blob data on the same image which is invalid. --- examples/blob.rs | 55 +++++---- webrender/src/render_backend.rs | 16 +-- webrender/src/resource_cache.rs | 199 ++++++++++++++++++-------------- webrender/src/texture_cache.rs | 2 +- webrender_api/src/api.rs | 75 +++++++++++- webrender_api/src/image.rs | 50 ++++---- webrender_api/src/units.rs | 9 +- wrench/src/blob.rs | 63 ++++------ wrench/src/json_frame_writer.rs | 8 +- wrench/src/rawtest.rs | 120 +++++++++---------- wrench/src/ron_frame_writer.rs | 9 +- wrench/src/yaml_frame_writer.rs | 8 +- 12 files changed, 360 insertions(+), 254 deletions(-) diff --git a/examples/blob.rs b/examples/blob.rs index 56b8eecfd4..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: &api::ImageDirtyRect) { + 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/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/resource_cache.rs b/webrender/src/resource_cache.rs index 77e9cb61bc..b62fcb0b7c 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, ImageDirtyRect, DirtyRect}; -use api::{MemoryReport, VoidPtrToSizeFn}; -use api::{TileOffset, TileSize, TileRange, BlobImageData}; +use api::{BlobImageKey, BlobDirtyRect, MemoryReport, VoidPtrToSizeFn}; +use api::{TileOffset, TileSize, TileRange, BlobImageData, LayoutIntRect, LayoutIntSize}; use app_units::Au; #[cfg(feature = "capture")] use capture::ExternalCaptureImage; @@ -113,7 +113,7 @@ enum RasterizedBlob { struct BlobImageTemplate { descriptor: ImageDescriptor, tiling: Option, - dirty_rect: ImageDirtyRect, + dirty_rect: BlobDirtyRect, viewport_tiles: Option, } @@ -267,7 +267,7 @@ impl ImageRequest { impl Into for ImageRequest { fn into(self) -> BlobImageRequest { BlobImageRequest { - key: self.key, + key: BlobImageKey(self.key), tile: self.tile, } } @@ -382,8 +382,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. @@ -424,7 +424,7 @@ impl ResourceCache { fn should_tile(limit: i32, descriptor: &ImageDescriptor, data: &ImageData) -> bool { let size_check = descriptor.size.width > limit || descriptor.size.height > limit; match *data { - ImageData::Raw(_) | ImageData::Blob(_) => size_check, + ImageData::Raw(_) | ImageData::Blob => size_check, ImageData::External(info) => { // External handles already represent existing textures so it does // not make sense to tile them into smaller ones. @@ -478,6 +478,24 @@ impl ResourceCache { ResourceUpdate::UpdateImage(img) => { self.update_image_template(img.key, img.descriptor, img.data, &img.dirty_rect); } + ResourceUpdate::AddBlobImage(img) => { + self.add_image_template( + img.key.as_image(), + img.descriptor, + ImageData::Blob, + img.tiling, + ); + } + ResourceUpdate::UpdateBlobImage(img) => { + self.update_image_template( + img.key.as_image(), + img.descriptor, + ImageData::Blob, + &to_image_dirty_rect( + &img.dirty_rect + ), + ); + } ResourceUpdate::DeleteImage(img) => { self.delete_image_template(img); } @@ -487,7 +505,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(_) | @@ -505,27 +523,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( @@ -768,7 +782,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, @@ -792,9 +806,9 @@ impl ResourceCache { // Happens before scene building. pub fn update_blob_image( &mut self, - key: ImageKey, + key: BlobImageKey, descriptor: &ImageDescriptor, - dirty_rect: &ImageDirtyRect, + dirty_rect: &BlobDirtyRect, data: Arc, ) { self.blob_image_handler.as_mut().unwrap().update(key, data, dirty_rect); @@ -826,9 +840,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"); @@ -975,32 +990,25 @@ 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 { @@ -1015,7 +1023,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()); @@ -1039,8 +1047,9 @@ impl ResourceCache { ) }); + let image_dirty_rect = to_image_dirty_rect(&template.dirty_rect); // Don't request tiles that weren't invalidated. - if let DirtyRect::SomeDirty(dirty_rect) = template.dirty_rect { + if let DirtyRect::SomeDirty(dirty_rect) = image_dirty_rect { let dirty_rect = DeviceIntRect { origin: point2( dirty_rect.origin.x, @@ -1076,15 +1085,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, }; @@ -1103,7 +1111,7 @@ impl ResourceCache { ); }); } 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) } @@ -1120,7 +1128,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; @@ -1143,8 +1151,7 @@ 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, @@ -1160,7 +1167,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) { @@ -1188,7 +1195,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()) { @@ -1386,7 +1393,7 @@ impl ResourceCache { ExternalImageType::Buffer => None, }, // raw and blob image are all using resource_cache. - ImageData::Raw(..) | ImageData::Blob(..) => None, + ImageData::Raw(..) | ImageData::Blob => None, }; ImageProperties { @@ -1473,9 +1480,9 @@ impl ResourceCache { // Arc, and the external image data is small. updates.push((image_template.data.clone(), None)); } - ImageData::Blob(..) => { + ImageData::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]; @@ -1656,8 +1663,7 @@ impl ResourceCache { 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, + ImageData::Blob | ImageData::External(..) => 0, } } @@ -1688,11 +1694,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)); } } @@ -1796,6 +1802,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::SomeDirty(rect) => DirtyRect::SomeDirty( + DeviceIntRect { + origin: DeviceIntPoint::new(rect.origin.x, rect.origin.y), + size: DeviceIntSize::new(rect.size.width, rect.size.height), + } + ), + DirtyRect::AllDirty => DirtyRect::AllDirty, + } +} + impl ResourceCache { #[cfg(feature = "capture")] pub fn save_capture( @@ -1876,19 +1895,18 @@ impl ResourceCache { .unwrap(); entry.insert(short_path); } - ImageData::Blob(_) => { + ImageData::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: DirtyRect::AllDirty, @@ -2094,3 +2112,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 5c56942264..2207d23e70 100644 --- a/webrender/src/texture_cache.rs +++ b/webrender/src/texture_cache.rs @@ -1394,7 +1394,7 @@ impl TextureCacheUpdate { dirty_rect: &ImageDirtyRect, ) -> TextureCacheUpdate { let source = match data { - ImageData::Blob(..) => { + ImageData::Blob => { panic!("The vector image should have been rasterized."); } ImageData::External(ext_image) => match ext_image.image_type { diff --git a/webrender_api/src/api.rs b/webrender_api/src/api.rs index 24781e0a5c..4cec01fcd4 100644 --- a/webrender_api/src/api.rs +++ b/webrender_api/src/api.rs @@ -11,12 +11,14 @@ 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 {NativeFontHandle, WorldPoint, ImageDirtyRect}; +use {ImageDescriptor, ItemTag, LayoutPoint, LayoutSize, LayoutTransform, LayoutVector2D}; +use {BlobDirtyRect, ImageDirtyRect, ImageKey, BlobImageKey, BlobImageData}; +use {NativeFontHandle, WorldPoint}; pub type TileSize = u16; /// Documents are rendered in the ascending order of their associated layer values. @@ -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), @@ -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) { @@ -467,6 +509,24 @@ pub struct UpdateImage { 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)] pub enum AddFont { Raw( @@ -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 5c75e77c44..5156c78a79 100644 --- a/webrender_api/src/image.rs +++ b/webrender_api/src/image.rs @@ -8,8 +8,8 @@ extern crate serde_bytes; use font::{FontInstanceKey, FontInstanceData, FontKey, FontTemplate}; use std::sync::Arc; -use {DevicePoint, DeviceIntPoint, DeviceIntRect, DeviceIntSize}; -use {ImageDirtyRect, IdNamespace, TileOffset, TileSize}; +use {DeviceIntPoint, DeviceIntRect, DeviceIntSize, LayoutIntRect}; +use {BlobDirtyRect, IdNamespace, TileOffset, TileSize}; use euclid::{size2, TypedRect, num::Zero}; use std::ops::{Add, Sub}; @@ -30,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. @@ -231,7 +245,7 @@ pub enum ImageData { 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), + 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), @@ -263,16 +277,11 @@ impl ImageData { 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, + ImageData::Blob => true, _ => false, } } @@ -286,7 +295,7 @@ impl ImageData { ExternalImageType::TextureHandle(_) => false, ExternalImageType::Buffer => true, }, - ImageData::Blob(_) => true, + ImageData::Blob => true, ImageData::Raw(_) => true, } } @@ -320,13 +329,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: &ImageDirtyRect); + 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. @@ -366,7 +375,7 @@ pub struct BlobImageParams { /// the entire image when only a portion is updated. /// /// If set to None the entire image is rasterized. - pub dirty_rect: ImageDirtyRect, + pub dirty_rect: BlobDirtyRect, } /// The possible states of a Dirty rect. @@ -472,11 +481,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, } @@ -484,7 +491,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>, @@ -506,7 +514,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 09371a0470..b1f31afecb 100644 --- a/webrender_api/src/units.rs +++ b/webrender_api/src/units.rs @@ -13,7 +13,7 @@ //! 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; @@ -76,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; @@ -121,6 +125,9 @@ 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 diff --git a/wrench/src/blob.rs b/wrench/src/blob.rs index 34316b2844..6c6e269b5d 100644 --- a/wrench/src/blob.rs +++ b/wrench/src/blob.rs @@ -11,8 +11,8 @@ use webrender::api::*; // 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 { @@ -36,12 +36,11 @@ fn render_blob( color: ColorU, descriptor: &BlobImageDescriptor, tile: Option<(TileSize, TileOffset)>, - dirty_rect: &ImageDirtyRect, + 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]; @@ -52,32 +51,19 @@ fn render_blob( None => true, }; - let mut local_dirty_rect = dirty_rect.to_subrect_of(&DeviceIntRect::new( - descriptor.offset.to_i32(), - descriptor.size, - )); + let dirty_rect = dirty_rect.to_subrect_of(&descriptor.rect); // We want the dirty rect local to the tile rather than the whole image. - if let Some((tile_size, tile)) = tile { - // We can't translate by a negative size so do it manually. - // - // We check for emptiness here because empty rects may have zeroed out - // positions, in which case we will get a benign underflow that nonetheless - // causes a panic in debug builds. - if !local_dirty_rect.is_empty() { - local_dirty_rect.origin -= DeviceIntPoint::new( - tile.x * tile_size as i32, - tile.y * tile_size as i32, - ).to_vector(); - } - } + let tx: BlobToDeviceTranslation = (-descriptor.rect.origin.to_vector()).into(); + + let rasterized_rect = tx.transform_rect(&dirty_rect); - for y in local_dirty_rect.min_y() .. local_dirty_rect.max_y() { - for x in local_dirty_rect.min_x() .. local_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) { @@ -91,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( @@ -110,7 +97,7 @@ fn render_blob( Ok(RasterizedBlobImage { data: Arc::new(texels), - rasterized_rect: local_dirty_rect, + rasterized_rect, }) } @@ -127,7 +114,7 @@ impl BlobCallbacks { } pub struct CheckerboardRenderer { - image_cmds: HashMap)>, + image_cmds: HashMap)>, callbacks: Arc>, } @@ -141,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: &ImageDirtyRect) { + 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); } @@ -182,11 +169,11 @@ struct Command { color: ColorU, descriptor: BlobImageDescriptor, tile: Option<(TileSize, TileOffset)>, - dirty_rect: ImageDirtyRect, + dirty_rect: BlobDirtyRect, } struct Rasterizer { - image_cmds: HashMap)>, + image_cmds: HashMap)>, } impl AsyncBlobImageRasterizer for Rasterizer { diff --git a/wrench/src/json_frame_writer.rs b/wrench/src/json_frame_writer.rs index 8e366cdcbe..a4c11fbacc 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(_) | ImageData::Blob => { 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 ee986e0792..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,10 +410,10 @@ 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))), + blob::serialize_blob(ColorU::new(50, 50, 150, 255)), &rect(10, 10, 100, 100).into(), ); @@ -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,16 +623,16 @@ 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))), + 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))), + blob::serialize_blob(ColorU::new(59, 50, 150, 255)), &rect(100, 100, 100, 100).into(), ); @@ -644,10 +644,10 @@ 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))), + blob::serialize_blob(ColorU::new(50, 150, 150, 255)), &rect(200, 200, 100, 100).into(), ); @@ -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,10 +710,10 @@ 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))), + blob::serialize_blob(ColorU::new(50, 50, 150, 255)), &rect(100, 100, 100, 100).into(), ); @@ -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,10 +735,10 @@ 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))), + blob::serialize_blob(ColorU::new(50, 150, 150, 255)), &rect(200, 200, 100, 100).into(), ); @@ -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..7281b20a44 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(_) | ImageData::Blob => { 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..fe8ab4e5aa 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(_) | ImageData::Blob => { 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(..) => {} } } } From d05f8adab2ce344ff1a77b7cf5bc31b4511627c9 Mon Sep 17 00:00:00 2001 From: Nicolas Silva Date: Wed, 21 Nov 2018 11:21:22 +0100 Subject: [PATCH 5/6] Rename DirtyRect SomeDirty/AllDirty into Partial/All. --- examples/image_resize.rs | 2 +- examples/texture_cache_stress.rs | 2 +- .../src/glyph_rasterizer/no_pathfinder.rs | 2 +- webrender/src/picture.rs | 2 +- webrender/src/render_task.rs | 2 +- webrender/src/resource_cache.rs | 24 +++++++------- webrender/src/texture_cache.rs | 6 ++-- webrender_api/src/image.rs | 32 +++++++++---------- 8 files changed, 36 insertions(+), 36 deletions(-) diff --git a/examples/image_resize.rs b/examples/image_resize.rs index 132da17107..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), - &DirtyRect::AllDirty, + &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 cffea57afd..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()), - &DirtyRect::AllDirty, + &DirtyRect::All, ); }, winit::VirtualKeyCode::E => { diff --git a/webrender/src/glyph_rasterizer/no_pathfinder.rs b/webrender/src/glyph_rasterizer/no_pathfinder.rs index dc048330bf..869aced985 100644 --- a/webrender/src/glyph_rasterizer/no_pathfinder.rs +++ b/webrender/src/glyph_rasterizer/no_pathfinder.rs @@ -184,7 +184,7 @@ impl GlyphRasterizer { TextureFilter::Linear, Some(ImageData::Raw(Arc::new(glyph.bytes))), [glyph.left, -glyph.top, glyph.scale], - DirtyRect::AllDirty, + DirtyRect::All, gpu_cache, Some(glyph_key_cache.eviction_notice()), UvRectKind::Rect, diff --git a/webrender/src/picture.rs b/webrender/src/picture.rs index 6a9b1096cf..861fa55363 100644 --- a/webrender/src/picture.rs +++ b/webrender/src/picture.rs @@ -1843,7 +1843,7 @@ impl PicturePrimitive { TextureFilter::Linear, None, [0.0; 3], - DirtyRect::AllDirty, + DirtyRect::All, frame_state.gpu_cache, None, UvRectKind::Rect, diff --git a/webrender/src/render_task.rs b/webrender/src/render_task.rs index 029035338c..39a731503f 100644 --- a/webrender/src/render_task.rs +++ b/webrender/src/render_task.rs @@ -1255,7 +1255,7 @@ impl RenderTaskCache { TextureFilter::Linear, None, entry.user_data.unwrap_or([0.0; 3]), - DirtyRect::AllDirty, + 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 b62fcb0b7c..2eb111e943 100644 --- a/webrender/src/resource_cache.rs +++ b/webrender/src/resource_cache.rs @@ -797,7 +797,7 @@ impl ResourceCache { BlobImageTemplate { descriptor: *descriptor, tiling, - dirty_rect: DirtyRect::AllDirty, + dirty_rect: DirtyRect::All, viewport_tiles: None, }, ); @@ -922,7 +922,7 @@ impl ResourceCache { &mut ImageResult::UntiledAuto(ref mut entry) => { Some(mem::replace(entry, CachedImageInfo { texture_cache_handle: TextureCacheHandle::invalid(), - dirty_rect: DirtyRect::AllDirty, + dirty_rect: DirtyRect::All, manual_eviction: false, })) } @@ -945,7 +945,7 @@ impl ResourceCache { entry.insert(if request.is_untiled_auto() { ImageResult::UntiledAuto(CachedImageInfo { texture_cache_handle: TextureCacheHandle::invalid(), - dirty_rect: DirtyRect::AllDirty, + dirty_rect: DirtyRect::All, manual_eviction: false, }) } else { @@ -962,7 +962,7 @@ impl ResourceCache { entries.entry(request.into()) .or_insert(CachedImageInfo { texture_cache_handle: TextureCacheHandle::invalid(), - dirty_rect: DirtyRect::AllDirty, + dirty_rect: DirtyRect::All, manual_eviction: false, }) }, @@ -1014,7 +1014,7 @@ impl ResourceCache { BlobImageParams { request, descriptor, - dirty_rect: DirtyRect::AllDirty, + dirty_rect: DirtyRect::All, } ); } @@ -1049,7 +1049,7 @@ impl ResourceCache { let image_dirty_rect = to_image_dirty_rect(&template.dirty_rect); // Don't request tiles that weren't invalidated. - if let DirtyRect::SomeDirty(dirty_rect) = image_dirty_rect { + if let DirtyRect::Partial(dirty_rect) = image_dirty_rect { let dirty_rect = DeviceIntRect { origin: point2( dirty_rect.origin.x, @@ -1106,7 +1106,7 @@ impl ResourceCache { tile: Some(tile), }, descriptor, - dirty_rect: DirtyRect::AllDirty, + dirty_rect: DirtyRect::All, } ); }); @@ -1139,7 +1139,7 @@ impl ResourceCache { let dirty_rect = if needs_upload { // The texture cache entry has been evicted, treat it as all dirty. - DirtyRect::AllDirty + DirtyRect::All } else { template.dirty_rect }; @@ -1541,7 +1541,7 @@ impl ResourceCache { // rects to upload so we use each of these rasterized rects rather than the // overall dirty rect of the image. if let Some(rect) = blob_rasterized_rect { - dirty_rect = DirtyRect::SomeDirty(rect); + dirty_rect = DirtyRect::Partial(rect); } let filter = match request.rendering { @@ -1805,13 +1805,13 @@ 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::SomeDirty(rect) => DirtyRect::SomeDirty( + 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::AllDirty => DirtyRect::AllDirty, + DirtyRect::All => DirtyRect::All, } } @@ -1909,7 +1909,7 @@ impl ResourceCache { rect: blob_size(desc.size).into(), format: desc.format, }, - dirty_rect: DirtyRect::AllDirty, + dirty_rect: DirtyRect::All, } ]; diff --git a/webrender/src/texture_cache.rs b/webrender/src/texture_cache.rs index 2207d23e70..75a647ecbe 100644 --- a/webrender/src/texture_cache.rs +++ b/webrender/src/texture_cache.rs @@ -702,7 +702,7 @@ impl TextureCache { self.allocate(¶ms, handle); // If we reallocated, we need to upload the whole item again. - dirty_rect = DirtyRect::AllDirty; + dirty_rect = DirtyRect::All; } let entry = self.entries.get_opt_mut(handle) @@ -1417,7 +1417,7 @@ impl TextureCacheUpdate { }; let update_op = match *dirty_rect { - DirtyRect::SomeDirty(dirty) => { + 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, } } - DirtyRect::AllDirty => { + DirtyRect::All => { TextureCacheUpdate { id: texture_id, rect: DeviceIntRect::new(origin, size), diff --git a/webrender_api/src/image.rs b/webrender_api/src/image.rs index 5156c78a79..1d973635f3 100644 --- a/webrender_api/src/image.rs +++ b/webrender_api/src/image.rs @@ -383,10 +383,10 @@ pub struct BlobImageParams { /// This exists because people kept getting confused with `Option`. #[derive(Debug, Serialize, Deserialize)] pub enum DirtyRect { - /// Everything is Dirty, equivalent to SomeDirty(image_bounds) - AllDirty, + /// Everything is Dirty, equivalent to Partial(image_bounds) + All, /// Some specific amount is dirty - SomeDirty(TypedRect) + Partial(TypedRect) } impl DirtyRect @@ -399,14 +399,14 @@ where { /// Creates an empty DirtyRect (indicating nothing is invalid) pub fn empty() -> Self { - DirtyRect::SomeDirty(TypedRect::zero()) + DirtyRect::Partial(TypedRect::zero()) } /// Returns whether the dirty rect is empty pub fn is_empty(&self) -> bool { match self { - DirtyRect::AllDirty => false, - DirtyRect::SomeDirty(rect) => rect.is_empty(), + DirtyRect::All => false, + DirtyRect::Partial(rect) => rect.is_empty(), } } @@ -415,15 +415,15 @@ where ::std::mem::replace(self, DirtyRect::empty()) } - /// Maps over the contents of SomeDirty. + /// Maps over the contents of Partial. pub fn map(self, func: F) -> Self where F: FnOnce(TypedRect) -> TypedRect, { use DirtyRect::*; match self { - AllDirty => AllDirty, - SomeDirty(rect) => SomeDirty(func(rect)), + All => All, + Partial(rect) => Partial(func(rect)), } } @@ -432,8 +432,8 @@ where use DirtyRect::*; match (*self, *other) { - (AllDirty, _) | (_, AllDirty) => AllDirty, - (SomeDirty(rect1), SomeDirty(rect2)) => SomeDirty(rect1.union(&rect2)), + (All, _) | (_, All) => All, + (Partial(rect1), Partial(rect2)) => Partial(rect1.union(&rect2)), } } @@ -442,8 +442,8 @@ where use DirtyRect::*; match (*self, *other) { - (AllDirty, rect) | (rect, AllDirty) => rect, - (SomeDirty(rect1), SomeDirty(rect2)) => SomeDirty(rect1.intersection(&rect2) + (All, rect) | (rect, All) => rect, + (Partial(rect1), Partial(rect2)) => Partial(rect1.intersection(&rect2) .unwrap_or(TypedRect::zero())) } } @@ -453,8 +453,8 @@ where use DirtyRect::*; match *self { - AllDirty => *rect, - SomeDirty(dirty_rect) => dirty_rect.intersection(rect) + All => *rect, + Partial(dirty_rect) => dirty_rect.intersection(rect) .unwrap_or(TypedRect::zero()), } } @@ -467,7 +467,7 @@ impl Clone for DirtyRect { impl From> for DirtyRect { fn from(rect: TypedRect) -> Self { - DirtyRect::SomeDirty(rect) + DirtyRect::Partial(rect) } } From b18556ea162a84e5e30c89b79b2c5121478aaadc Mon Sep 17 00:00:00 2001 From: Nicolas Silva Date: Wed, 21 Nov 2018 16:49:28 +0100 Subject: [PATCH 6/6] Use separate enum to describe image storage in the API and the resource cache. --- .../src/glyph_rasterizer/no_pathfinder.rs | 5 +- webrender/src/resource_cache.rs | 106 +++++++++++++----- webrender/src/texture_cache.rs | 14 +-- webrender_api/src/image.rs | 28 ----- wrench/src/json_frame_writer.rs | 2 +- wrench/src/ron_frame_writer.rs | 2 +- wrench/src/yaml_frame_writer.rs | 2 +- 7 files changed, 89 insertions(+), 70 deletions(-) diff --git a/webrender/src/glyph_rasterizer/no_pathfinder.rs b/webrender/src/glyph_rasterizer/no_pathfinder.rs index 869aced985..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, DirtyRect}; +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,7 +183,7 @@ 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], DirtyRect::All, gpu_cache, diff --git a/webrender/src/resource_cache.rs b/webrender/src/resource_cache.rs index 2eb111e943..d395b08ca8 100644 --- a/webrender/src/resource_cache.rs +++ b/webrender/src/resource_cache.rs @@ -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))] @@ -118,7 +169,7 @@ struct BlobImageTemplate { } struct ImageResource { - data: ImageData, + data: CachedImageData, descriptor: ImageDescriptor, tiling: Option, viewport_tiles: Option, @@ -347,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>; @@ -421,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 @@ -473,16 +519,16 @@ 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, - ImageData::Blob, + CachedImageData::Blob, img.tiling, ); } @@ -490,7 +536,7 @@ impl ResourceCache { self.update_image_template( img.key.as_image(), img.descriptor, - ImageData::Blob, + CachedImageData::Blob, &to_image_dirty_rect( &img.dirty_rect ), @@ -704,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) { @@ -727,7 +773,7 @@ impl ResourceCache { &mut self, image_key: ImageKey, descriptor: ImageDescriptor, - data: ImageData, + data: CachedImageData, dirty_rect: &ImageDirtyRect, ) { let max_texture_size = self.max_texture_size(); @@ -1387,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 { @@ -1472,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(&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) )); } @@ -1662,8 +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 | ImageData::External(..) => 0, + CachedImageData::Raw(ref v) => unsafe { op(v.as_ptr() as *const c_void) }, + CachedImageData::Blob | CachedImageData::External(..) => 0, } } @@ -1873,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, @@ -1895,7 +1941,7 @@ impl ResourceCache { .unwrap(); entry.insert(short_path); } - ImageData::Blob => { + CachedImageData::Blob => { assert_eq!(template.tiling, None); let blob_request_params = &[ BlobImageParams { @@ -1939,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 { @@ -1974,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(), @@ -2080,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) { @@ -2097,7 +2143,7 @@ impl ResourceCache { .clone() } }; - ImageData::Raw(arc) + CachedImageData::Raw(arc) } }; diff --git a/webrender/src/texture_cache.rs b/webrender/src/texture_cache.rs index 75a647ecbe..ac5359886a 100644 --- a/webrender/src/texture_cache.rs +++ b/webrender/src/texture_cache.rs @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use api::{DebugFlags, DeviceIntPoint, DeviceIntRect, DeviceIntSize, DirtyRect, ImageDirtyRect}; -use api::{ExternalImageType, ImageData, ImageFormat}; +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,7 +673,7 @@ impl TextureCache { handle: &mut TextureCacheHandle, descriptor: ImageDescriptor, filter: TextureFilter, - data: Option, + data: Option, user_data: [f32; 3], mut dirty_rect: ImageDirtyRect, gpu_cache: &mut GpuCache, @@ -1385,7 +1385,7 @@ 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, @@ -1394,10 +1394,10 @@ impl TextureCacheUpdate { 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(); diff --git a/webrender_api/src/image.rs b/webrender_api/src/image.rs index 1d973635f3..d40d4cc5a5 100644 --- a/webrender_api/src/image.rs +++ b/webrender_api/src/image.rs @@ -243,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, /// 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), @@ -276,29 +273,6 @@ impl ImageData { pub fn new_shared(bytes: Arc>) -> Self { ImageData::Raw(bytes) } - - /// 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. @@ -307,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 diff --git a/wrench/src/json_frame_writer.rs b/wrench/src/json_frame_writer.rs index a4c11fbacc..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; } }; diff --git a/wrench/src/ron_frame_writer.rs b/wrench/src/ron_frame_writer.rs index 7281b20a44..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; } }; diff --git a/wrench/src/yaml_frame_writer.rs b/wrench/src/yaml_frame_writer.rs index fe8ab4e5aa..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; } };