From 240b8ed79c94e61a1f5781ed6ed2725298b93fbe Mon Sep 17 00:00:00 2001 From: Hiroyuki Ikezoe Date: Thu, 27 Feb 2020 09:56:57 +0000 Subject: [PATCH 01/33] Bug 1510030 - Don't return the current opacity value from update_opacity_binding. r=gw The return value is not used at all. Differential Revision: https://phabricator.services.mozilla.com/D63600 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/921116ca67a59fb323b74d1a610249c2448101d7 --- webrender/src/prim_store/mod.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/webrender/src/prim_store/mod.rs b/webrender/src/prim_store/mod.rs index 9626d5413f..09c98e2402 100644 --- a/webrender/src/prim_store/mod.rs +++ b/webrender/src/prim_store/mod.rs @@ -4306,13 +4306,10 @@ fn update_opacity_binding( opacity_bindings: &mut OpacityBindingStorage, opacity_binding_index: OpacityBindingIndex, scene_properties: &SceneProperties, -) -> f32 { - if opacity_binding_index == OpacityBindingIndex::INVALID { - 1.0 - } else { +) { + if opacity_binding_index != OpacityBindingIndex::INVALID { let binding = &mut opacity_bindings[opacity_binding_index]; binding.update(scene_properties); - binding.current } } From 0809ea19fe76710a364515264efaca9e0db751b7 Mon Sep 17 00:00:00 2001 From: Hiroyuki Ikezoe Date: Thu, 27 Feb 2020 09:57:06 +0000 Subject: [PATCH 02/33] Bug 1510030 - Fix the comment for OpacityBinding::update(). r=gw Differential Revision: https://phabricator.services.mozilla.com/D63601 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/737f4bc3afd3e823fd3f0e253388789a8c0d0299 --- webrender/src/prim_store/mod.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/webrender/src/prim_store/mod.rs b/webrender/src/prim_store/mod.rs index 09c98e2402..dbcd4f8825 100644 --- a/webrender/src/prim_store/mod.rs +++ b/webrender/src/prim_store/mod.rs @@ -918,8 +918,7 @@ impl OpacityBinding { } // Resolve the current value of each opacity binding, and - // store that as a single combined opacity. Returns true - // if the opacity value changed from last time. + // store that as a single combined opacity. pub fn update(&mut self, scene_properties: &SceneProperties) { let mut new_opacity = 1.0; From 8aa347d44ec1d910a96202ade83995093e2559ce Mon Sep 17 00:00:00 2001 From: Hiroyuki Ikezoe Date: Thu, 27 Feb 2020 09:57:15 +0000 Subject: [PATCH 03/33] Bug 1510030 - Make prepare_prim_for_render private. r=gw Differential Revision: https://phabricator.services.mozilla.com/D63603 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/6b3af91a7ce6e5aee774cdde1c0dc5e2da7bebae --- webrender/src/prim_store/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webrender/src/prim_store/mod.rs b/webrender/src/prim_store/mod.rs index dbcd4f8825..ed39fc39e2 100644 --- a/webrender/src/prim_store/mod.rs +++ b/webrender/src/prim_store/mod.rs @@ -2656,7 +2656,7 @@ impl PrimitiveStore { self.pictures[pic_index.0].requested_composite_mode = None; } - pub fn prepare_prim_for_render( + fn prepare_prim_for_render( &mut self, prim_instance: &mut PrimitiveInstance, prim_spatial_node_index: SpatialNodeIndex, From b2bc0a83aa334930a4a72952f9b52b4e7a11ff15 Mon Sep 17 00:00:00 2001 From: Hiroyuki Ikezoe Date: Thu, 27 Feb 2020 09:57:23 +0000 Subject: [PATCH 04/33] Bug 1510030 - Implement WebRender backend to run background color animations on the compositor. r=gw,boris Differential Revision: https://phabricator.services.mozilla.com/D63604 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/087518046b0c65aebc0c976bf2f736acf7cd4a89 --- webrender/src/box_shadow.rs | 5 +- webrender/src/picture.rs | 158 +++++++++++++++++++++++++++--- webrender/src/prim_store/mod.rs | 73 +++++++++++--- webrender/src/scene.rs | 28 ++++++ webrender/src/scene_building.rs | 20 ++-- webrender_api/src/api.rs | 50 +++++++++- webrender_api/src/display_item.rs | 4 +- webrender_api/src/display_list.rs | 14 ++- wrench/src/scene.rs | 18 ++++ wrench/src/yaml_frame_writer.rs | 8 +- 10 files changed, 332 insertions(+), 46 deletions(-) diff --git a/webrender/src/box_shadow.rs b/webrender/src/box_shadow.rs index 124d44d5de..eb8d4d0d4e 100644 --- a/webrender/src/box_shadow.rs +++ b/webrender/src/box_shadow.rs @@ -3,6 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use api::{BorderRadius, BoxShadowClipMode, ClipMode, ColorF, PrimitiveKeyKind}; +use api::PropertyBinding; use api::MAX_BLUR_RADIUS; use api::units::*; use crate::clip::{ClipItemKey, ClipItemKeyKind}; @@ -163,7 +164,7 @@ impl<'a> SceneBuilder<'a> { &LayoutPrimitiveInfo::with_clip_rect(final_prim_rect, prim_info.clip_rect), clips, PrimitiveKeyKind::Rectangle { - color: color.into(), + color: PropertyBinding::Value(color.into()), }, ); } else { @@ -189,7 +190,7 @@ impl<'a> SceneBuilder<'a> { // Draw the box-shadow as a solid rect, using a box-shadow // clip mask item. let prim = PrimitiveKeyKind::Rectangle { - color: color.into(), + color: PropertyBinding::Value(color.into()), }; // Create the box-shadow clip item. diff --git a/webrender/src/picture.rs b/webrender/src/picture.rs index b601793234..e48bb99663 100644 --- a/webrender/src/picture.rs +++ b/webrender/src/picture.rs @@ -96,7 +96,7 @@ use api::{MixBlendMode, PipelineId, PremultipliedColorF, FilterPrimitiveKind}; use api::{PropertyBinding, PropertyBindingId, FilterPrimitive, FontRenderMode}; -use api::{DebugFlags, RasterSpace, ImageKey, ColorF, PrimitiveFlags}; +use api::{DebugFlags, RasterSpace, ImageKey, ColorF, ColorU, PrimitiveFlags}; use api::units::*; use crate::box_shadow::{BLUR_SAMPLE_SCALE}; use crate::clip::{ClipStore, ClipChainInstance, ClipDataHandle, ClipChainId}; @@ -120,6 +120,7 @@ use crate::prim_store::{SpaceMapper, PrimitiveVisibilityMask, PointKey, Primitiv use crate::prim_store::{SpaceSnapper, PictureIndex, PrimitiveInstance, PrimitiveInstanceKind}; use crate::prim_store::{get_raster_rects, PrimitiveScratchBuffer, RectangleKey}; use crate::prim_store::{OpacityBindingStorage, ImageInstanceStorage, OpacityBindingIndex}; +use crate::prim_store::{ColorBindingStorage, ColorBindingIndex}; use crate::print_tree::{PrintTree, PrintTreePrinter}; use crate::render_backend::DataStores; use crate::render_task_graph::RenderTaskId; @@ -264,6 +265,8 @@ pub struct PictureCacheState { spatial_nodes: FastHashMap, /// State of opacity bindings from previous frame opacity_bindings: FastHashMap, + /// State of color bindings from previous frame + color_bindings: FastHashMap, /// The current transform of the picture cache root spatial node root_transform: TransformKey, /// The current tile size in device pixels @@ -278,6 +281,7 @@ pub struct PictureCacheState { pub struct PictureCacheRecycledAllocations { old_opacity_bindings: FastHashMap, + old_color_bindings: FastHashMap, compare_cache: FastHashMap, } @@ -401,33 +405,39 @@ fn clampf(value: f32, low: f32, high: f32) -> f32 { #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct PrimitiveDependencyIndex(pub u32); -/// Information about the state of an opacity binding. +/// Information about the state of a binding. #[derive(Debug)] -pub struct OpacityBindingInfo { +pub struct BindingInfo { /// The current value retrieved from dynamic scene properties. - value: f32, + value: T, /// True if it was changed (or is new) since the last frame build. changed: bool, } -/// Information stored in a tile descriptor for an opacity binding. +/// Information stored in a tile descriptor for a binding. #[derive(Debug, PartialEq, Clone, Copy)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] -pub enum OpacityBinding { - Value(f32), +pub enum Binding { + Value(T), Binding(PropertyBindingId), } -impl From> for OpacityBinding { - fn from(binding: PropertyBinding) -> OpacityBinding { +impl From> for Binding { + fn from(binding: PropertyBinding) -> Binding { match binding { - PropertyBinding::Binding(key, _) => OpacityBinding::Binding(key.id), - PropertyBinding::Value(value) => OpacityBinding::Value(value), + PropertyBinding::Binding(key, _) => Binding::Binding(key.id), + PropertyBinding::Value(value) => Binding::Value(value), } } } +pub type OpacityBinding = Binding; +pub type OpacityBindingInfo = BindingInfo; + +pub type ColorBinding = Binding; +pub type ColorBindingInfo = BindingInfo; + /// Information about the state of a spatial node value #[derive(Debug)] pub struct SpatialNodeDependency { @@ -476,6 +486,9 @@ struct TilePostUpdateContext<'a> { /// Information about opacity bindings from the picture cache. opacity_bindings: &'a FastHashMap, + /// Information about color bindings from the picture cache. + color_bindings: &'a FastHashMap, + /// Current size in device pixels of tiles for this cache current_tile_size: DeviceIntSize, @@ -515,6 +528,9 @@ struct PrimitiveDependencyInfo { /// Opacity bindings this primitive depends on. opacity_bindings: SmallVec<[OpacityBinding; 4]>, + /// Color binding this primitive depends on. + color_binding: Option, + /// Clips that this primitive depends on. clips: SmallVec<[ItemUid; 8]>, @@ -537,6 +553,7 @@ impl PrimitiveDependencyInfo { prim_origin, images: SmallVec::new(), opacity_bindings: SmallVec::new(), + color_binding: None, clip_by_tile: false, prim_clip_rect, clips: SmallVec::new(), @@ -688,6 +705,8 @@ pub enum PrimitiveCompareResult { Image, /// The value of an opacity binding changed OpacityBinding, + /// The value of a color binding changed + ColorBinding, } /// A more detailed version of PrimitiveCompareResult used when @@ -718,7 +737,11 @@ pub enum PrimitiveCompareResultDetail { /// The value of an opacity binding changed OpacityBinding { detail: CompareHelperResult, - } + }, + /// The value of a color binding changed + ColorBinding { + detail: CompareHelperResult, + }, } /// Debugging information about why a tile was invalidated @@ -886,6 +909,7 @@ impl Tile { state.resource_cache, ctx.spatial_nodes, ctx.opacity_bindings, + ctx.color_bindings, ); let mut dirty_rect = PictureRect::zero(); @@ -1054,6 +1078,12 @@ impl Tile { // Include any transforms that this primitive depends on. self.current_descriptor.transforms.extend_from_slice(&info.spatial_nodes); + // Include any color bindings this primitive depends on. + if info.color_binding.is_some() { + self.current_descriptor.color_bindings.insert( + self.current_descriptor.color_bindings.len(), info.color_binding.unwrap()); + } + // TODO(gw): The origin of background rects produced by APZ changes // in Gecko during scrolling. Consider investigating this so the // hack / workaround below is not required. @@ -1106,6 +1136,7 @@ impl Tile { clip_dep_count: info.clips.len() as u8, image_dep_count: info.images.len() as u8, opacity_binding_dep_count: info.opacity_bindings.len() as u8, + color_binding_dep_count: if info.color_binding.is_some() { 1 } else { 0 } as u8, }); // Add this primitive to the dirty rect quadtree. @@ -1317,6 +1348,7 @@ pub struct PrimitiveDescriptor { image_dep_count: u8, opacity_binding_dep_count: u8, clip_dep_count: u8, + color_binding_dep_count: u8, } impl PartialEq for PrimitiveDescriptor { @@ -1467,6 +1499,10 @@ pub struct TileDescriptor { /// Picture space rect that contains valid pixels region of this tile. local_valid_rect: PictureRect, + + /// List of the effects of color that we care about + /// tracking for this tile. + color_bindings: Vec, } impl TileDescriptor { @@ -1478,6 +1514,7 @@ impl TileDescriptor { images: Vec::new(), transforms: Vec::new(), local_valid_rect: PictureRect::zero(), + color_bindings: Vec::new(), } } @@ -1495,11 +1532,12 @@ impl TileDescriptor { prim.prim_clip_rect.w, prim.prim_clip_rect.h, )); - pt.add_item(format!("deps: t={} i={} o={} c={}", + pt.add_item(format!("deps: t={} i={} o={} c={} color={}", prim.transform_dep_count, prim.image_dep_count, prim.opacity_binding_dep_count, prim.clip_dep_count, + prim.color_binding_dep_count, )); pt.end_level(); } @@ -1542,6 +1580,15 @@ impl TileDescriptor { pt.end_level(); } + if !self.color_bindings.is_empty() { + pt.new_level("color_bindings".to_string()); + for color_binding in &self.color_bindings { + pt.new_level(format!("binding={:?}", color_binding)); + pt.end_level(); + } + pt.end_level(); + } + pt.end_level(); } @@ -1554,6 +1601,7 @@ impl TileDescriptor { self.images.clear(); self.transforms.clear(); self.local_valid_rect = PictureRect::zero(); + self.color_bindings.clear(); } } @@ -2026,6 +2074,11 @@ pub struct TileCacheInstance { /// calculate invalid relative transforms when building the spatial /// nodes hash above. used_spatial_nodes: FastHashSet, + /// List of color bindings, with some extra information + /// about whether they changed since last frame. + color_bindings: FastHashMap, + /// Switch back and forth between old and new bindings hashmaps to avoid re-allocating. + old_color_bindings: FastHashMap, /// The current dirty region tracker for this picture. pub dirty_region: DirtyRegion, /// Current size of tiles in picture units. @@ -2114,6 +2167,8 @@ impl TileCacheInstance { spatial_nodes: FastHashMap::default(), old_spatial_nodes: FastHashMap::default(), used_spatial_nodes: FastHashSet::default(), + color_bindings: FastHashMap::default(), + old_color_bindings: FastHashMap::default(), dirty_region: DirtyRegion::new(), tile_size: PictureSize::zero(), tile_rect: TileRect::zero(), @@ -2175,7 +2230,7 @@ impl TileCacheInstance { (p0, p1) } - /// Update transforms, opacity bindings and tile rects. + /// Update transforms, opacity, color bindings and tile rects. pub fn pre_update( &mut self, pic_rect: PictureRect, @@ -2257,6 +2312,7 @@ impl TileCacheInstance { self.root_transform = prev_state.root_transform; self.spatial_nodes = prev_state.spatial_nodes; self.opacity_bindings = prev_state.opacity_bindings; + self.color_bindings = prev_state.color_bindings; self.current_tile_size = prev_state.current_tile_size; self.native_surface_id = prev_state.native_surface_id; self.is_opaque = prev_state.is_opaque; @@ -2280,6 +2336,11 @@ impl TileCacheInstance { &mut self.old_opacity_bindings, prev_state.allocations.old_opacity_bindings, ); + recycle_map( + self.color_bindings.len(), + &mut self.old_color_bindings, + prev_state.allocations.old_color_bindings, + ); recycle_map( prev_state.allocations.compare_cache.len(), &mut self.compare_cache, @@ -2380,6 +2441,23 @@ impl TileCacheInstance { }); } + // Do a hacky diff of color binding values from the last frame. This is + // used later on during tile invalidation tests. + let current_properties = frame_context.scene_properties.color_properties(); + mem::swap(&mut self.color_bindings, &mut self.old_color_bindings); + + self.color_bindings.clear(); + for (id, value) in current_properties { + let changed = match self.old_color_bindings.get(id) { + Some(old_property) => old_property.value != (*value).into(), + None => true, + }; + self.color_bindings.insert(*id, ColorBindingInfo { + value: (*value).into(), + changed, + }); + } + let world_tile_size = WorldSize::new( self.current_tile_size.width as f32 / frame_context.global_device_pixel_scale.0, self.current_tile_size.height as f32 / frame_context.global_device_pixel_scale.0, @@ -2539,6 +2617,7 @@ impl TileCacheInstance { pictures: &[PicturePrimitive], resource_cache: &ResourceCache, opacity_binding_store: &OpacityBindingStorage, + color_bindings: &ColorBindingStorage, image_instances: &ImageInstanceStorage, surface_stack: &[SurfaceIndex], composite_state: &CompositeState, @@ -2675,13 +2754,15 @@ impl TileCacheInstance { prim_info.opacity_bindings.push(binding.into()); } } - PrimitiveInstanceKind::Rectangle { data_handle, opacity_binding_index, .. } => { + PrimitiveInstanceKind::Rectangle { data_handle, opacity_binding_index, color_binding_index, .. } => { if opacity_binding_index == OpacityBindingIndex::INVALID { // Rectangles can only form a backdrop candidate if they are known opaque. // TODO(gw): We could resolve the opacity binding here, but the common // case for background rects is that they don't have animated opacity. let color = match data_stores.prim[data_handle].kind { - PrimitiveTemplateKind::Rectangle { color, .. } => color, + PrimitiveTemplateKind::Rectangle { color, .. } => { + frame_context.scene_properties.resolve_color(&color) + } _ => unreachable!(), }; if color.a >= 1.0 { @@ -2694,6 +2775,10 @@ impl TileCacheInstance { } } + if color_binding_index != ColorBindingIndex::INVALID { + prim_info.color_binding = Some(color_bindings[color_binding_index].into()); + } + prim_info.clip_by_tile = true; } PrimitiveInstanceKind::Image { data_handle, image_instance_index, .. } => { @@ -3095,6 +3180,7 @@ impl TileCacheInstance { backdrop: self.backdrop, spatial_nodes: &self.spatial_nodes, opacity_bindings: &self.opacity_bindings, + color_bindings: &self.color_bindings, current_tile_size: self.current_tile_size, local_rect: self.local_rect, }; @@ -3978,12 +4064,14 @@ impl PicturePrimitive { tiles: tile_cache.tiles, spatial_nodes: tile_cache.spatial_nodes, opacity_bindings: tile_cache.opacity_bindings, + color_bindings: tile_cache.color_bindings, root_transform: tile_cache.root_transform, current_tile_size: tile_cache.current_tile_size, native_surface_id: tile_cache.native_surface_id.take(), is_opaque: tile_cache.is_opaque, allocations: PictureCacheRecycledAllocations { old_opacity_bindings: tile_cache.old_opacity_bindings, + old_color_bindings: tile_cache.old_color_bindings, compare_cache: tile_cache.compare_cache, }, }, @@ -5538,9 +5626,11 @@ struct PrimitiveComparer<'a> { transform_comparer: CompareHelper<'a, SpatialNodeIndex>, image_comparer: CompareHelper<'a, ImageDependency>, opacity_comparer: CompareHelper<'a, OpacityBinding>, + color_comparer: CompareHelper<'a, ColorBinding>, resource_cache: &'a ResourceCache, spatial_nodes: &'a FastHashMap, opacity_bindings: &'a FastHashMap, + color_bindings: &'a FastHashMap, } impl<'a> PrimitiveComparer<'a> { @@ -5550,6 +5640,7 @@ impl<'a> PrimitiveComparer<'a> { resource_cache: &'a ResourceCache, spatial_nodes: &'a FastHashMap, opacity_bindings: &'a FastHashMap, + color_bindings: &'a FastHashMap, ) -> Self { let clip_comparer = CompareHelper::new( &prev.clips, @@ -5571,14 +5662,21 @@ impl<'a> PrimitiveComparer<'a> { &curr.opacity_bindings, ); + let color_comparer = CompareHelper::new( + &prev.color_bindings, + &curr.color_bindings, + ); + PrimitiveComparer { clip_comparer, transform_comparer, image_comparer, opacity_comparer, + color_comparer, resource_cache, spatial_nodes, opacity_bindings, + color_bindings, } } @@ -5587,6 +5685,7 @@ impl<'a> PrimitiveComparer<'a> { self.transform_comparer.reset(); self.image_comparer.reset(); self.opacity_comparer.reset(); + self.color_comparer.reset(); } fn advance_prev(&mut self, prim: &PrimitiveDescriptor) { @@ -5594,6 +5693,7 @@ impl<'a> PrimitiveComparer<'a> { self.transform_comparer.advance_prev(prim.transform_dep_count); self.image_comparer.advance_prev(prim.image_dep_count); self.opacity_comparer.advance_prev(prim.opacity_binding_dep_count); + self.color_comparer.advance_prev(prim.color_binding_dep_count); } fn advance_curr(&mut self, prim: &PrimitiveDescriptor) { @@ -5601,6 +5701,7 @@ impl<'a> PrimitiveComparer<'a> { self.transform_comparer.advance_curr(prim.transform_dep_count); self.image_comparer.advance_curr(prim.image_dep_count); self.opacity_comparer.advance_curr(prim.opacity_binding_dep_count); + self.color_comparer.advance_curr(prim.color_binding_dep_count); } /// Check if two primitive descriptors are the same. @@ -5613,6 +5714,7 @@ impl<'a> PrimitiveComparer<'a> { let resource_cache = self.resource_cache; let spatial_nodes = self.spatial_nodes; let opacity_bindings = self.opacity_bindings; + let color_bindings = self.color_bindings; // Check equality of the PrimitiveDescriptor if prev != curr { @@ -5692,6 +5794,30 @@ impl<'a> PrimitiveComparer<'a> { return PrimitiveCompareResult::OpacityBinding; } + // Check if any of the color bindings this prim has are different. + let mut bind_result = CompareHelperResult::Equal; + if !self.color_comparer.is_same( + prev.color_binding_dep_count, + curr.color_binding_dep_count, + |curr| { + if let ColorBinding::Binding(id) = curr { + if color_bindings + .get(id) + .map_or(true, |info| info.changed) { + return true; + } + } + + true + }, + if opt_detail.is_some() { Some(&mut bind_result) } else { None }, + ) { + if let Some(detail) = opt_detail { + *detail = PrimitiveCompareResultDetail::ColorBinding{ detail: bind_result }; + } + return PrimitiveCompareResult::ColorBinding; + } + PrimitiveCompareResult::Equal } } diff --git a/webrender/src/prim_store/mod.rs b/webrender/src/prim_store/mod.rs index ed39fc39e2..b1bc2b280c 100644 --- a/webrender/src/prim_store/mod.rs +++ b/webrender/src/prim_store/mod.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::{BorderRadius, ClipMode, ColorF}; +use api::{BorderRadius, ClipMode, ColorF, ColorU}; use api::{ImageRendering, RepeatMode, PrimitiveFlags}; use api::{PremultipliedColorF, PropertyBinding, Shadow, GradientStop}; use api::{BoxShadowClipMode, LineStyle, LineOrientation, BorderStyle}; @@ -720,7 +720,7 @@ impl intern::InternDebug for PrimitiveKey {} #[derive(MallocSizeOf)] pub enum PrimitiveTemplateKind { Rectangle { - color: ColorF, + color: PropertyBinding, }, Clear, } @@ -812,7 +812,8 @@ impl PrimitiveTemplateKind { /// Write any GPU blocks for the primitive template to the given request object. fn write_prim_gpu_blocks( &self, - request: &mut GpuDataRequest + request: &mut GpuDataRequest, + scene_properties: &SceneProperties, ) { match *self { PrimitiveTemplateKind::Clear => { @@ -820,7 +821,7 @@ impl PrimitiveTemplateKind { request.push(PremultipliedColorF::BLACK); } PrimitiveTemplateKind::Rectangle { ref color, .. } => { - request.push(color.premultiplied()); + request.push(scene_properties.resolve_color(color).premultiplied()) } } } @@ -834,9 +835,10 @@ impl PrimitiveTemplate { pub fn update( &mut self, frame_state: &mut FrameBuildingState, + scene_properties: &SceneProperties, ) { if let Some(mut request) = frame_state.gpu_cache.request(&mut self.common.gpu_cache_handle) { - self.kind.write_prim_gpu_blocks(&mut request); + self.kind.write_prim_gpu_blocks(&mut request, scene_properties); } self.opacity = match self.kind { @@ -844,7 +846,7 @@ impl PrimitiveTemplate { PrimitiveOpacity::translucent() } PrimitiveTemplateKind::Rectangle { ref color, .. } => { - PrimitiveOpacity::from_alpha(color.a) + PrimitiveOpacity::from_alpha(scene_properties.resolve_color(color).a) } }; } @@ -873,7 +875,7 @@ impl InternablePrimitive for PrimitiveKeyKind { fn make_instance_kind( key: PrimitiveKey, data_handle: PrimitiveDataHandle, - _: &mut PrimitiveStore, + prim_store: &mut PrimitiveStore, _reference_frame_relative_offset: LayoutVector2D, ) -> PrimitiveInstanceKind { match key.kind { @@ -882,11 +884,18 @@ impl InternablePrimitive for PrimitiveKeyKind { data_handle } } - PrimitiveKeyKind::Rectangle { .. } => { + PrimitiveKeyKind::Rectangle { color, .. } => { + let color_binding_index = match color { + PropertyBinding::Binding(..) => { + prim_store.color_bindings.push(color) + } + PropertyBinding::Value(..) => ColorBindingIndex::INVALID, + }; PrimitiveInstanceKind::Rectangle { data_handle, opacity_binding_index: OpacityBindingIndex::INVALID, segment_instance_index: SegmentInstanceIndex::INVALID, + color_binding_index, } } } @@ -1326,7 +1335,10 @@ impl IsVisible for PrimitiveKeyKind { true } PrimitiveKeyKind::Rectangle { ref color, .. } => { - color.a > 0 + match *color { + PropertyBinding::Value(value) => value.a > 0, + PropertyBinding::Binding(..) => true, + } } } } @@ -1343,7 +1355,7 @@ impl CreateShadow for PrimitiveKeyKind { match *self { PrimitiveKeyKind::Rectangle { .. } => { PrimitiveKeyKind::Rectangle { - color: shadow.color.into(), + color: PropertyBinding::Value(shadow.color.into()), } } PrimitiveKeyKind::Clear => { @@ -1404,6 +1416,7 @@ pub enum PrimitiveInstanceKind { data_handle: PrimitiveDataHandle, opacity_binding_index: OpacityBindingIndex, segment_instance_index: SegmentInstanceIndex, + color_binding_index: ColorBindingIndex, }, YuvImage { /// Handle to the common interned data for this primitive. @@ -1660,6 +1673,8 @@ pub type TextRunIndex = storage::Index; pub type TextRunStorage = storage::Storage; pub type OpacityBindingIndex = storage::Index; pub type OpacityBindingStorage = storage::Storage; +pub type ColorBindingIndex = storage::Index>; +pub type ColorBindingStorage = storage::Storage>; pub type BorderHandleStorage = storage::Storage; pub type SegmentStorage = storage::Storage; pub type SegmentsRange = storage::Range; @@ -1799,6 +1814,7 @@ pub struct PrimitiveStoreStats { opacity_binding_count: usize, image_count: usize, linear_gradient_count: usize, + color_binding_count: usize, } impl PrimitiveStoreStats { @@ -1809,6 +1825,7 @@ impl PrimitiveStoreStats { opacity_binding_count: 0, image_count: 0, linear_gradient_count: 0, + color_binding_count: 0, } } } @@ -1826,6 +1843,8 @@ pub struct PrimitiveStore { /// List of animated opacity bindings for a primitive. pub opacity_bindings: OpacityBindingStorage, + /// animated color bindings for this primitive. + pub color_bindings: ColorBindingStorage, } impl PrimitiveStore { @@ -1835,6 +1854,7 @@ impl PrimitiveStore { text_runs: TextRunStorage::new(stats.text_run_count), images: ImageInstanceStorage::new(stats.image_count), opacity_bindings: OpacityBindingStorage::new(stats.opacity_binding_count), + color_bindings: ColorBindingStorage::new(stats.color_binding_count), linear_gradients: LinearGradientStorage::new(stats.linear_gradient_count), } } @@ -1846,6 +1866,7 @@ impl PrimitiveStore { image_count: self.images.len(), opacity_binding_count: self.opacity_bindings.len(), linear_gradient_count: self.linear_gradients.len(), + color_binding_count: self.color_bindings.len(), } } @@ -2150,6 +2171,7 @@ impl PrimitiveStore { &self.pictures, frame_state.resource_cache, &self.opacity_bindings, + &self.color_bindings, &self.images, &frame_state.surface_stack, &frame_state.composite_state, @@ -2975,7 +2997,7 @@ impl PrimitiveStore { // Update the template this instane references, which may refresh the GPU // cache with any shared template data. - prim_data.update(frame_state); + prim_data.update(frame_state, frame_context.scene_properties); } PrimitiveInstanceKind::NormalBorder { data_handle, ref mut cache_handles, .. } => { let prim_data = &mut data_stores.normal_border[*data_handle]; @@ -3065,13 +3087,37 @@ impl PrimitiveStore { // cache with any shared template data. prim_data.kind.update(&mut prim_data.common, frame_state); } - PrimitiveInstanceKind::Rectangle { data_handle, segment_instance_index, opacity_binding_index, .. } => { + PrimitiveInstanceKind::Rectangle { data_handle, segment_instance_index, opacity_binding_index, color_binding_index, .. } => { let prim_data = &mut data_stores.prim[*data_handle]; prim_data.common.may_need_repetition = false; + if *color_binding_index != ColorBindingIndex::INVALID { + match self.color_bindings[*color_binding_index] { + PropertyBinding::Binding(..) => { + // We explicitly invalidate the gpu cache + // if the color is animating. + let gpu_cache_handle = + if *segment_instance_index == SegmentInstanceIndex::INVALID { + None + } else if *segment_instance_index == SegmentInstanceIndex::UNUSED { + Some(&prim_data.common.gpu_cache_handle) + } else { + Some(&scratch.segment_instances[*segment_instance_index].gpu_cache_handle) + }; + if let Some(gpu_cache_handle) = gpu_cache_handle { + frame_state.gpu_cache.invalidate(gpu_cache_handle); + } + } + PropertyBinding::Value(..) => {}, + } + } + // Update the template this instane references, which may refresh the GPU // cache with any shared template data. - prim_data.update(frame_state); + prim_data.update( + frame_state, + frame_context.scene_properties, + ); update_opacity_binding( &mut self.opacity_bindings, @@ -3087,6 +3133,7 @@ impl PrimitiveStore { |request| { prim_data.kind.write_prim_gpu_blocks( request, + frame_context.scene_properties, ); } ); diff --git a/webrender/src/scene.rs b/webrender/src/scene.rs index f00cf5b7ca..0416ec8f68 100644 --- a/webrender/src/scene.rs +++ b/webrender/src/scene.rs @@ -22,6 +22,7 @@ use std::sync::Arc; pub struct SceneProperties { transform_properties: FastHashMap, float_properties: FastHashMap, + color_properties: FastHashMap, current_properties: DynamicProperties, pending_properties: Option, } @@ -31,6 +32,7 @@ impl SceneProperties { SceneProperties { transform_properties: FastHashMap::default(), float_properties: FastHashMap::default(), + color_properties: FastHashMap::default(), current_properties: DynamicProperties::default(), pending_properties: None, } @@ -78,6 +80,11 @@ impl SceneProperties { .insert(property.key.id, property.value); } + for property in &pending_properties.colors { + self.color_properties + .insert(property.key.id, property.value); + } + self.current_properties = pending_properties.clone(); properties_changed = true; } @@ -121,6 +128,27 @@ impl SceneProperties { pub fn float_properties(&self) -> &FastHashMap { &self.float_properties } + + /// Get the current value for a color property. + pub fn resolve_color( + &self, + property: &PropertyBinding + ) -> ColorF { + match *property { + PropertyBinding::Value(value) => value, + PropertyBinding::Binding(ref key, v) => { + self.color_properties + .get(&key.id) + .cloned() + .unwrap_or(v) + } + } + } + + pub fn color_properties(&self) -> &FastHashMap { + &self.color_properties + } + } /// A representation of the layout within the display port for a given document or iframe. diff --git a/webrender/src/scene_building.rs b/webrender/src/scene_building.rs index 2991c757ea..d94cb06bf7 100644 --- a/webrender/src/scene_building.rs +++ b/webrender/src/scene_building.rs @@ -1214,7 +1214,7 @@ impl<'a> SceneBuilder<'a> { self.add_solid_rectangle( clip_and_scroll, &layout, - ColorF::TRANSPARENT, + PropertyBinding::Value(ColorF::TRANSPARENT), ); } DisplayItem::ClearRectangle(ref info) => { @@ -2749,13 +2749,19 @@ impl<'a> SceneBuilder<'a> { &mut self, clip_and_scroll: ScrollNodeAndClipChain, info: &LayoutPrimitiveInfo, - color: ColorF, + color: PropertyBinding, ) { - if color.a == 0.0 { - // Don't add transparent rectangles to the draw list, but do consider them for hit - // testing. This allows specifying invisible hit testing areas. - self.add_primitive_to_hit_testing_list(info, clip_and_scroll); - return; + match color { + PropertyBinding::Value(value) => { + if value.a == 0.0 { + // Don't add transparent rectangles to the draw list, + // but do consider them for hit testing. This allows + // specifying invisible hit testing areas. + self.add_primitive_to_hit_testing_list(info, clip_and_scroll); + return; + } + }, + PropertyBinding::Binding(..) => {}, } self.add_primitive( diff --git a/webrender_api/src/api.rs b/webrender_api/src/api.rs index 302ab3d2e9..881b6f0613 100644 --- a/webrender_api/src/api.rs +++ b/webrender_api/src/api.rs @@ -1154,7 +1154,7 @@ pub enum PrimitiveKeyKind { /// Rectangle { /// - color: ColorU, + color: PropertyBinding, }, } @@ -1821,7 +1821,7 @@ impl PropertyBindingId { /// A unique key that is used for connecting animated property /// values to bindings in the display list. #[repr(C)] -#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)] pub struct PropertyBindingKey { /// pub id: PropertyBindingId, @@ -1853,7 +1853,7 @@ impl PropertyBindingKey { /// used for the case where the animation is still in-delay phase /// (i.e. the animation doesn't produce any animation values). #[repr(C)] -#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, PeekPoke)] +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)] pub enum PropertyBinding { /// Non-animated value. Value(T), @@ -1873,6 +1873,46 @@ impl From for PropertyBinding { } } +impl From> for PropertyBindingKey { + fn from(key: PropertyBindingKey) -> PropertyBindingKey { + PropertyBindingKey { + id: key.id.clone(), + _phantom: PhantomData, + } + } +} + +impl From> for PropertyBindingKey { + fn from(key: PropertyBindingKey) -> PropertyBindingKey { + PropertyBindingKey { + id: key.id.clone(), + _phantom: PhantomData, + } + } +} + +impl From> for PropertyBinding { + fn from(value: PropertyBinding) -> PropertyBinding { + match value { + PropertyBinding::Value(value) => PropertyBinding::Value(value.into()), + PropertyBinding::Binding(k, v) => { + PropertyBinding::Binding(k.into(), v.into()) + } + } + } +} + +impl From> for PropertyBinding { + fn from(value: PropertyBinding) -> PropertyBinding { + match value { + PropertyBinding::Value(value) => PropertyBinding::Value(value.into()), + PropertyBinding::Binding(k, v) => { + PropertyBinding::Binding(k.into(), v.into()) + } + } + } +} + /// The current value of an animated property. This is /// supplied by the calling code. #[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq)] @@ -1890,8 +1930,10 @@ pub struct PropertyValue { pub struct DynamicProperties { /// pub transforms: Vec>, - /// + /// opacity pub floats: Vec>, + /// background color + pub colors: Vec>, } /// A handler to integrate WebRender with the thread that contains the `Renderer`. diff --git a/webrender_api/src/display_item.rs b/webrender_api/src/display_item.rs index 3a3e1d9ad2..c87249df6d 100644 --- a/webrender_api/src/display_item.rs +++ b/webrender_api/src/display_item.rs @@ -299,11 +299,11 @@ pub struct ScrollFrameDisplayItem { pub external_scroll_offset: LayoutVector2D, } -/// A solid color to draw (may not actually be a rectangle due to complex clips) +/// A solid or an animating color to draw (may not actually be a rectangle due to complex clips) #[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] pub struct RectangleDisplayItem { pub common: CommonItemProperties, - pub color: ColorF, + pub color: PropertyBinding, } /// Clears all colors from the area, making it possible to cut holes in the window. diff --git a/webrender_api/src/display_list.rs b/webrender_api/src/display_list.rs index 8d0a27d0e5..2f7d3f1aff 100644 --- a/webrender_api/src/display_list.rs +++ b/webrender_api/src/display_list.rs @@ -1116,7 +1116,19 @@ impl DisplayListBuilder { ) { let item = di::DisplayItem::Rectangle(di::RectangleDisplayItem { common: *common, - color + color: PropertyBinding::Value(color), + }); + self.push_item(&item); + } + + pub fn push_rect_with_animation( + &mut self, + common: &di::CommonItemProperties, + color: PropertyBinding, + ) { + let item = di::DisplayItem::Rectangle(di::RectangleDisplayItem { + common: *common, + color, }); self.push_item(&item); } diff --git a/wrench/src/scene.rs b/wrench/src/scene.rs index d2e708a9b3..818d000edc 100644 --- a/wrench/src/scene.rs +++ b/wrench/src/scene.rs @@ -14,6 +14,7 @@ use webrender::api::units::{LayoutSize, LayoutTransform}; pub struct SceneProperties { transform_properties: HashMap, float_properties: HashMap, + color_properties: HashMap, } impl SceneProperties { @@ -21,6 +22,7 @@ impl SceneProperties { pub fn set_properties(&mut self, properties: &DynamicProperties) { self.transform_properties.clear(); self.float_properties.clear(); + self.color_properties.clear(); for property in &properties.transforms { self.transform_properties @@ -31,6 +33,11 @@ impl SceneProperties { self.float_properties .insert(property.key.id, property.value); } + + for property in &properties.colors { + self.color_properties + .insert(property.key.id, property.value); + } } /// Get the current value for a transform property. @@ -57,6 +64,17 @@ impl SceneProperties { .unwrap_or(v), } } + + /// Get the current value for a color property. + pub fn resolve_color(&self, property: &PropertyBinding) -> ColorF { + match *property { + PropertyBinding::Value(value) => value, + PropertyBinding::Binding(ref key, v) => self.color_properties + .get(&key.id) + .cloned() + .unwrap_or(v), + } + } } /// A representation of the layout within the display port for a given document or iframe. diff --git a/wrench/src/yaml_frame_writer.rs b/wrench/src/yaml_frame_writer.rs index c7b64055f5..6a1da76809 100644 --- a/wrench/src/yaml_frame_writer.rs +++ b/wrench/src/yaml_frame_writer.rs @@ -1002,7 +1002,13 @@ impl YamlFrameWriter { DisplayItem::Rectangle(item) => { str_node(&mut v, "type", "rect"); common_node(&mut v, clip_id_mapper, &item.common); - color_node(&mut v, "color", item.color); + + let key_label = match item.color { + PropertyBinding::Value(..) => "color", + PropertyBinding::Binding(..) => "animating-color", + }; + color_node(&mut v, key_label, + scene.properties.resolve_color(&item.color)); } DisplayItem::HitTest(item) => { str_node(&mut v, "type", "hit-test"); From b0e05109015ab896d5d8c9f97270cbea3186d677 Mon Sep 17 00:00:00 2001 From: Bogdan Tara Date: Thu, 27 Feb 2020 09:57:32 +0000 Subject: [PATCH 05/33] Backed out 6 changesets (bug 1510030) for webrender bustages CLOSED TREE Backed out changeset 28a2fba71977 (bug 1510030) Backed out changeset 1700b3a416cd (bug 1510030) Backed out changeset 087518046b0c (bug 1510030) Backed out changeset 6b3af91a7ce6 (bug 1510030) Backed out changeset 737f4bc3afd3 (bug 1510030) Backed out changeset 921116ca67a5 (bug 1510030) [ghsync] From https://hg.mozilla.org/mozilla-central/rev/7fc17499b72099c0fa5d27f76c619229c01f2516 --- webrender/src/box_shadow.rs | 5 +- webrender/src/picture.rs | 158 +++--------------------------- webrender/src/prim_store/mod.rs | 85 ++++------------ webrender/src/scene.rs | 28 ------ webrender/src/scene_building.rs | 20 ++-- webrender_api/src/api.rs | 50 +--------- webrender_api/src/display_item.rs | 4 +- webrender_api/src/display_list.rs | 14 +-- wrench/src/scene.rs | 18 ---- wrench/src/yaml_frame_writer.rs | 8 +- 10 files changed, 54 insertions(+), 336 deletions(-) diff --git a/webrender/src/box_shadow.rs b/webrender/src/box_shadow.rs index eb8d4d0d4e..124d44d5de 100644 --- a/webrender/src/box_shadow.rs +++ b/webrender/src/box_shadow.rs @@ -3,7 +3,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use api::{BorderRadius, BoxShadowClipMode, ClipMode, ColorF, PrimitiveKeyKind}; -use api::PropertyBinding; use api::MAX_BLUR_RADIUS; use api::units::*; use crate::clip::{ClipItemKey, ClipItemKeyKind}; @@ -164,7 +163,7 @@ impl<'a> SceneBuilder<'a> { &LayoutPrimitiveInfo::with_clip_rect(final_prim_rect, prim_info.clip_rect), clips, PrimitiveKeyKind::Rectangle { - color: PropertyBinding::Value(color.into()), + color: color.into(), }, ); } else { @@ -190,7 +189,7 @@ impl<'a> SceneBuilder<'a> { // Draw the box-shadow as a solid rect, using a box-shadow // clip mask item. let prim = PrimitiveKeyKind::Rectangle { - color: PropertyBinding::Value(color.into()), + color: color.into(), }; // Create the box-shadow clip item. diff --git a/webrender/src/picture.rs b/webrender/src/picture.rs index e48bb99663..b601793234 100644 --- a/webrender/src/picture.rs +++ b/webrender/src/picture.rs @@ -96,7 +96,7 @@ use api::{MixBlendMode, PipelineId, PremultipliedColorF, FilterPrimitiveKind}; use api::{PropertyBinding, PropertyBindingId, FilterPrimitive, FontRenderMode}; -use api::{DebugFlags, RasterSpace, ImageKey, ColorF, ColorU, PrimitiveFlags}; +use api::{DebugFlags, RasterSpace, ImageKey, ColorF, PrimitiveFlags}; use api::units::*; use crate::box_shadow::{BLUR_SAMPLE_SCALE}; use crate::clip::{ClipStore, ClipChainInstance, ClipDataHandle, ClipChainId}; @@ -120,7 +120,6 @@ use crate::prim_store::{SpaceMapper, PrimitiveVisibilityMask, PointKey, Primitiv use crate::prim_store::{SpaceSnapper, PictureIndex, PrimitiveInstance, PrimitiveInstanceKind}; use crate::prim_store::{get_raster_rects, PrimitiveScratchBuffer, RectangleKey}; use crate::prim_store::{OpacityBindingStorage, ImageInstanceStorage, OpacityBindingIndex}; -use crate::prim_store::{ColorBindingStorage, ColorBindingIndex}; use crate::print_tree::{PrintTree, PrintTreePrinter}; use crate::render_backend::DataStores; use crate::render_task_graph::RenderTaskId; @@ -265,8 +264,6 @@ pub struct PictureCacheState { spatial_nodes: FastHashMap, /// State of opacity bindings from previous frame opacity_bindings: FastHashMap, - /// State of color bindings from previous frame - color_bindings: FastHashMap, /// The current transform of the picture cache root spatial node root_transform: TransformKey, /// The current tile size in device pixels @@ -281,7 +278,6 @@ pub struct PictureCacheState { pub struct PictureCacheRecycledAllocations { old_opacity_bindings: FastHashMap, - old_color_bindings: FastHashMap, compare_cache: FastHashMap, } @@ -405,39 +401,33 @@ fn clampf(value: f32, low: f32, high: f32) -> f32 { #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct PrimitiveDependencyIndex(pub u32); -/// Information about the state of a binding. +/// Information about the state of an opacity binding. #[derive(Debug)] -pub struct BindingInfo { +pub struct OpacityBindingInfo { /// The current value retrieved from dynamic scene properties. - value: T, + value: f32, /// True if it was changed (or is new) since the last frame build. changed: bool, } -/// Information stored in a tile descriptor for a binding. +/// Information stored in a tile descriptor for an opacity binding. #[derive(Debug, PartialEq, Clone, Copy)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] -pub enum Binding { - Value(T), +pub enum OpacityBinding { + Value(f32), Binding(PropertyBindingId), } -impl From> for Binding { - fn from(binding: PropertyBinding) -> Binding { +impl From> for OpacityBinding { + fn from(binding: PropertyBinding) -> OpacityBinding { match binding { - PropertyBinding::Binding(key, _) => Binding::Binding(key.id), - PropertyBinding::Value(value) => Binding::Value(value), + PropertyBinding::Binding(key, _) => OpacityBinding::Binding(key.id), + PropertyBinding::Value(value) => OpacityBinding::Value(value), } } } -pub type OpacityBinding = Binding; -pub type OpacityBindingInfo = BindingInfo; - -pub type ColorBinding = Binding; -pub type ColorBindingInfo = BindingInfo; - /// Information about the state of a spatial node value #[derive(Debug)] pub struct SpatialNodeDependency { @@ -486,9 +476,6 @@ struct TilePostUpdateContext<'a> { /// Information about opacity bindings from the picture cache. opacity_bindings: &'a FastHashMap, - /// Information about color bindings from the picture cache. - color_bindings: &'a FastHashMap, - /// Current size in device pixels of tiles for this cache current_tile_size: DeviceIntSize, @@ -528,9 +515,6 @@ struct PrimitiveDependencyInfo { /// Opacity bindings this primitive depends on. opacity_bindings: SmallVec<[OpacityBinding; 4]>, - /// Color binding this primitive depends on. - color_binding: Option, - /// Clips that this primitive depends on. clips: SmallVec<[ItemUid; 8]>, @@ -553,7 +537,6 @@ impl PrimitiveDependencyInfo { prim_origin, images: SmallVec::new(), opacity_bindings: SmallVec::new(), - color_binding: None, clip_by_tile: false, prim_clip_rect, clips: SmallVec::new(), @@ -705,8 +688,6 @@ pub enum PrimitiveCompareResult { Image, /// The value of an opacity binding changed OpacityBinding, - /// The value of a color binding changed - ColorBinding, } /// A more detailed version of PrimitiveCompareResult used when @@ -737,11 +718,7 @@ pub enum PrimitiveCompareResultDetail { /// The value of an opacity binding changed OpacityBinding { detail: CompareHelperResult, - }, - /// The value of a color binding changed - ColorBinding { - detail: CompareHelperResult, - }, + } } /// Debugging information about why a tile was invalidated @@ -909,7 +886,6 @@ impl Tile { state.resource_cache, ctx.spatial_nodes, ctx.opacity_bindings, - ctx.color_bindings, ); let mut dirty_rect = PictureRect::zero(); @@ -1078,12 +1054,6 @@ impl Tile { // Include any transforms that this primitive depends on. self.current_descriptor.transforms.extend_from_slice(&info.spatial_nodes); - // Include any color bindings this primitive depends on. - if info.color_binding.is_some() { - self.current_descriptor.color_bindings.insert( - self.current_descriptor.color_bindings.len(), info.color_binding.unwrap()); - } - // TODO(gw): The origin of background rects produced by APZ changes // in Gecko during scrolling. Consider investigating this so the // hack / workaround below is not required. @@ -1136,7 +1106,6 @@ impl Tile { clip_dep_count: info.clips.len() as u8, image_dep_count: info.images.len() as u8, opacity_binding_dep_count: info.opacity_bindings.len() as u8, - color_binding_dep_count: if info.color_binding.is_some() { 1 } else { 0 } as u8, }); // Add this primitive to the dirty rect quadtree. @@ -1348,7 +1317,6 @@ pub struct PrimitiveDescriptor { image_dep_count: u8, opacity_binding_dep_count: u8, clip_dep_count: u8, - color_binding_dep_count: u8, } impl PartialEq for PrimitiveDescriptor { @@ -1499,10 +1467,6 @@ pub struct TileDescriptor { /// Picture space rect that contains valid pixels region of this tile. local_valid_rect: PictureRect, - - /// List of the effects of color that we care about - /// tracking for this tile. - color_bindings: Vec, } impl TileDescriptor { @@ -1514,7 +1478,6 @@ impl TileDescriptor { images: Vec::new(), transforms: Vec::new(), local_valid_rect: PictureRect::zero(), - color_bindings: Vec::new(), } } @@ -1532,12 +1495,11 @@ impl TileDescriptor { prim.prim_clip_rect.w, prim.prim_clip_rect.h, )); - pt.add_item(format!("deps: t={} i={} o={} c={} color={}", + pt.add_item(format!("deps: t={} i={} o={} c={}", prim.transform_dep_count, prim.image_dep_count, prim.opacity_binding_dep_count, prim.clip_dep_count, - prim.color_binding_dep_count, )); pt.end_level(); } @@ -1580,15 +1542,6 @@ impl TileDescriptor { pt.end_level(); } - if !self.color_bindings.is_empty() { - pt.new_level("color_bindings".to_string()); - for color_binding in &self.color_bindings { - pt.new_level(format!("binding={:?}", color_binding)); - pt.end_level(); - } - pt.end_level(); - } - pt.end_level(); } @@ -1601,7 +1554,6 @@ impl TileDescriptor { self.images.clear(); self.transforms.clear(); self.local_valid_rect = PictureRect::zero(); - self.color_bindings.clear(); } } @@ -2074,11 +2026,6 @@ pub struct TileCacheInstance { /// calculate invalid relative transforms when building the spatial /// nodes hash above. used_spatial_nodes: FastHashSet, - /// List of color bindings, with some extra information - /// about whether they changed since last frame. - color_bindings: FastHashMap, - /// Switch back and forth between old and new bindings hashmaps to avoid re-allocating. - old_color_bindings: FastHashMap, /// The current dirty region tracker for this picture. pub dirty_region: DirtyRegion, /// Current size of tiles in picture units. @@ -2167,8 +2114,6 @@ impl TileCacheInstance { spatial_nodes: FastHashMap::default(), old_spatial_nodes: FastHashMap::default(), used_spatial_nodes: FastHashSet::default(), - color_bindings: FastHashMap::default(), - old_color_bindings: FastHashMap::default(), dirty_region: DirtyRegion::new(), tile_size: PictureSize::zero(), tile_rect: TileRect::zero(), @@ -2230,7 +2175,7 @@ impl TileCacheInstance { (p0, p1) } - /// Update transforms, opacity, color bindings and tile rects. + /// Update transforms, opacity bindings and tile rects. pub fn pre_update( &mut self, pic_rect: PictureRect, @@ -2312,7 +2257,6 @@ impl TileCacheInstance { self.root_transform = prev_state.root_transform; self.spatial_nodes = prev_state.spatial_nodes; self.opacity_bindings = prev_state.opacity_bindings; - self.color_bindings = prev_state.color_bindings; self.current_tile_size = prev_state.current_tile_size; self.native_surface_id = prev_state.native_surface_id; self.is_opaque = prev_state.is_opaque; @@ -2336,11 +2280,6 @@ impl TileCacheInstance { &mut self.old_opacity_bindings, prev_state.allocations.old_opacity_bindings, ); - recycle_map( - self.color_bindings.len(), - &mut self.old_color_bindings, - prev_state.allocations.old_color_bindings, - ); recycle_map( prev_state.allocations.compare_cache.len(), &mut self.compare_cache, @@ -2441,23 +2380,6 @@ impl TileCacheInstance { }); } - // Do a hacky diff of color binding values from the last frame. This is - // used later on during tile invalidation tests. - let current_properties = frame_context.scene_properties.color_properties(); - mem::swap(&mut self.color_bindings, &mut self.old_color_bindings); - - self.color_bindings.clear(); - for (id, value) in current_properties { - let changed = match self.old_color_bindings.get(id) { - Some(old_property) => old_property.value != (*value).into(), - None => true, - }; - self.color_bindings.insert(*id, ColorBindingInfo { - value: (*value).into(), - changed, - }); - } - let world_tile_size = WorldSize::new( self.current_tile_size.width as f32 / frame_context.global_device_pixel_scale.0, self.current_tile_size.height as f32 / frame_context.global_device_pixel_scale.0, @@ -2617,7 +2539,6 @@ impl TileCacheInstance { pictures: &[PicturePrimitive], resource_cache: &ResourceCache, opacity_binding_store: &OpacityBindingStorage, - color_bindings: &ColorBindingStorage, image_instances: &ImageInstanceStorage, surface_stack: &[SurfaceIndex], composite_state: &CompositeState, @@ -2754,15 +2675,13 @@ impl TileCacheInstance { prim_info.opacity_bindings.push(binding.into()); } } - PrimitiveInstanceKind::Rectangle { data_handle, opacity_binding_index, color_binding_index, .. } => { + PrimitiveInstanceKind::Rectangle { data_handle, opacity_binding_index, .. } => { if opacity_binding_index == OpacityBindingIndex::INVALID { // Rectangles can only form a backdrop candidate if they are known opaque. // TODO(gw): We could resolve the opacity binding here, but the common // case for background rects is that they don't have animated opacity. let color = match data_stores.prim[data_handle].kind { - PrimitiveTemplateKind::Rectangle { color, .. } => { - frame_context.scene_properties.resolve_color(&color) - } + PrimitiveTemplateKind::Rectangle { color, .. } => color, _ => unreachable!(), }; if color.a >= 1.0 { @@ -2775,10 +2694,6 @@ impl TileCacheInstance { } } - if color_binding_index != ColorBindingIndex::INVALID { - prim_info.color_binding = Some(color_bindings[color_binding_index].into()); - } - prim_info.clip_by_tile = true; } PrimitiveInstanceKind::Image { data_handle, image_instance_index, .. } => { @@ -3180,7 +3095,6 @@ impl TileCacheInstance { backdrop: self.backdrop, spatial_nodes: &self.spatial_nodes, opacity_bindings: &self.opacity_bindings, - color_bindings: &self.color_bindings, current_tile_size: self.current_tile_size, local_rect: self.local_rect, }; @@ -4064,14 +3978,12 @@ impl PicturePrimitive { tiles: tile_cache.tiles, spatial_nodes: tile_cache.spatial_nodes, opacity_bindings: tile_cache.opacity_bindings, - color_bindings: tile_cache.color_bindings, root_transform: tile_cache.root_transform, current_tile_size: tile_cache.current_tile_size, native_surface_id: tile_cache.native_surface_id.take(), is_opaque: tile_cache.is_opaque, allocations: PictureCacheRecycledAllocations { old_opacity_bindings: tile_cache.old_opacity_bindings, - old_color_bindings: tile_cache.old_color_bindings, compare_cache: tile_cache.compare_cache, }, }, @@ -5626,11 +5538,9 @@ struct PrimitiveComparer<'a> { transform_comparer: CompareHelper<'a, SpatialNodeIndex>, image_comparer: CompareHelper<'a, ImageDependency>, opacity_comparer: CompareHelper<'a, OpacityBinding>, - color_comparer: CompareHelper<'a, ColorBinding>, resource_cache: &'a ResourceCache, spatial_nodes: &'a FastHashMap, opacity_bindings: &'a FastHashMap, - color_bindings: &'a FastHashMap, } impl<'a> PrimitiveComparer<'a> { @@ -5640,7 +5550,6 @@ impl<'a> PrimitiveComparer<'a> { resource_cache: &'a ResourceCache, spatial_nodes: &'a FastHashMap, opacity_bindings: &'a FastHashMap, - color_bindings: &'a FastHashMap, ) -> Self { let clip_comparer = CompareHelper::new( &prev.clips, @@ -5662,21 +5571,14 @@ impl<'a> PrimitiveComparer<'a> { &curr.opacity_bindings, ); - let color_comparer = CompareHelper::new( - &prev.color_bindings, - &curr.color_bindings, - ); - PrimitiveComparer { clip_comparer, transform_comparer, image_comparer, opacity_comparer, - color_comparer, resource_cache, spatial_nodes, opacity_bindings, - color_bindings, } } @@ -5685,7 +5587,6 @@ impl<'a> PrimitiveComparer<'a> { self.transform_comparer.reset(); self.image_comparer.reset(); self.opacity_comparer.reset(); - self.color_comparer.reset(); } fn advance_prev(&mut self, prim: &PrimitiveDescriptor) { @@ -5693,7 +5594,6 @@ impl<'a> PrimitiveComparer<'a> { self.transform_comparer.advance_prev(prim.transform_dep_count); self.image_comparer.advance_prev(prim.image_dep_count); self.opacity_comparer.advance_prev(prim.opacity_binding_dep_count); - self.color_comparer.advance_prev(prim.color_binding_dep_count); } fn advance_curr(&mut self, prim: &PrimitiveDescriptor) { @@ -5701,7 +5601,6 @@ impl<'a> PrimitiveComparer<'a> { self.transform_comparer.advance_curr(prim.transform_dep_count); self.image_comparer.advance_curr(prim.image_dep_count); self.opacity_comparer.advance_curr(prim.opacity_binding_dep_count); - self.color_comparer.advance_curr(prim.color_binding_dep_count); } /// Check if two primitive descriptors are the same. @@ -5714,7 +5613,6 @@ impl<'a> PrimitiveComparer<'a> { let resource_cache = self.resource_cache; let spatial_nodes = self.spatial_nodes; let opacity_bindings = self.opacity_bindings; - let color_bindings = self.color_bindings; // Check equality of the PrimitiveDescriptor if prev != curr { @@ -5794,30 +5692,6 @@ impl<'a> PrimitiveComparer<'a> { return PrimitiveCompareResult::OpacityBinding; } - // Check if any of the color bindings this prim has are different. - let mut bind_result = CompareHelperResult::Equal; - if !self.color_comparer.is_same( - prev.color_binding_dep_count, - curr.color_binding_dep_count, - |curr| { - if let ColorBinding::Binding(id) = curr { - if color_bindings - .get(id) - .map_or(true, |info| info.changed) { - return true; - } - } - - true - }, - if opt_detail.is_some() { Some(&mut bind_result) } else { None }, - ) { - if let Some(detail) = opt_detail { - *detail = PrimitiveCompareResultDetail::ColorBinding{ detail: bind_result }; - } - return PrimitiveCompareResult::ColorBinding; - } - PrimitiveCompareResult::Equal } } diff --git a/webrender/src/prim_store/mod.rs b/webrender/src/prim_store/mod.rs index b1bc2b280c..9626d5413f 100644 --- a/webrender/src/prim_store/mod.rs +++ b/webrender/src/prim_store/mod.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::{BorderRadius, ClipMode, ColorF, ColorU}; +use api::{BorderRadius, ClipMode, ColorF}; use api::{ImageRendering, RepeatMode, PrimitiveFlags}; use api::{PremultipliedColorF, PropertyBinding, Shadow, GradientStop}; use api::{BoxShadowClipMode, LineStyle, LineOrientation, BorderStyle}; @@ -720,7 +720,7 @@ impl intern::InternDebug for PrimitiveKey {} #[derive(MallocSizeOf)] pub enum PrimitiveTemplateKind { Rectangle { - color: PropertyBinding, + color: ColorF, }, Clear, } @@ -812,8 +812,7 @@ impl PrimitiveTemplateKind { /// Write any GPU blocks for the primitive template to the given request object. fn write_prim_gpu_blocks( &self, - request: &mut GpuDataRequest, - scene_properties: &SceneProperties, + request: &mut GpuDataRequest ) { match *self { PrimitiveTemplateKind::Clear => { @@ -821,7 +820,7 @@ impl PrimitiveTemplateKind { request.push(PremultipliedColorF::BLACK); } PrimitiveTemplateKind::Rectangle { ref color, .. } => { - request.push(scene_properties.resolve_color(color).premultiplied()) + request.push(color.premultiplied()); } } } @@ -835,10 +834,9 @@ impl PrimitiveTemplate { pub fn update( &mut self, frame_state: &mut FrameBuildingState, - scene_properties: &SceneProperties, ) { if let Some(mut request) = frame_state.gpu_cache.request(&mut self.common.gpu_cache_handle) { - self.kind.write_prim_gpu_blocks(&mut request, scene_properties); + self.kind.write_prim_gpu_blocks(&mut request); } self.opacity = match self.kind { @@ -846,7 +844,7 @@ impl PrimitiveTemplate { PrimitiveOpacity::translucent() } PrimitiveTemplateKind::Rectangle { ref color, .. } => { - PrimitiveOpacity::from_alpha(scene_properties.resolve_color(color).a) + PrimitiveOpacity::from_alpha(color.a) } }; } @@ -875,7 +873,7 @@ impl InternablePrimitive for PrimitiveKeyKind { fn make_instance_kind( key: PrimitiveKey, data_handle: PrimitiveDataHandle, - prim_store: &mut PrimitiveStore, + _: &mut PrimitiveStore, _reference_frame_relative_offset: LayoutVector2D, ) -> PrimitiveInstanceKind { match key.kind { @@ -884,18 +882,11 @@ impl InternablePrimitive for PrimitiveKeyKind { data_handle } } - PrimitiveKeyKind::Rectangle { color, .. } => { - let color_binding_index = match color { - PropertyBinding::Binding(..) => { - prim_store.color_bindings.push(color) - } - PropertyBinding::Value(..) => ColorBindingIndex::INVALID, - }; + PrimitiveKeyKind::Rectangle { .. } => { PrimitiveInstanceKind::Rectangle { data_handle, opacity_binding_index: OpacityBindingIndex::INVALID, segment_instance_index: SegmentInstanceIndex::INVALID, - color_binding_index, } } } @@ -927,7 +918,8 @@ impl OpacityBinding { } // Resolve the current value of each opacity binding, and - // store that as a single combined opacity. + // store that as a single combined opacity. Returns true + // if the opacity value changed from last time. pub fn update(&mut self, scene_properties: &SceneProperties) { let mut new_opacity = 1.0; @@ -1335,10 +1327,7 @@ impl IsVisible for PrimitiveKeyKind { true } PrimitiveKeyKind::Rectangle { ref color, .. } => { - match *color { - PropertyBinding::Value(value) => value.a > 0, - PropertyBinding::Binding(..) => true, - } + color.a > 0 } } } @@ -1355,7 +1344,7 @@ impl CreateShadow for PrimitiveKeyKind { match *self { PrimitiveKeyKind::Rectangle { .. } => { PrimitiveKeyKind::Rectangle { - color: PropertyBinding::Value(shadow.color.into()), + color: shadow.color.into(), } } PrimitiveKeyKind::Clear => { @@ -1416,7 +1405,6 @@ pub enum PrimitiveInstanceKind { data_handle: PrimitiveDataHandle, opacity_binding_index: OpacityBindingIndex, segment_instance_index: SegmentInstanceIndex, - color_binding_index: ColorBindingIndex, }, YuvImage { /// Handle to the common interned data for this primitive. @@ -1673,8 +1661,6 @@ pub type TextRunIndex = storage::Index; pub type TextRunStorage = storage::Storage; pub type OpacityBindingIndex = storage::Index; pub type OpacityBindingStorage = storage::Storage; -pub type ColorBindingIndex = storage::Index>; -pub type ColorBindingStorage = storage::Storage>; pub type BorderHandleStorage = storage::Storage; pub type SegmentStorage = storage::Storage; pub type SegmentsRange = storage::Range; @@ -1814,7 +1800,6 @@ pub struct PrimitiveStoreStats { opacity_binding_count: usize, image_count: usize, linear_gradient_count: usize, - color_binding_count: usize, } impl PrimitiveStoreStats { @@ -1825,7 +1810,6 @@ impl PrimitiveStoreStats { opacity_binding_count: 0, image_count: 0, linear_gradient_count: 0, - color_binding_count: 0, } } } @@ -1843,8 +1827,6 @@ pub struct PrimitiveStore { /// List of animated opacity bindings for a primitive. pub opacity_bindings: OpacityBindingStorage, - /// animated color bindings for this primitive. - pub color_bindings: ColorBindingStorage, } impl PrimitiveStore { @@ -1854,7 +1836,6 @@ impl PrimitiveStore { text_runs: TextRunStorage::new(stats.text_run_count), images: ImageInstanceStorage::new(stats.image_count), opacity_bindings: OpacityBindingStorage::new(stats.opacity_binding_count), - color_bindings: ColorBindingStorage::new(stats.color_binding_count), linear_gradients: LinearGradientStorage::new(stats.linear_gradient_count), } } @@ -1866,7 +1847,6 @@ impl PrimitiveStore { image_count: self.images.len(), opacity_binding_count: self.opacity_bindings.len(), linear_gradient_count: self.linear_gradients.len(), - color_binding_count: self.color_bindings.len(), } } @@ -2171,7 +2151,6 @@ impl PrimitiveStore { &self.pictures, frame_state.resource_cache, &self.opacity_bindings, - &self.color_bindings, &self.images, &frame_state.surface_stack, &frame_state.composite_state, @@ -2678,7 +2657,7 @@ impl PrimitiveStore { self.pictures[pic_index.0].requested_composite_mode = None; } - fn prepare_prim_for_render( + pub fn prepare_prim_for_render( &mut self, prim_instance: &mut PrimitiveInstance, prim_spatial_node_index: SpatialNodeIndex, @@ -2997,7 +2976,7 @@ impl PrimitiveStore { // Update the template this instane references, which may refresh the GPU // cache with any shared template data. - prim_data.update(frame_state, frame_context.scene_properties); + prim_data.update(frame_state); } PrimitiveInstanceKind::NormalBorder { data_handle, ref mut cache_handles, .. } => { let prim_data = &mut data_stores.normal_border[*data_handle]; @@ -3087,37 +3066,13 @@ impl PrimitiveStore { // cache with any shared template data. prim_data.kind.update(&mut prim_data.common, frame_state); } - PrimitiveInstanceKind::Rectangle { data_handle, segment_instance_index, opacity_binding_index, color_binding_index, .. } => { + PrimitiveInstanceKind::Rectangle { data_handle, segment_instance_index, opacity_binding_index, .. } => { let prim_data = &mut data_stores.prim[*data_handle]; prim_data.common.may_need_repetition = false; - if *color_binding_index != ColorBindingIndex::INVALID { - match self.color_bindings[*color_binding_index] { - PropertyBinding::Binding(..) => { - // We explicitly invalidate the gpu cache - // if the color is animating. - let gpu_cache_handle = - if *segment_instance_index == SegmentInstanceIndex::INVALID { - None - } else if *segment_instance_index == SegmentInstanceIndex::UNUSED { - Some(&prim_data.common.gpu_cache_handle) - } else { - Some(&scratch.segment_instances[*segment_instance_index].gpu_cache_handle) - }; - if let Some(gpu_cache_handle) = gpu_cache_handle { - frame_state.gpu_cache.invalidate(gpu_cache_handle); - } - } - PropertyBinding::Value(..) => {}, - } - } - // Update the template this instane references, which may refresh the GPU // cache with any shared template data. - prim_data.update( - frame_state, - frame_context.scene_properties, - ); + prim_data.update(frame_state); update_opacity_binding( &mut self.opacity_bindings, @@ -3133,7 +3088,6 @@ impl PrimitiveStore { |request| { prim_data.kind.write_prim_gpu_blocks( request, - frame_context.scene_properties, ); } ); @@ -4352,10 +4306,13 @@ fn update_opacity_binding( opacity_bindings: &mut OpacityBindingStorage, opacity_binding_index: OpacityBindingIndex, scene_properties: &SceneProperties, -) { - if opacity_binding_index != OpacityBindingIndex::INVALID { +) -> f32 { + if opacity_binding_index == OpacityBindingIndex::INVALID { + 1.0 + } else { let binding = &mut opacity_bindings[opacity_binding_index]; binding.update(scene_properties); + binding.current } } diff --git a/webrender/src/scene.rs b/webrender/src/scene.rs index 0416ec8f68..f00cf5b7ca 100644 --- a/webrender/src/scene.rs +++ b/webrender/src/scene.rs @@ -22,7 +22,6 @@ use std::sync::Arc; pub struct SceneProperties { transform_properties: FastHashMap, float_properties: FastHashMap, - color_properties: FastHashMap, current_properties: DynamicProperties, pending_properties: Option, } @@ -32,7 +31,6 @@ impl SceneProperties { SceneProperties { transform_properties: FastHashMap::default(), float_properties: FastHashMap::default(), - color_properties: FastHashMap::default(), current_properties: DynamicProperties::default(), pending_properties: None, } @@ -80,11 +78,6 @@ impl SceneProperties { .insert(property.key.id, property.value); } - for property in &pending_properties.colors { - self.color_properties - .insert(property.key.id, property.value); - } - self.current_properties = pending_properties.clone(); properties_changed = true; } @@ -128,27 +121,6 @@ impl SceneProperties { pub fn float_properties(&self) -> &FastHashMap { &self.float_properties } - - /// Get the current value for a color property. - pub fn resolve_color( - &self, - property: &PropertyBinding - ) -> ColorF { - match *property { - PropertyBinding::Value(value) => value, - PropertyBinding::Binding(ref key, v) => { - self.color_properties - .get(&key.id) - .cloned() - .unwrap_or(v) - } - } - } - - pub fn color_properties(&self) -> &FastHashMap { - &self.color_properties - } - } /// A representation of the layout within the display port for a given document or iframe. diff --git a/webrender/src/scene_building.rs b/webrender/src/scene_building.rs index d94cb06bf7..2991c757ea 100644 --- a/webrender/src/scene_building.rs +++ b/webrender/src/scene_building.rs @@ -1214,7 +1214,7 @@ impl<'a> SceneBuilder<'a> { self.add_solid_rectangle( clip_and_scroll, &layout, - PropertyBinding::Value(ColorF::TRANSPARENT), + ColorF::TRANSPARENT, ); } DisplayItem::ClearRectangle(ref info) => { @@ -2749,19 +2749,13 @@ impl<'a> SceneBuilder<'a> { &mut self, clip_and_scroll: ScrollNodeAndClipChain, info: &LayoutPrimitiveInfo, - color: PropertyBinding, + color: ColorF, ) { - match color { - PropertyBinding::Value(value) => { - if value.a == 0.0 { - // Don't add transparent rectangles to the draw list, - // but do consider them for hit testing. This allows - // specifying invisible hit testing areas. - self.add_primitive_to_hit_testing_list(info, clip_and_scroll); - return; - } - }, - PropertyBinding::Binding(..) => {}, + if color.a == 0.0 { + // Don't add transparent rectangles to the draw list, but do consider them for hit + // testing. This allows specifying invisible hit testing areas. + self.add_primitive_to_hit_testing_list(info, clip_and_scroll); + return; } self.add_primitive( diff --git a/webrender_api/src/api.rs b/webrender_api/src/api.rs index 881b6f0613..302ab3d2e9 100644 --- a/webrender_api/src/api.rs +++ b/webrender_api/src/api.rs @@ -1154,7 +1154,7 @@ pub enum PrimitiveKeyKind { /// Rectangle { /// - color: PropertyBinding, + color: ColorU, }, } @@ -1821,7 +1821,7 @@ impl PropertyBindingId { /// A unique key that is used for connecting animated property /// values to bindings in the display list. #[repr(C)] -#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)] +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] pub struct PropertyBindingKey { /// pub id: PropertyBindingId, @@ -1853,7 +1853,7 @@ impl PropertyBindingKey { /// used for the case where the animation is still in-delay phase /// (i.e. the animation doesn't produce any animation values). #[repr(C)] -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)] +#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, PeekPoke)] pub enum PropertyBinding { /// Non-animated value. Value(T), @@ -1873,46 +1873,6 @@ impl From for PropertyBinding { } } -impl From> for PropertyBindingKey { - fn from(key: PropertyBindingKey) -> PropertyBindingKey { - PropertyBindingKey { - id: key.id.clone(), - _phantom: PhantomData, - } - } -} - -impl From> for PropertyBindingKey { - fn from(key: PropertyBindingKey) -> PropertyBindingKey { - PropertyBindingKey { - id: key.id.clone(), - _phantom: PhantomData, - } - } -} - -impl From> for PropertyBinding { - fn from(value: PropertyBinding) -> PropertyBinding { - match value { - PropertyBinding::Value(value) => PropertyBinding::Value(value.into()), - PropertyBinding::Binding(k, v) => { - PropertyBinding::Binding(k.into(), v.into()) - } - } - } -} - -impl From> for PropertyBinding { - fn from(value: PropertyBinding) -> PropertyBinding { - match value { - PropertyBinding::Value(value) => PropertyBinding::Value(value.into()), - PropertyBinding::Binding(k, v) => { - PropertyBinding::Binding(k.into(), v.into()) - } - } - } -} - /// The current value of an animated property. This is /// supplied by the calling code. #[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq)] @@ -1930,10 +1890,8 @@ pub struct PropertyValue { pub struct DynamicProperties { /// pub transforms: Vec>, - /// opacity + /// pub floats: Vec>, - /// background color - pub colors: Vec>, } /// A handler to integrate WebRender with the thread that contains the `Renderer`. diff --git a/webrender_api/src/display_item.rs b/webrender_api/src/display_item.rs index c87249df6d..3a3e1d9ad2 100644 --- a/webrender_api/src/display_item.rs +++ b/webrender_api/src/display_item.rs @@ -299,11 +299,11 @@ pub struct ScrollFrameDisplayItem { pub external_scroll_offset: LayoutVector2D, } -/// A solid or an animating color to draw (may not actually be a rectangle due to complex clips) +/// A solid color to draw (may not actually be a rectangle due to complex clips) #[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] pub struct RectangleDisplayItem { pub common: CommonItemProperties, - pub color: PropertyBinding, + pub color: ColorF, } /// Clears all colors from the area, making it possible to cut holes in the window. diff --git a/webrender_api/src/display_list.rs b/webrender_api/src/display_list.rs index 2f7d3f1aff..8d0a27d0e5 100644 --- a/webrender_api/src/display_list.rs +++ b/webrender_api/src/display_list.rs @@ -1116,19 +1116,7 @@ impl DisplayListBuilder { ) { let item = di::DisplayItem::Rectangle(di::RectangleDisplayItem { common: *common, - color: PropertyBinding::Value(color), - }); - self.push_item(&item); - } - - pub fn push_rect_with_animation( - &mut self, - common: &di::CommonItemProperties, - color: PropertyBinding, - ) { - let item = di::DisplayItem::Rectangle(di::RectangleDisplayItem { - common: *common, - color, + color }); self.push_item(&item); } diff --git a/wrench/src/scene.rs b/wrench/src/scene.rs index 818d000edc..d2e708a9b3 100644 --- a/wrench/src/scene.rs +++ b/wrench/src/scene.rs @@ -14,7 +14,6 @@ use webrender::api::units::{LayoutSize, LayoutTransform}; pub struct SceneProperties { transform_properties: HashMap, float_properties: HashMap, - color_properties: HashMap, } impl SceneProperties { @@ -22,7 +21,6 @@ impl SceneProperties { pub fn set_properties(&mut self, properties: &DynamicProperties) { self.transform_properties.clear(); self.float_properties.clear(); - self.color_properties.clear(); for property in &properties.transforms { self.transform_properties @@ -33,11 +31,6 @@ impl SceneProperties { self.float_properties .insert(property.key.id, property.value); } - - for property in &properties.colors { - self.color_properties - .insert(property.key.id, property.value); - } } /// Get the current value for a transform property. @@ -64,17 +57,6 @@ impl SceneProperties { .unwrap_or(v), } } - - /// Get the current value for a color property. - pub fn resolve_color(&self, property: &PropertyBinding) -> ColorF { - match *property { - PropertyBinding::Value(value) => value, - PropertyBinding::Binding(ref key, v) => self.color_properties - .get(&key.id) - .cloned() - .unwrap_or(v), - } - } } /// A representation of the layout within the display port for a given document or iframe. diff --git a/wrench/src/yaml_frame_writer.rs b/wrench/src/yaml_frame_writer.rs index 6a1da76809..c7b64055f5 100644 --- a/wrench/src/yaml_frame_writer.rs +++ b/wrench/src/yaml_frame_writer.rs @@ -1002,13 +1002,7 @@ impl YamlFrameWriter { DisplayItem::Rectangle(item) => { str_node(&mut v, "type", "rect"); common_node(&mut v, clip_id_mapper, &item.common); - - let key_label = match item.color { - PropertyBinding::Value(..) => "color", - PropertyBinding::Binding(..) => "animating-color", - }; - color_node(&mut v, key_label, - scene.properties.resolve_color(&item.color)); + color_node(&mut v, "color", item.color); } DisplayItem::HitTest(item) => { str_node(&mut v, "type", "hit-test"); From 8353c5a300602eb6e657d82bc927f22ebb7e2b44 Mon Sep 17 00:00:00 2001 From: Hiroyuki Ikezoe Date: Thu, 27 Feb 2020 09:57:41 +0000 Subject: [PATCH 06/33] Bug 1510030 - Don't return the current opacity value from update_opacity_binding. r=gw The return value is not used at all. Differential Revision: https://phabricator.services.mozilla.com/D63600 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/b3dfa17c6305c657f8c3b3c95121c19f005d451c --- webrender/src/prim_store/mod.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/webrender/src/prim_store/mod.rs b/webrender/src/prim_store/mod.rs index 9626d5413f..09c98e2402 100644 --- a/webrender/src/prim_store/mod.rs +++ b/webrender/src/prim_store/mod.rs @@ -4306,13 +4306,10 @@ fn update_opacity_binding( opacity_bindings: &mut OpacityBindingStorage, opacity_binding_index: OpacityBindingIndex, scene_properties: &SceneProperties, -) -> f32 { - if opacity_binding_index == OpacityBindingIndex::INVALID { - 1.0 - } else { +) { + if opacity_binding_index != OpacityBindingIndex::INVALID { let binding = &mut opacity_bindings[opacity_binding_index]; binding.update(scene_properties); - binding.current } } From 6f45b76affe1f4b8511aafeecef60dce66bdb117 Mon Sep 17 00:00:00 2001 From: Hiroyuki Ikezoe Date: Thu, 27 Feb 2020 09:57:50 +0000 Subject: [PATCH 07/33] Bug 1510030 - Fix the comment for OpacityBinding::update(). r=gw Differential Revision: https://phabricator.services.mozilla.com/D63601 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/781cd96dd9a2d21922395d548d05e4f7ae5c155b --- webrender/src/prim_store/mod.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/webrender/src/prim_store/mod.rs b/webrender/src/prim_store/mod.rs index 09c98e2402..dbcd4f8825 100644 --- a/webrender/src/prim_store/mod.rs +++ b/webrender/src/prim_store/mod.rs @@ -918,8 +918,7 @@ impl OpacityBinding { } // Resolve the current value of each opacity binding, and - // store that as a single combined opacity. Returns true - // if the opacity value changed from last time. + // store that as a single combined opacity. pub fn update(&mut self, scene_properties: &SceneProperties) { let mut new_opacity = 1.0; From d422aabce6cab73b97da17b80a45202fb73092d5 Mon Sep 17 00:00:00 2001 From: Hiroyuki Ikezoe Date: Thu, 27 Feb 2020 09:57:59 +0000 Subject: [PATCH 08/33] Bug 1510030 - Make prepare_prim_for_render private. r=gw Differential Revision: https://phabricator.services.mozilla.com/D63603 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/b37ce2b87ac7b02a82a9caf6c45f7a4dea6d6017 --- webrender/src/prim_store/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webrender/src/prim_store/mod.rs b/webrender/src/prim_store/mod.rs index dbcd4f8825..ed39fc39e2 100644 --- a/webrender/src/prim_store/mod.rs +++ b/webrender/src/prim_store/mod.rs @@ -2656,7 +2656,7 @@ impl PrimitiveStore { self.pictures[pic_index.0].requested_composite_mode = None; } - pub fn prepare_prim_for_render( + fn prepare_prim_for_render( &mut self, prim_instance: &mut PrimitiveInstance, prim_spatial_node_index: SpatialNodeIndex, From 692bc61017df9f90dc8b286e1c94d231a929e283 Mon Sep 17 00:00:00 2001 From: Hiroyuki Ikezoe Date: Thu, 27 Feb 2020 09:58:08 +0000 Subject: [PATCH 09/33] Bug 1510030 - Implement WebRender backend to run background color animations on the compositor. r=gw,boris Differential Revision: https://phabricator.services.mozilla.com/D63604 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/a2be11cbe3f22a0b4f121df97045f560dca8ccb4 --- examples/animation.rs | 1 + webrender/src/box_shadow.rs | 5 +- webrender/src/picture.rs | 158 +++++++++++++++++++++++++++--- webrender/src/prim_store/mod.rs | 81 +++++++++++---- webrender/src/scene.rs | 28 ++++++ webrender/src/scene_building.rs | 20 ++-- webrender_api/src/api.rs | 50 +++++++++- webrender_api/src/display_item.rs | 4 +- webrender_api/src/display_list.rs | 14 ++- wrench/src/scene.rs | 18 ++++ wrench/src/yaml_frame_writer.rs | 8 +- 11 files changed, 337 insertions(+), 50 deletions(-) diff --git a/examples/animation.rs b/examples/animation.rs index 4cd7f343ce..d442ab0309 100644 --- a/examples/animation.rs +++ b/examples/animation.rs @@ -187,6 +187,7 @@ impl Example for App { value: self.opacity, } ], + colors: vec![], }, ); txn.generate_frame(); diff --git a/webrender/src/box_shadow.rs b/webrender/src/box_shadow.rs index 124d44d5de..eb8d4d0d4e 100644 --- a/webrender/src/box_shadow.rs +++ b/webrender/src/box_shadow.rs @@ -3,6 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use api::{BorderRadius, BoxShadowClipMode, ClipMode, ColorF, PrimitiveKeyKind}; +use api::PropertyBinding; use api::MAX_BLUR_RADIUS; use api::units::*; use crate::clip::{ClipItemKey, ClipItemKeyKind}; @@ -163,7 +164,7 @@ impl<'a> SceneBuilder<'a> { &LayoutPrimitiveInfo::with_clip_rect(final_prim_rect, prim_info.clip_rect), clips, PrimitiveKeyKind::Rectangle { - color: color.into(), + color: PropertyBinding::Value(color.into()), }, ); } else { @@ -189,7 +190,7 @@ impl<'a> SceneBuilder<'a> { // Draw the box-shadow as a solid rect, using a box-shadow // clip mask item. let prim = PrimitiveKeyKind::Rectangle { - color: color.into(), + color: PropertyBinding::Value(color.into()), }; // Create the box-shadow clip item. diff --git a/webrender/src/picture.rs b/webrender/src/picture.rs index b601793234..e48bb99663 100644 --- a/webrender/src/picture.rs +++ b/webrender/src/picture.rs @@ -96,7 +96,7 @@ use api::{MixBlendMode, PipelineId, PremultipliedColorF, FilterPrimitiveKind}; use api::{PropertyBinding, PropertyBindingId, FilterPrimitive, FontRenderMode}; -use api::{DebugFlags, RasterSpace, ImageKey, ColorF, PrimitiveFlags}; +use api::{DebugFlags, RasterSpace, ImageKey, ColorF, ColorU, PrimitiveFlags}; use api::units::*; use crate::box_shadow::{BLUR_SAMPLE_SCALE}; use crate::clip::{ClipStore, ClipChainInstance, ClipDataHandle, ClipChainId}; @@ -120,6 +120,7 @@ use crate::prim_store::{SpaceMapper, PrimitiveVisibilityMask, PointKey, Primitiv use crate::prim_store::{SpaceSnapper, PictureIndex, PrimitiveInstance, PrimitiveInstanceKind}; use crate::prim_store::{get_raster_rects, PrimitiveScratchBuffer, RectangleKey}; use crate::prim_store::{OpacityBindingStorage, ImageInstanceStorage, OpacityBindingIndex}; +use crate::prim_store::{ColorBindingStorage, ColorBindingIndex}; use crate::print_tree::{PrintTree, PrintTreePrinter}; use crate::render_backend::DataStores; use crate::render_task_graph::RenderTaskId; @@ -264,6 +265,8 @@ pub struct PictureCacheState { spatial_nodes: FastHashMap, /// State of opacity bindings from previous frame opacity_bindings: FastHashMap, + /// State of color bindings from previous frame + color_bindings: FastHashMap, /// The current transform of the picture cache root spatial node root_transform: TransformKey, /// The current tile size in device pixels @@ -278,6 +281,7 @@ pub struct PictureCacheState { pub struct PictureCacheRecycledAllocations { old_opacity_bindings: FastHashMap, + old_color_bindings: FastHashMap, compare_cache: FastHashMap, } @@ -401,33 +405,39 @@ fn clampf(value: f32, low: f32, high: f32) -> f32 { #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct PrimitiveDependencyIndex(pub u32); -/// Information about the state of an opacity binding. +/// Information about the state of a binding. #[derive(Debug)] -pub struct OpacityBindingInfo { +pub struct BindingInfo { /// The current value retrieved from dynamic scene properties. - value: f32, + value: T, /// True if it was changed (or is new) since the last frame build. changed: bool, } -/// Information stored in a tile descriptor for an opacity binding. +/// Information stored in a tile descriptor for a binding. #[derive(Debug, PartialEq, Clone, Copy)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] -pub enum OpacityBinding { - Value(f32), +pub enum Binding { + Value(T), Binding(PropertyBindingId), } -impl From> for OpacityBinding { - fn from(binding: PropertyBinding) -> OpacityBinding { +impl From> for Binding { + fn from(binding: PropertyBinding) -> Binding { match binding { - PropertyBinding::Binding(key, _) => OpacityBinding::Binding(key.id), - PropertyBinding::Value(value) => OpacityBinding::Value(value), + PropertyBinding::Binding(key, _) => Binding::Binding(key.id), + PropertyBinding::Value(value) => Binding::Value(value), } } } +pub type OpacityBinding = Binding; +pub type OpacityBindingInfo = BindingInfo; + +pub type ColorBinding = Binding; +pub type ColorBindingInfo = BindingInfo; + /// Information about the state of a spatial node value #[derive(Debug)] pub struct SpatialNodeDependency { @@ -476,6 +486,9 @@ struct TilePostUpdateContext<'a> { /// Information about opacity bindings from the picture cache. opacity_bindings: &'a FastHashMap, + /// Information about color bindings from the picture cache. + color_bindings: &'a FastHashMap, + /// Current size in device pixels of tiles for this cache current_tile_size: DeviceIntSize, @@ -515,6 +528,9 @@ struct PrimitiveDependencyInfo { /// Opacity bindings this primitive depends on. opacity_bindings: SmallVec<[OpacityBinding; 4]>, + /// Color binding this primitive depends on. + color_binding: Option, + /// Clips that this primitive depends on. clips: SmallVec<[ItemUid; 8]>, @@ -537,6 +553,7 @@ impl PrimitiveDependencyInfo { prim_origin, images: SmallVec::new(), opacity_bindings: SmallVec::new(), + color_binding: None, clip_by_tile: false, prim_clip_rect, clips: SmallVec::new(), @@ -688,6 +705,8 @@ pub enum PrimitiveCompareResult { Image, /// The value of an opacity binding changed OpacityBinding, + /// The value of a color binding changed + ColorBinding, } /// A more detailed version of PrimitiveCompareResult used when @@ -718,7 +737,11 @@ pub enum PrimitiveCompareResultDetail { /// The value of an opacity binding changed OpacityBinding { detail: CompareHelperResult, - } + }, + /// The value of a color binding changed + ColorBinding { + detail: CompareHelperResult, + }, } /// Debugging information about why a tile was invalidated @@ -886,6 +909,7 @@ impl Tile { state.resource_cache, ctx.spatial_nodes, ctx.opacity_bindings, + ctx.color_bindings, ); let mut dirty_rect = PictureRect::zero(); @@ -1054,6 +1078,12 @@ impl Tile { // Include any transforms that this primitive depends on. self.current_descriptor.transforms.extend_from_slice(&info.spatial_nodes); + // Include any color bindings this primitive depends on. + if info.color_binding.is_some() { + self.current_descriptor.color_bindings.insert( + self.current_descriptor.color_bindings.len(), info.color_binding.unwrap()); + } + // TODO(gw): The origin of background rects produced by APZ changes // in Gecko during scrolling. Consider investigating this so the // hack / workaround below is not required. @@ -1106,6 +1136,7 @@ impl Tile { clip_dep_count: info.clips.len() as u8, image_dep_count: info.images.len() as u8, opacity_binding_dep_count: info.opacity_bindings.len() as u8, + color_binding_dep_count: if info.color_binding.is_some() { 1 } else { 0 } as u8, }); // Add this primitive to the dirty rect quadtree. @@ -1317,6 +1348,7 @@ pub struct PrimitiveDescriptor { image_dep_count: u8, opacity_binding_dep_count: u8, clip_dep_count: u8, + color_binding_dep_count: u8, } impl PartialEq for PrimitiveDescriptor { @@ -1467,6 +1499,10 @@ pub struct TileDescriptor { /// Picture space rect that contains valid pixels region of this tile. local_valid_rect: PictureRect, + + /// List of the effects of color that we care about + /// tracking for this tile. + color_bindings: Vec, } impl TileDescriptor { @@ -1478,6 +1514,7 @@ impl TileDescriptor { images: Vec::new(), transforms: Vec::new(), local_valid_rect: PictureRect::zero(), + color_bindings: Vec::new(), } } @@ -1495,11 +1532,12 @@ impl TileDescriptor { prim.prim_clip_rect.w, prim.prim_clip_rect.h, )); - pt.add_item(format!("deps: t={} i={} o={} c={}", + pt.add_item(format!("deps: t={} i={} o={} c={} color={}", prim.transform_dep_count, prim.image_dep_count, prim.opacity_binding_dep_count, prim.clip_dep_count, + prim.color_binding_dep_count, )); pt.end_level(); } @@ -1542,6 +1580,15 @@ impl TileDescriptor { pt.end_level(); } + if !self.color_bindings.is_empty() { + pt.new_level("color_bindings".to_string()); + for color_binding in &self.color_bindings { + pt.new_level(format!("binding={:?}", color_binding)); + pt.end_level(); + } + pt.end_level(); + } + pt.end_level(); } @@ -1554,6 +1601,7 @@ impl TileDescriptor { self.images.clear(); self.transforms.clear(); self.local_valid_rect = PictureRect::zero(); + self.color_bindings.clear(); } } @@ -2026,6 +2074,11 @@ pub struct TileCacheInstance { /// calculate invalid relative transforms when building the spatial /// nodes hash above. used_spatial_nodes: FastHashSet, + /// List of color bindings, with some extra information + /// about whether they changed since last frame. + color_bindings: FastHashMap, + /// Switch back and forth between old and new bindings hashmaps to avoid re-allocating. + old_color_bindings: FastHashMap, /// The current dirty region tracker for this picture. pub dirty_region: DirtyRegion, /// Current size of tiles in picture units. @@ -2114,6 +2167,8 @@ impl TileCacheInstance { spatial_nodes: FastHashMap::default(), old_spatial_nodes: FastHashMap::default(), used_spatial_nodes: FastHashSet::default(), + color_bindings: FastHashMap::default(), + old_color_bindings: FastHashMap::default(), dirty_region: DirtyRegion::new(), tile_size: PictureSize::zero(), tile_rect: TileRect::zero(), @@ -2175,7 +2230,7 @@ impl TileCacheInstance { (p0, p1) } - /// Update transforms, opacity bindings and tile rects. + /// Update transforms, opacity, color bindings and tile rects. pub fn pre_update( &mut self, pic_rect: PictureRect, @@ -2257,6 +2312,7 @@ impl TileCacheInstance { self.root_transform = prev_state.root_transform; self.spatial_nodes = prev_state.spatial_nodes; self.opacity_bindings = prev_state.opacity_bindings; + self.color_bindings = prev_state.color_bindings; self.current_tile_size = prev_state.current_tile_size; self.native_surface_id = prev_state.native_surface_id; self.is_opaque = prev_state.is_opaque; @@ -2280,6 +2336,11 @@ impl TileCacheInstance { &mut self.old_opacity_bindings, prev_state.allocations.old_opacity_bindings, ); + recycle_map( + self.color_bindings.len(), + &mut self.old_color_bindings, + prev_state.allocations.old_color_bindings, + ); recycle_map( prev_state.allocations.compare_cache.len(), &mut self.compare_cache, @@ -2380,6 +2441,23 @@ impl TileCacheInstance { }); } + // Do a hacky diff of color binding values from the last frame. This is + // used later on during tile invalidation tests. + let current_properties = frame_context.scene_properties.color_properties(); + mem::swap(&mut self.color_bindings, &mut self.old_color_bindings); + + self.color_bindings.clear(); + for (id, value) in current_properties { + let changed = match self.old_color_bindings.get(id) { + Some(old_property) => old_property.value != (*value).into(), + None => true, + }; + self.color_bindings.insert(*id, ColorBindingInfo { + value: (*value).into(), + changed, + }); + } + let world_tile_size = WorldSize::new( self.current_tile_size.width as f32 / frame_context.global_device_pixel_scale.0, self.current_tile_size.height as f32 / frame_context.global_device_pixel_scale.0, @@ -2539,6 +2617,7 @@ impl TileCacheInstance { pictures: &[PicturePrimitive], resource_cache: &ResourceCache, opacity_binding_store: &OpacityBindingStorage, + color_bindings: &ColorBindingStorage, image_instances: &ImageInstanceStorage, surface_stack: &[SurfaceIndex], composite_state: &CompositeState, @@ -2675,13 +2754,15 @@ impl TileCacheInstance { prim_info.opacity_bindings.push(binding.into()); } } - PrimitiveInstanceKind::Rectangle { data_handle, opacity_binding_index, .. } => { + PrimitiveInstanceKind::Rectangle { data_handle, opacity_binding_index, color_binding_index, .. } => { if opacity_binding_index == OpacityBindingIndex::INVALID { // Rectangles can only form a backdrop candidate if they are known opaque. // TODO(gw): We could resolve the opacity binding here, but the common // case for background rects is that they don't have animated opacity. let color = match data_stores.prim[data_handle].kind { - PrimitiveTemplateKind::Rectangle { color, .. } => color, + PrimitiveTemplateKind::Rectangle { color, .. } => { + frame_context.scene_properties.resolve_color(&color) + } _ => unreachable!(), }; if color.a >= 1.0 { @@ -2694,6 +2775,10 @@ impl TileCacheInstance { } } + if color_binding_index != ColorBindingIndex::INVALID { + prim_info.color_binding = Some(color_bindings[color_binding_index].into()); + } + prim_info.clip_by_tile = true; } PrimitiveInstanceKind::Image { data_handle, image_instance_index, .. } => { @@ -3095,6 +3180,7 @@ impl TileCacheInstance { backdrop: self.backdrop, spatial_nodes: &self.spatial_nodes, opacity_bindings: &self.opacity_bindings, + color_bindings: &self.color_bindings, current_tile_size: self.current_tile_size, local_rect: self.local_rect, }; @@ -3978,12 +4064,14 @@ impl PicturePrimitive { tiles: tile_cache.tiles, spatial_nodes: tile_cache.spatial_nodes, opacity_bindings: tile_cache.opacity_bindings, + color_bindings: tile_cache.color_bindings, root_transform: tile_cache.root_transform, current_tile_size: tile_cache.current_tile_size, native_surface_id: tile_cache.native_surface_id.take(), is_opaque: tile_cache.is_opaque, allocations: PictureCacheRecycledAllocations { old_opacity_bindings: tile_cache.old_opacity_bindings, + old_color_bindings: tile_cache.old_color_bindings, compare_cache: tile_cache.compare_cache, }, }, @@ -5538,9 +5626,11 @@ struct PrimitiveComparer<'a> { transform_comparer: CompareHelper<'a, SpatialNodeIndex>, image_comparer: CompareHelper<'a, ImageDependency>, opacity_comparer: CompareHelper<'a, OpacityBinding>, + color_comparer: CompareHelper<'a, ColorBinding>, resource_cache: &'a ResourceCache, spatial_nodes: &'a FastHashMap, opacity_bindings: &'a FastHashMap, + color_bindings: &'a FastHashMap, } impl<'a> PrimitiveComparer<'a> { @@ -5550,6 +5640,7 @@ impl<'a> PrimitiveComparer<'a> { resource_cache: &'a ResourceCache, spatial_nodes: &'a FastHashMap, opacity_bindings: &'a FastHashMap, + color_bindings: &'a FastHashMap, ) -> Self { let clip_comparer = CompareHelper::new( &prev.clips, @@ -5571,14 +5662,21 @@ impl<'a> PrimitiveComparer<'a> { &curr.opacity_bindings, ); + let color_comparer = CompareHelper::new( + &prev.color_bindings, + &curr.color_bindings, + ); + PrimitiveComparer { clip_comparer, transform_comparer, image_comparer, opacity_comparer, + color_comparer, resource_cache, spatial_nodes, opacity_bindings, + color_bindings, } } @@ -5587,6 +5685,7 @@ impl<'a> PrimitiveComparer<'a> { self.transform_comparer.reset(); self.image_comparer.reset(); self.opacity_comparer.reset(); + self.color_comparer.reset(); } fn advance_prev(&mut self, prim: &PrimitiveDescriptor) { @@ -5594,6 +5693,7 @@ impl<'a> PrimitiveComparer<'a> { self.transform_comparer.advance_prev(prim.transform_dep_count); self.image_comparer.advance_prev(prim.image_dep_count); self.opacity_comparer.advance_prev(prim.opacity_binding_dep_count); + self.color_comparer.advance_prev(prim.color_binding_dep_count); } fn advance_curr(&mut self, prim: &PrimitiveDescriptor) { @@ -5601,6 +5701,7 @@ impl<'a> PrimitiveComparer<'a> { self.transform_comparer.advance_curr(prim.transform_dep_count); self.image_comparer.advance_curr(prim.image_dep_count); self.opacity_comparer.advance_curr(prim.opacity_binding_dep_count); + self.color_comparer.advance_curr(prim.color_binding_dep_count); } /// Check if two primitive descriptors are the same. @@ -5613,6 +5714,7 @@ impl<'a> PrimitiveComparer<'a> { let resource_cache = self.resource_cache; let spatial_nodes = self.spatial_nodes; let opacity_bindings = self.opacity_bindings; + let color_bindings = self.color_bindings; // Check equality of the PrimitiveDescriptor if prev != curr { @@ -5692,6 +5794,30 @@ impl<'a> PrimitiveComparer<'a> { return PrimitiveCompareResult::OpacityBinding; } + // Check if any of the color bindings this prim has are different. + let mut bind_result = CompareHelperResult::Equal; + if !self.color_comparer.is_same( + prev.color_binding_dep_count, + curr.color_binding_dep_count, + |curr| { + if let ColorBinding::Binding(id) = curr { + if color_bindings + .get(id) + .map_or(true, |info| info.changed) { + return true; + } + } + + true + }, + if opt_detail.is_some() { Some(&mut bind_result) } else { None }, + ) { + if let Some(detail) = opt_detail { + *detail = PrimitiveCompareResultDetail::ColorBinding{ detail: bind_result }; + } + return PrimitiveCompareResult::ColorBinding; + } + PrimitiveCompareResult::Equal } } diff --git a/webrender/src/prim_store/mod.rs b/webrender/src/prim_store/mod.rs index ed39fc39e2..63444097a2 100644 --- a/webrender/src/prim_store/mod.rs +++ b/webrender/src/prim_store/mod.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::{BorderRadius, ClipMode, ColorF}; +use api::{BorderRadius, ClipMode, ColorF, ColorU}; use api::{ImageRendering, RepeatMode, PrimitiveFlags}; use api::{PremultipliedColorF, PropertyBinding, Shadow, GradientStop}; use api::{BoxShadowClipMode, LineStyle, LineOrientation, BorderStyle}; @@ -720,7 +720,7 @@ impl intern::InternDebug for PrimitiveKey {} #[derive(MallocSizeOf)] pub enum PrimitiveTemplateKind { Rectangle { - color: ColorF, + color: PropertyBinding, }, Clear, } @@ -812,7 +812,8 @@ impl PrimitiveTemplateKind { /// Write any GPU blocks for the primitive template to the given request object. fn write_prim_gpu_blocks( &self, - request: &mut GpuDataRequest + request: &mut GpuDataRequest, + scene_properties: &SceneProperties, ) { match *self { PrimitiveTemplateKind::Clear => { @@ -820,7 +821,7 @@ impl PrimitiveTemplateKind { request.push(PremultipliedColorF::BLACK); } PrimitiveTemplateKind::Rectangle { ref color, .. } => { - request.push(color.premultiplied()); + request.push(scene_properties.resolve_color(color).premultiplied()) } } } @@ -834,9 +835,10 @@ impl PrimitiveTemplate { pub fn update( &mut self, frame_state: &mut FrameBuildingState, + scene_properties: &SceneProperties, ) { if let Some(mut request) = frame_state.gpu_cache.request(&mut self.common.gpu_cache_handle) { - self.kind.write_prim_gpu_blocks(&mut request); + self.kind.write_prim_gpu_blocks(&mut request, scene_properties); } self.opacity = match self.kind { @@ -844,7 +846,7 @@ impl PrimitiveTemplate { PrimitiveOpacity::translucent() } PrimitiveTemplateKind::Rectangle { ref color, .. } => { - PrimitiveOpacity::from_alpha(color.a) + PrimitiveOpacity::from_alpha(scene_properties.resolve_color(color).a) } }; } @@ -873,7 +875,7 @@ impl InternablePrimitive for PrimitiveKeyKind { fn make_instance_kind( key: PrimitiveKey, data_handle: PrimitiveDataHandle, - _: &mut PrimitiveStore, + prim_store: &mut PrimitiveStore, _reference_frame_relative_offset: LayoutVector2D, ) -> PrimitiveInstanceKind { match key.kind { @@ -882,11 +884,18 @@ impl InternablePrimitive for PrimitiveKeyKind { data_handle } } - PrimitiveKeyKind::Rectangle { .. } => { + PrimitiveKeyKind::Rectangle { color, .. } => { + let color_binding_index = match color { + PropertyBinding::Binding(..) => { + prim_store.color_bindings.push(color) + } + PropertyBinding::Value(..) => ColorBindingIndex::INVALID, + }; PrimitiveInstanceKind::Rectangle { data_handle, opacity_binding_index: OpacityBindingIndex::INVALID, segment_instance_index: SegmentInstanceIndex::INVALID, + color_binding_index, } } } @@ -1326,7 +1335,10 @@ impl IsVisible for PrimitiveKeyKind { true } PrimitiveKeyKind::Rectangle { ref color, .. } => { - color.a > 0 + match *color { + PropertyBinding::Value(value) => value.a > 0, + PropertyBinding::Binding(..) => true, + } } } } @@ -1343,7 +1355,7 @@ impl CreateShadow for PrimitiveKeyKind { match *self { PrimitiveKeyKind::Rectangle { .. } => { PrimitiveKeyKind::Rectangle { - color: shadow.color.into(), + color: PropertyBinding::Value(shadow.color.into()), } } PrimitiveKeyKind::Clear => { @@ -1404,6 +1416,7 @@ pub enum PrimitiveInstanceKind { data_handle: PrimitiveDataHandle, opacity_binding_index: OpacityBindingIndex, segment_instance_index: SegmentInstanceIndex, + color_binding_index: ColorBindingIndex, }, YuvImage { /// Handle to the common interned data for this primitive. @@ -1660,6 +1673,8 @@ pub type TextRunIndex = storage::Index; pub type TextRunStorage = storage::Storage; pub type OpacityBindingIndex = storage::Index; pub type OpacityBindingStorage = storage::Storage; +pub type ColorBindingIndex = storage::Index>; +pub type ColorBindingStorage = storage::Storage>; pub type BorderHandleStorage = storage::Storage; pub type SegmentStorage = storage::Storage; pub type SegmentsRange = storage::Range; @@ -1799,6 +1814,7 @@ pub struct PrimitiveStoreStats { opacity_binding_count: usize, image_count: usize, linear_gradient_count: usize, + color_binding_count: usize, } impl PrimitiveStoreStats { @@ -1809,6 +1825,7 @@ impl PrimitiveStoreStats { opacity_binding_count: 0, image_count: 0, linear_gradient_count: 0, + color_binding_count: 0, } } } @@ -1826,6 +1843,8 @@ pub struct PrimitiveStore { /// List of animated opacity bindings for a primitive. pub opacity_bindings: OpacityBindingStorage, + /// animated color bindings for this primitive. + pub color_bindings: ColorBindingStorage, } impl PrimitiveStore { @@ -1835,6 +1854,7 @@ impl PrimitiveStore { text_runs: TextRunStorage::new(stats.text_run_count), images: ImageInstanceStorage::new(stats.image_count), opacity_bindings: OpacityBindingStorage::new(stats.opacity_binding_count), + color_bindings: ColorBindingStorage::new(stats.color_binding_count), linear_gradients: LinearGradientStorage::new(stats.linear_gradient_count), } } @@ -1846,6 +1866,7 @@ impl PrimitiveStore { image_count: self.images.len(), opacity_binding_count: self.opacity_bindings.len(), linear_gradient_count: self.linear_gradients.len(), + color_binding_count: self.color_bindings.len(), } } @@ -2150,6 +2171,7 @@ impl PrimitiveStore { &self.pictures, frame_state.resource_cache, &self.opacity_bindings, + &self.color_bindings, &self.images, &frame_state.surface_stack, &frame_state.composite_state, @@ -2975,7 +2997,7 @@ impl PrimitiveStore { // Update the template this instane references, which may refresh the GPU // cache with any shared template data. - prim_data.update(frame_state); + prim_data.update(frame_state, frame_context.scene_properties); } PrimitiveInstanceKind::NormalBorder { data_handle, ref mut cache_handles, .. } => { let prim_data = &mut data_stores.normal_border[*data_handle]; @@ -3065,13 +3087,37 @@ impl PrimitiveStore { // cache with any shared template data. prim_data.kind.update(&mut prim_data.common, frame_state); } - PrimitiveInstanceKind::Rectangle { data_handle, segment_instance_index, opacity_binding_index, .. } => { + PrimitiveInstanceKind::Rectangle { data_handle, segment_instance_index, opacity_binding_index, color_binding_index, .. } => { let prim_data = &mut data_stores.prim[*data_handle]; prim_data.common.may_need_repetition = false; + if *color_binding_index != ColorBindingIndex::INVALID { + match self.color_bindings[*color_binding_index] { + PropertyBinding::Binding(..) => { + // We explicitly invalidate the gpu cache + // if the color is animating. + let gpu_cache_handle = + if *segment_instance_index == SegmentInstanceIndex::INVALID { + None + } else if *segment_instance_index == SegmentInstanceIndex::UNUSED { + Some(&prim_data.common.gpu_cache_handle) + } else { + Some(&scratch.segment_instances[*segment_instance_index].gpu_cache_handle) + }; + if let Some(gpu_cache_handle) = gpu_cache_handle { + frame_state.gpu_cache.invalidate(gpu_cache_handle); + } + } + PropertyBinding::Value(..) => {}, + } + } + // Update the template this instane references, which may refresh the GPU // cache with any shared template data. - prim_data.update(frame_state); + prim_data.update( + frame_state, + frame_context.scene_properties, + ); update_opacity_binding( &mut self.opacity_bindings, @@ -3087,6 +3133,7 @@ impl PrimitiveStore { |request| { prim_data.kind.write_prim_gpu_blocks( request, + frame_context.scene_properties, ); } ); @@ -4342,8 +4389,8 @@ fn test_struct_sizes() { // be done with care, and after checking if talos performance regresses badly. assert_eq!(mem::size_of::(), 88, "PrimitiveInstance size changed"); assert_eq!(mem::size_of::(), 40, "PrimitiveInstanceKind size changed"); - assert_eq!(mem::size_of::(), 40, "PrimitiveTemplate size changed"); - assert_eq!(mem::size_of::(), 20, "PrimitiveTemplateKind size changed"); - assert_eq!(mem::size_of::(), 20, "PrimitiveKey size changed"); - assert_eq!(mem::size_of::(), 5, "PrimitiveKeyKind size changed"); + assert_eq!(mem::size_of::(), 48, "PrimitiveTemplate size changed"); + assert_eq!(mem::size_of::(), 28, "PrimitiveTemplateKind size changed"); + assert_eq!(mem::size_of::(), 28, "PrimitiveKey size changed"); + assert_eq!(mem::size_of::(), 16, "PrimitiveKeyKind size changed"); } diff --git a/webrender/src/scene.rs b/webrender/src/scene.rs index f00cf5b7ca..0416ec8f68 100644 --- a/webrender/src/scene.rs +++ b/webrender/src/scene.rs @@ -22,6 +22,7 @@ use std::sync::Arc; pub struct SceneProperties { transform_properties: FastHashMap, float_properties: FastHashMap, + color_properties: FastHashMap, current_properties: DynamicProperties, pending_properties: Option, } @@ -31,6 +32,7 @@ impl SceneProperties { SceneProperties { transform_properties: FastHashMap::default(), float_properties: FastHashMap::default(), + color_properties: FastHashMap::default(), current_properties: DynamicProperties::default(), pending_properties: None, } @@ -78,6 +80,11 @@ impl SceneProperties { .insert(property.key.id, property.value); } + for property in &pending_properties.colors { + self.color_properties + .insert(property.key.id, property.value); + } + self.current_properties = pending_properties.clone(); properties_changed = true; } @@ -121,6 +128,27 @@ impl SceneProperties { pub fn float_properties(&self) -> &FastHashMap { &self.float_properties } + + /// Get the current value for a color property. + pub fn resolve_color( + &self, + property: &PropertyBinding + ) -> ColorF { + match *property { + PropertyBinding::Value(value) => value, + PropertyBinding::Binding(ref key, v) => { + self.color_properties + .get(&key.id) + .cloned() + .unwrap_or(v) + } + } + } + + pub fn color_properties(&self) -> &FastHashMap { + &self.color_properties + } + } /// A representation of the layout within the display port for a given document or iframe. diff --git a/webrender/src/scene_building.rs b/webrender/src/scene_building.rs index 2991c757ea..d94cb06bf7 100644 --- a/webrender/src/scene_building.rs +++ b/webrender/src/scene_building.rs @@ -1214,7 +1214,7 @@ impl<'a> SceneBuilder<'a> { self.add_solid_rectangle( clip_and_scroll, &layout, - ColorF::TRANSPARENT, + PropertyBinding::Value(ColorF::TRANSPARENT), ); } DisplayItem::ClearRectangle(ref info) => { @@ -2749,13 +2749,19 @@ impl<'a> SceneBuilder<'a> { &mut self, clip_and_scroll: ScrollNodeAndClipChain, info: &LayoutPrimitiveInfo, - color: ColorF, + color: PropertyBinding, ) { - if color.a == 0.0 { - // Don't add transparent rectangles to the draw list, but do consider them for hit - // testing. This allows specifying invisible hit testing areas. - self.add_primitive_to_hit_testing_list(info, clip_and_scroll); - return; + match color { + PropertyBinding::Value(value) => { + if value.a == 0.0 { + // Don't add transparent rectangles to the draw list, + // but do consider them for hit testing. This allows + // specifying invisible hit testing areas. + self.add_primitive_to_hit_testing_list(info, clip_and_scroll); + return; + } + }, + PropertyBinding::Binding(..) => {}, } self.add_primitive( diff --git a/webrender_api/src/api.rs b/webrender_api/src/api.rs index 302ab3d2e9..881b6f0613 100644 --- a/webrender_api/src/api.rs +++ b/webrender_api/src/api.rs @@ -1154,7 +1154,7 @@ pub enum PrimitiveKeyKind { /// Rectangle { /// - color: ColorU, + color: PropertyBinding, }, } @@ -1821,7 +1821,7 @@ impl PropertyBindingId { /// A unique key that is used for connecting animated property /// values to bindings in the display list. #[repr(C)] -#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)] pub struct PropertyBindingKey { /// pub id: PropertyBindingId, @@ -1853,7 +1853,7 @@ impl PropertyBindingKey { /// used for the case where the animation is still in-delay phase /// (i.e. the animation doesn't produce any animation values). #[repr(C)] -#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, PeekPoke)] +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)] pub enum PropertyBinding { /// Non-animated value. Value(T), @@ -1873,6 +1873,46 @@ impl From for PropertyBinding { } } +impl From> for PropertyBindingKey { + fn from(key: PropertyBindingKey) -> PropertyBindingKey { + PropertyBindingKey { + id: key.id.clone(), + _phantom: PhantomData, + } + } +} + +impl From> for PropertyBindingKey { + fn from(key: PropertyBindingKey) -> PropertyBindingKey { + PropertyBindingKey { + id: key.id.clone(), + _phantom: PhantomData, + } + } +} + +impl From> for PropertyBinding { + fn from(value: PropertyBinding) -> PropertyBinding { + match value { + PropertyBinding::Value(value) => PropertyBinding::Value(value.into()), + PropertyBinding::Binding(k, v) => { + PropertyBinding::Binding(k.into(), v.into()) + } + } + } +} + +impl From> for PropertyBinding { + fn from(value: PropertyBinding) -> PropertyBinding { + match value { + PropertyBinding::Value(value) => PropertyBinding::Value(value.into()), + PropertyBinding::Binding(k, v) => { + PropertyBinding::Binding(k.into(), v.into()) + } + } + } +} + /// The current value of an animated property. This is /// supplied by the calling code. #[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq)] @@ -1890,8 +1930,10 @@ pub struct PropertyValue { pub struct DynamicProperties { /// pub transforms: Vec>, - /// + /// opacity pub floats: Vec>, + /// background color + pub colors: Vec>, } /// A handler to integrate WebRender with the thread that contains the `Renderer`. diff --git a/webrender_api/src/display_item.rs b/webrender_api/src/display_item.rs index 3a3e1d9ad2..c87249df6d 100644 --- a/webrender_api/src/display_item.rs +++ b/webrender_api/src/display_item.rs @@ -299,11 +299,11 @@ pub struct ScrollFrameDisplayItem { pub external_scroll_offset: LayoutVector2D, } -/// A solid color to draw (may not actually be a rectangle due to complex clips) +/// A solid or an animating color to draw (may not actually be a rectangle due to complex clips) #[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] pub struct RectangleDisplayItem { pub common: CommonItemProperties, - pub color: ColorF, + pub color: PropertyBinding, } /// Clears all colors from the area, making it possible to cut holes in the window. diff --git a/webrender_api/src/display_list.rs b/webrender_api/src/display_list.rs index 8d0a27d0e5..2f7d3f1aff 100644 --- a/webrender_api/src/display_list.rs +++ b/webrender_api/src/display_list.rs @@ -1116,7 +1116,19 @@ impl DisplayListBuilder { ) { let item = di::DisplayItem::Rectangle(di::RectangleDisplayItem { common: *common, - color + color: PropertyBinding::Value(color), + }); + self.push_item(&item); + } + + pub fn push_rect_with_animation( + &mut self, + common: &di::CommonItemProperties, + color: PropertyBinding, + ) { + let item = di::DisplayItem::Rectangle(di::RectangleDisplayItem { + common: *common, + color, }); self.push_item(&item); } diff --git a/wrench/src/scene.rs b/wrench/src/scene.rs index d2e708a9b3..818d000edc 100644 --- a/wrench/src/scene.rs +++ b/wrench/src/scene.rs @@ -14,6 +14,7 @@ use webrender::api::units::{LayoutSize, LayoutTransform}; pub struct SceneProperties { transform_properties: HashMap, float_properties: HashMap, + color_properties: HashMap, } impl SceneProperties { @@ -21,6 +22,7 @@ impl SceneProperties { pub fn set_properties(&mut self, properties: &DynamicProperties) { self.transform_properties.clear(); self.float_properties.clear(); + self.color_properties.clear(); for property in &properties.transforms { self.transform_properties @@ -31,6 +33,11 @@ impl SceneProperties { self.float_properties .insert(property.key.id, property.value); } + + for property in &properties.colors { + self.color_properties + .insert(property.key.id, property.value); + } } /// Get the current value for a transform property. @@ -57,6 +64,17 @@ impl SceneProperties { .unwrap_or(v), } } + + /// Get the current value for a color property. + pub fn resolve_color(&self, property: &PropertyBinding) -> ColorF { + match *property { + PropertyBinding::Value(value) => value, + PropertyBinding::Binding(ref key, v) => self.color_properties + .get(&key.id) + .cloned() + .unwrap_or(v), + } + } } /// A representation of the layout within the display port for a given document or iframe. diff --git a/wrench/src/yaml_frame_writer.rs b/wrench/src/yaml_frame_writer.rs index c7b64055f5..6a1da76809 100644 --- a/wrench/src/yaml_frame_writer.rs +++ b/wrench/src/yaml_frame_writer.rs @@ -1002,7 +1002,13 @@ impl YamlFrameWriter { DisplayItem::Rectangle(item) => { str_node(&mut v, "type", "rect"); common_node(&mut v, clip_id_mapper, &item.common); - color_node(&mut v, "color", item.color); + + let key_label = match item.color { + PropertyBinding::Value(..) => "color", + PropertyBinding::Binding(..) => "animating-color", + }; + color_node(&mut v, key_label, + scene.properties.resolve_color(&item.color)); } DisplayItem::HitTest(item) => { str_node(&mut v, "type", "hit-test"); From 1fc351208a1cf7a831e2d0666bdc7e9e55093670 Mon Sep 17 00:00:00 2001 From: Bogdan Tara Date: Thu, 27 Feb 2020 09:58:17 +0000 Subject: [PATCH 10/33] Backed out 6 changesets (bug 1510030) for test_running_on_compositor.html failures CLOSED TREE Backed out changeset a14a131ca731 (bug 1510030) Backed out changeset a75359516fec (bug 1510030) Backed out changeset a2be11cbe3f2 (bug 1510030) Backed out changeset b37ce2b87ac7 (bug 1510030) Backed out changeset 781cd96dd9a2 (bug 1510030) Backed out changeset b3dfa17c6305 (bug 1510030) [ghsync] From https://hg.mozilla.org/mozilla-central/rev/7d06c5f0f339c0016c764d49ebe6962b177a1e15 --- examples/animation.rs | 1 - webrender/src/box_shadow.rs | 5 +- webrender/src/picture.rs | 158 +++--------------------------- webrender/src/prim_store/mod.rs | 93 +++++------------- webrender/src/scene.rs | 28 ------ webrender/src/scene_building.rs | 20 ++-- webrender_api/src/api.rs | 50 +--------- webrender_api/src/display_item.rs | 4 +- webrender_api/src/display_list.rs | 14 +-- wrench/src/scene.rs | 18 ---- wrench/src/yaml_frame_writer.rs | 8 +- 11 files changed, 58 insertions(+), 341 deletions(-) diff --git a/examples/animation.rs b/examples/animation.rs index d442ab0309..4cd7f343ce 100644 --- a/examples/animation.rs +++ b/examples/animation.rs @@ -187,7 +187,6 @@ impl Example for App { value: self.opacity, } ], - colors: vec![], }, ); txn.generate_frame(); diff --git a/webrender/src/box_shadow.rs b/webrender/src/box_shadow.rs index eb8d4d0d4e..124d44d5de 100644 --- a/webrender/src/box_shadow.rs +++ b/webrender/src/box_shadow.rs @@ -3,7 +3,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use api::{BorderRadius, BoxShadowClipMode, ClipMode, ColorF, PrimitiveKeyKind}; -use api::PropertyBinding; use api::MAX_BLUR_RADIUS; use api::units::*; use crate::clip::{ClipItemKey, ClipItemKeyKind}; @@ -164,7 +163,7 @@ impl<'a> SceneBuilder<'a> { &LayoutPrimitiveInfo::with_clip_rect(final_prim_rect, prim_info.clip_rect), clips, PrimitiveKeyKind::Rectangle { - color: PropertyBinding::Value(color.into()), + color: color.into(), }, ); } else { @@ -190,7 +189,7 @@ impl<'a> SceneBuilder<'a> { // Draw the box-shadow as a solid rect, using a box-shadow // clip mask item. let prim = PrimitiveKeyKind::Rectangle { - color: PropertyBinding::Value(color.into()), + color: color.into(), }; // Create the box-shadow clip item. diff --git a/webrender/src/picture.rs b/webrender/src/picture.rs index e48bb99663..b601793234 100644 --- a/webrender/src/picture.rs +++ b/webrender/src/picture.rs @@ -96,7 +96,7 @@ use api::{MixBlendMode, PipelineId, PremultipliedColorF, FilterPrimitiveKind}; use api::{PropertyBinding, PropertyBindingId, FilterPrimitive, FontRenderMode}; -use api::{DebugFlags, RasterSpace, ImageKey, ColorF, ColorU, PrimitiveFlags}; +use api::{DebugFlags, RasterSpace, ImageKey, ColorF, PrimitiveFlags}; use api::units::*; use crate::box_shadow::{BLUR_SAMPLE_SCALE}; use crate::clip::{ClipStore, ClipChainInstance, ClipDataHandle, ClipChainId}; @@ -120,7 +120,6 @@ use crate::prim_store::{SpaceMapper, PrimitiveVisibilityMask, PointKey, Primitiv use crate::prim_store::{SpaceSnapper, PictureIndex, PrimitiveInstance, PrimitiveInstanceKind}; use crate::prim_store::{get_raster_rects, PrimitiveScratchBuffer, RectangleKey}; use crate::prim_store::{OpacityBindingStorage, ImageInstanceStorage, OpacityBindingIndex}; -use crate::prim_store::{ColorBindingStorage, ColorBindingIndex}; use crate::print_tree::{PrintTree, PrintTreePrinter}; use crate::render_backend::DataStores; use crate::render_task_graph::RenderTaskId; @@ -265,8 +264,6 @@ pub struct PictureCacheState { spatial_nodes: FastHashMap, /// State of opacity bindings from previous frame opacity_bindings: FastHashMap, - /// State of color bindings from previous frame - color_bindings: FastHashMap, /// The current transform of the picture cache root spatial node root_transform: TransformKey, /// The current tile size in device pixels @@ -281,7 +278,6 @@ pub struct PictureCacheState { pub struct PictureCacheRecycledAllocations { old_opacity_bindings: FastHashMap, - old_color_bindings: FastHashMap, compare_cache: FastHashMap, } @@ -405,39 +401,33 @@ fn clampf(value: f32, low: f32, high: f32) -> f32 { #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct PrimitiveDependencyIndex(pub u32); -/// Information about the state of a binding. +/// Information about the state of an opacity binding. #[derive(Debug)] -pub struct BindingInfo { +pub struct OpacityBindingInfo { /// The current value retrieved from dynamic scene properties. - value: T, + value: f32, /// True if it was changed (or is new) since the last frame build. changed: bool, } -/// Information stored in a tile descriptor for a binding. +/// Information stored in a tile descriptor for an opacity binding. #[derive(Debug, PartialEq, Clone, Copy)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] -pub enum Binding { - Value(T), +pub enum OpacityBinding { + Value(f32), Binding(PropertyBindingId), } -impl From> for Binding { - fn from(binding: PropertyBinding) -> Binding { +impl From> for OpacityBinding { + fn from(binding: PropertyBinding) -> OpacityBinding { match binding { - PropertyBinding::Binding(key, _) => Binding::Binding(key.id), - PropertyBinding::Value(value) => Binding::Value(value), + PropertyBinding::Binding(key, _) => OpacityBinding::Binding(key.id), + PropertyBinding::Value(value) => OpacityBinding::Value(value), } } } -pub type OpacityBinding = Binding; -pub type OpacityBindingInfo = BindingInfo; - -pub type ColorBinding = Binding; -pub type ColorBindingInfo = BindingInfo; - /// Information about the state of a spatial node value #[derive(Debug)] pub struct SpatialNodeDependency { @@ -486,9 +476,6 @@ struct TilePostUpdateContext<'a> { /// Information about opacity bindings from the picture cache. opacity_bindings: &'a FastHashMap, - /// Information about color bindings from the picture cache. - color_bindings: &'a FastHashMap, - /// Current size in device pixels of tiles for this cache current_tile_size: DeviceIntSize, @@ -528,9 +515,6 @@ struct PrimitiveDependencyInfo { /// Opacity bindings this primitive depends on. opacity_bindings: SmallVec<[OpacityBinding; 4]>, - /// Color binding this primitive depends on. - color_binding: Option, - /// Clips that this primitive depends on. clips: SmallVec<[ItemUid; 8]>, @@ -553,7 +537,6 @@ impl PrimitiveDependencyInfo { prim_origin, images: SmallVec::new(), opacity_bindings: SmallVec::new(), - color_binding: None, clip_by_tile: false, prim_clip_rect, clips: SmallVec::new(), @@ -705,8 +688,6 @@ pub enum PrimitiveCompareResult { Image, /// The value of an opacity binding changed OpacityBinding, - /// The value of a color binding changed - ColorBinding, } /// A more detailed version of PrimitiveCompareResult used when @@ -737,11 +718,7 @@ pub enum PrimitiveCompareResultDetail { /// The value of an opacity binding changed OpacityBinding { detail: CompareHelperResult, - }, - /// The value of a color binding changed - ColorBinding { - detail: CompareHelperResult, - }, + } } /// Debugging information about why a tile was invalidated @@ -909,7 +886,6 @@ impl Tile { state.resource_cache, ctx.spatial_nodes, ctx.opacity_bindings, - ctx.color_bindings, ); let mut dirty_rect = PictureRect::zero(); @@ -1078,12 +1054,6 @@ impl Tile { // Include any transforms that this primitive depends on. self.current_descriptor.transforms.extend_from_slice(&info.spatial_nodes); - // Include any color bindings this primitive depends on. - if info.color_binding.is_some() { - self.current_descriptor.color_bindings.insert( - self.current_descriptor.color_bindings.len(), info.color_binding.unwrap()); - } - // TODO(gw): The origin of background rects produced by APZ changes // in Gecko during scrolling. Consider investigating this so the // hack / workaround below is not required. @@ -1136,7 +1106,6 @@ impl Tile { clip_dep_count: info.clips.len() as u8, image_dep_count: info.images.len() as u8, opacity_binding_dep_count: info.opacity_bindings.len() as u8, - color_binding_dep_count: if info.color_binding.is_some() { 1 } else { 0 } as u8, }); // Add this primitive to the dirty rect quadtree. @@ -1348,7 +1317,6 @@ pub struct PrimitiveDescriptor { image_dep_count: u8, opacity_binding_dep_count: u8, clip_dep_count: u8, - color_binding_dep_count: u8, } impl PartialEq for PrimitiveDescriptor { @@ -1499,10 +1467,6 @@ pub struct TileDescriptor { /// Picture space rect that contains valid pixels region of this tile. local_valid_rect: PictureRect, - - /// List of the effects of color that we care about - /// tracking for this tile. - color_bindings: Vec, } impl TileDescriptor { @@ -1514,7 +1478,6 @@ impl TileDescriptor { images: Vec::new(), transforms: Vec::new(), local_valid_rect: PictureRect::zero(), - color_bindings: Vec::new(), } } @@ -1532,12 +1495,11 @@ impl TileDescriptor { prim.prim_clip_rect.w, prim.prim_clip_rect.h, )); - pt.add_item(format!("deps: t={} i={} o={} c={} color={}", + pt.add_item(format!("deps: t={} i={} o={} c={}", prim.transform_dep_count, prim.image_dep_count, prim.opacity_binding_dep_count, prim.clip_dep_count, - prim.color_binding_dep_count, )); pt.end_level(); } @@ -1580,15 +1542,6 @@ impl TileDescriptor { pt.end_level(); } - if !self.color_bindings.is_empty() { - pt.new_level("color_bindings".to_string()); - for color_binding in &self.color_bindings { - pt.new_level(format!("binding={:?}", color_binding)); - pt.end_level(); - } - pt.end_level(); - } - pt.end_level(); } @@ -1601,7 +1554,6 @@ impl TileDescriptor { self.images.clear(); self.transforms.clear(); self.local_valid_rect = PictureRect::zero(); - self.color_bindings.clear(); } } @@ -2074,11 +2026,6 @@ pub struct TileCacheInstance { /// calculate invalid relative transforms when building the spatial /// nodes hash above. used_spatial_nodes: FastHashSet, - /// List of color bindings, with some extra information - /// about whether they changed since last frame. - color_bindings: FastHashMap, - /// Switch back and forth between old and new bindings hashmaps to avoid re-allocating. - old_color_bindings: FastHashMap, /// The current dirty region tracker for this picture. pub dirty_region: DirtyRegion, /// Current size of tiles in picture units. @@ -2167,8 +2114,6 @@ impl TileCacheInstance { spatial_nodes: FastHashMap::default(), old_spatial_nodes: FastHashMap::default(), used_spatial_nodes: FastHashSet::default(), - color_bindings: FastHashMap::default(), - old_color_bindings: FastHashMap::default(), dirty_region: DirtyRegion::new(), tile_size: PictureSize::zero(), tile_rect: TileRect::zero(), @@ -2230,7 +2175,7 @@ impl TileCacheInstance { (p0, p1) } - /// Update transforms, opacity, color bindings and tile rects. + /// Update transforms, opacity bindings and tile rects. pub fn pre_update( &mut self, pic_rect: PictureRect, @@ -2312,7 +2257,6 @@ impl TileCacheInstance { self.root_transform = prev_state.root_transform; self.spatial_nodes = prev_state.spatial_nodes; self.opacity_bindings = prev_state.opacity_bindings; - self.color_bindings = prev_state.color_bindings; self.current_tile_size = prev_state.current_tile_size; self.native_surface_id = prev_state.native_surface_id; self.is_opaque = prev_state.is_opaque; @@ -2336,11 +2280,6 @@ impl TileCacheInstance { &mut self.old_opacity_bindings, prev_state.allocations.old_opacity_bindings, ); - recycle_map( - self.color_bindings.len(), - &mut self.old_color_bindings, - prev_state.allocations.old_color_bindings, - ); recycle_map( prev_state.allocations.compare_cache.len(), &mut self.compare_cache, @@ -2441,23 +2380,6 @@ impl TileCacheInstance { }); } - // Do a hacky diff of color binding values from the last frame. This is - // used later on during tile invalidation tests. - let current_properties = frame_context.scene_properties.color_properties(); - mem::swap(&mut self.color_bindings, &mut self.old_color_bindings); - - self.color_bindings.clear(); - for (id, value) in current_properties { - let changed = match self.old_color_bindings.get(id) { - Some(old_property) => old_property.value != (*value).into(), - None => true, - }; - self.color_bindings.insert(*id, ColorBindingInfo { - value: (*value).into(), - changed, - }); - } - let world_tile_size = WorldSize::new( self.current_tile_size.width as f32 / frame_context.global_device_pixel_scale.0, self.current_tile_size.height as f32 / frame_context.global_device_pixel_scale.0, @@ -2617,7 +2539,6 @@ impl TileCacheInstance { pictures: &[PicturePrimitive], resource_cache: &ResourceCache, opacity_binding_store: &OpacityBindingStorage, - color_bindings: &ColorBindingStorage, image_instances: &ImageInstanceStorage, surface_stack: &[SurfaceIndex], composite_state: &CompositeState, @@ -2754,15 +2675,13 @@ impl TileCacheInstance { prim_info.opacity_bindings.push(binding.into()); } } - PrimitiveInstanceKind::Rectangle { data_handle, opacity_binding_index, color_binding_index, .. } => { + PrimitiveInstanceKind::Rectangle { data_handle, opacity_binding_index, .. } => { if opacity_binding_index == OpacityBindingIndex::INVALID { // Rectangles can only form a backdrop candidate if they are known opaque. // TODO(gw): We could resolve the opacity binding here, but the common // case for background rects is that they don't have animated opacity. let color = match data_stores.prim[data_handle].kind { - PrimitiveTemplateKind::Rectangle { color, .. } => { - frame_context.scene_properties.resolve_color(&color) - } + PrimitiveTemplateKind::Rectangle { color, .. } => color, _ => unreachable!(), }; if color.a >= 1.0 { @@ -2775,10 +2694,6 @@ impl TileCacheInstance { } } - if color_binding_index != ColorBindingIndex::INVALID { - prim_info.color_binding = Some(color_bindings[color_binding_index].into()); - } - prim_info.clip_by_tile = true; } PrimitiveInstanceKind::Image { data_handle, image_instance_index, .. } => { @@ -3180,7 +3095,6 @@ impl TileCacheInstance { backdrop: self.backdrop, spatial_nodes: &self.spatial_nodes, opacity_bindings: &self.opacity_bindings, - color_bindings: &self.color_bindings, current_tile_size: self.current_tile_size, local_rect: self.local_rect, }; @@ -4064,14 +3978,12 @@ impl PicturePrimitive { tiles: tile_cache.tiles, spatial_nodes: tile_cache.spatial_nodes, opacity_bindings: tile_cache.opacity_bindings, - color_bindings: tile_cache.color_bindings, root_transform: tile_cache.root_transform, current_tile_size: tile_cache.current_tile_size, native_surface_id: tile_cache.native_surface_id.take(), is_opaque: tile_cache.is_opaque, allocations: PictureCacheRecycledAllocations { old_opacity_bindings: tile_cache.old_opacity_bindings, - old_color_bindings: tile_cache.old_color_bindings, compare_cache: tile_cache.compare_cache, }, }, @@ -5626,11 +5538,9 @@ struct PrimitiveComparer<'a> { transform_comparer: CompareHelper<'a, SpatialNodeIndex>, image_comparer: CompareHelper<'a, ImageDependency>, opacity_comparer: CompareHelper<'a, OpacityBinding>, - color_comparer: CompareHelper<'a, ColorBinding>, resource_cache: &'a ResourceCache, spatial_nodes: &'a FastHashMap, opacity_bindings: &'a FastHashMap, - color_bindings: &'a FastHashMap, } impl<'a> PrimitiveComparer<'a> { @@ -5640,7 +5550,6 @@ impl<'a> PrimitiveComparer<'a> { resource_cache: &'a ResourceCache, spatial_nodes: &'a FastHashMap, opacity_bindings: &'a FastHashMap, - color_bindings: &'a FastHashMap, ) -> Self { let clip_comparer = CompareHelper::new( &prev.clips, @@ -5662,21 +5571,14 @@ impl<'a> PrimitiveComparer<'a> { &curr.opacity_bindings, ); - let color_comparer = CompareHelper::new( - &prev.color_bindings, - &curr.color_bindings, - ); - PrimitiveComparer { clip_comparer, transform_comparer, image_comparer, opacity_comparer, - color_comparer, resource_cache, spatial_nodes, opacity_bindings, - color_bindings, } } @@ -5685,7 +5587,6 @@ impl<'a> PrimitiveComparer<'a> { self.transform_comparer.reset(); self.image_comparer.reset(); self.opacity_comparer.reset(); - self.color_comparer.reset(); } fn advance_prev(&mut self, prim: &PrimitiveDescriptor) { @@ -5693,7 +5594,6 @@ impl<'a> PrimitiveComparer<'a> { self.transform_comparer.advance_prev(prim.transform_dep_count); self.image_comparer.advance_prev(prim.image_dep_count); self.opacity_comparer.advance_prev(prim.opacity_binding_dep_count); - self.color_comparer.advance_prev(prim.color_binding_dep_count); } fn advance_curr(&mut self, prim: &PrimitiveDescriptor) { @@ -5701,7 +5601,6 @@ impl<'a> PrimitiveComparer<'a> { self.transform_comparer.advance_curr(prim.transform_dep_count); self.image_comparer.advance_curr(prim.image_dep_count); self.opacity_comparer.advance_curr(prim.opacity_binding_dep_count); - self.color_comparer.advance_curr(prim.color_binding_dep_count); } /// Check if two primitive descriptors are the same. @@ -5714,7 +5613,6 @@ impl<'a> PrimitiveComparer<'a> { let resource_cache = self.resource_cache; let spatial_nodes = self.spatial_nodes; let opacity_bindings = self.opacity_bindings; - let color_bindings = self.color_bindings; // Check equality of the PrimitiveDescriptor if prev != curr { @@ -5794,30 +5692,6 @@ impl<'a> PrimitiveComparer<'a> { return PrimitiveCompareResult::OpacityBinding; } - // Check if any of the color bindings this prim has are different. - let mut bind_result = CompareHelperResult::Equal; - if !self.color_comparer.is_same( - prev.color_binding_dep_count, - curr.color_binding_dep_count, - |curr| { - if let ColorBinding::Binding(id) = curr { - if color_bindings - .get(id) - .map_or(true, |info| info.changed) { - return true; - } - } - - true - }, - if opt_detail.is_some() { Some(&mut bind_result) } else { None }, - ) { - if let Some(detail) = opt_detail { - *detail = PrimitiveCompareResultDetail::ColorBinding{ detail: bind_result }; - } - return PrimitiveCompareResult::ColorBinding; - } - PrimitiveCompareResult::Equal } } diff --git a/webrender/src/prim_store/mod.rs b/webrender/src/prim_store/mod.rs index 63444097a2..9626d5413f 100644 --- a/webrender/src/prim_store/mod.rs +++ b/webrender/src/prim_store/mod.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::{BorderRadius, ClipMode, ColorF, ColorU}; +use api::{BorderRadius, ClipMode, ColorF}; use api::{ImageRendering, RepeatMode, PrimitiveFlags}; use api::{PremultipliedColorF, PropertyBinding, Shadow, GradientStop}; use api::{BoxShadowClipMode, LineStyle, LineOrientation, BorderStyle}; @@ -720,7 +720,7 @@ impl intern::InternDebug for PrimitiveKey {} #[derive(MallocSizeOf)] pub enum PrimitiveTemplateKind { Rectangle { - color: PropertyBinding, + color: ColorF, }, Clear, } @@ -812,8 +812,7 @@ impl PrimitiveTemplateKind { /// Write any GPU blocks for the primitive template to the given request object. fn write_prim_gpu_blocks( &self, - request: &mut GpuDataRequest, - scene_properties: &SceneProperties, + request: &mut GpuDataRequest ) { match *self { PrimitiveTemplateKind::Clear => { @@ -821,7 +820,7 @@ impl PrimitiveTemplateKind { request.push(PremultipliedColorF::BLACK); } PrimitiveTemplateKind::Rectangle { ref color, .. } => { - request.push(scene_properties.resolve_color(color).premultiplied()) + request.push(color.premultiplied()); } } } @@ -835,10 +834,9 @@ impl PrimitiveTemplate { pub fn update( &mut self, frame_state: &mut FrameBuildingState, - scene_properties: &SceneProperties, ) { if let Some(mut request) = frame_state.gpu_cache.request(&mut self.common.gpu_cache_handle) { - self.kind.write_prim_gpu_blocks(&mut request, scene_properties); + self.kind.write_prim_gpu_blocks(&mut request); } self.opacity = match self.kind { @@ -846,7 +844,7 @@ impl PrimitiveTemplate { PrimitiveOpacity::translucent() } PrimitiveTemplateKind::Rectangle { ref color, .. } => { - PrimitiveOpacity::from_alpha(scene_properties.resolve_color(color).a) + PrimitiveOpacity::from_alpha(color.a) } }; } @@ -875,7 +873,7 @@ impl InternablePrimitive for PrimitiveKeyKind { fn make_instance_kind( key: PrimitiveKey, data_handle: PrimitiveDataHandle, - prim_store: &mut PrimitiveStore, + _: &mut PrimitiveStore, _reference_frame_relative_offset: LayoutVector2D, ) -> PrimitiveInstanceKind { match key.kind { @@ -884,18 +882,11 @@ impl InternablePrimitive for PrimitiveKeyKind { data_handle } } - PrimitiveKeyKind::Rectangle { color, .. } => { - let color_binding_index = match color { - PropertyBinding::Binding(..) => { - prim_store.color_bindings.push(color) - } - PropertyBinding::Value(..) => ColorBindingIndex::INVALID, - }; + PrimitiveKeyKind::Rectangle { .. } => { PrimitiveInstanceKind::Rectangle { data_handle, opacity_binding_index: OpacityBindingIndex::INVALID, segment_instance_index: SegmentInstanceIndex::INVALID, - color_binding_index, } } } @@ -927,7 +918,8 @@ impl OpacityBinding { } // Resolve the current value of each opacity binding, and - // store that as a single combined opacity. + // store that as a single combined opacity. Returns true + // if the opacity value changed from last time. pub fn update(&mut self, scene_properties: &SceneProperties) { let mut new_opacity = 1.0; @@ -1335,10 +1327,7 @@ impl IsVisible for PrimitiveKeyKind { true } PrimitiveKeyKind::Rectangle { ref color, .. } => { - match *color { - PropertyBinding::Value(value) => value.a > 0, - PropertyBinding::Binding(..) => true, - } + color.a > 0 } } } @@ -1355,7 +1344,7 @@ impl CreateShadow for PrimitiveKeyKind { match *self { PrimitiveKeyKind::Rectangle { .. } => { PrimitiveKeyKind::Rectangle { - color: PropertyBinding::Value(shadow.color.into()), + color: shadow.color.into(), } } PrimitiveKeyKind::Clear => { @@ -1416,7 +1405,6 @@ pub enum PrimitiveInstanceKind { data_handle: PrimitiveDataHandle, opacity_binding_index: OpacityBindingIndex, segment_instance_index: SegmentInstanceIndex, - color_binding_index: ColorBindingIndex, }, YuvImage { /// Handle to the common interned data for this primitive. @@ -1673,8 +1661,6 @@ pub type TextRunIndex = storage::Index; pub type TextRunStorage = storage::Storage; pub type OpacityBindingIndex = storage::Index; pub type OpacityBindingStorage = storage::Storage; -pub type ColorBindingIndex = storage::Index>; -pub type ColorBindingStorage = storage::Storage>; pub type BorderHandleStorage = storage::Storage; pub type SegmentStorage = storage::Storage; pub type SegmentsRange = storage::Range; @@ -1814,7 +1800,6 @@ pub struct PrimitiveStoreStats { opacity_binding_count: usize, image_count: usize, linear_gradient_count: usize, - color_binding_count: usize, } impl PrimitiveStoreStats { @@ -1825,7 +1810,6 @@ impl PrimitiveStoreStats { opacity_binding_count: 0, image_count: 0, linear_gradient_count: 0, - color_binding_count: 0, } } } @@ -1843,8 +1827,6 @@ pub struct PrimitiveStore { /// List of animated opacity bindings for a primitive. pub opacity_bindings: OpacityBindingStorage, - /// animated color bindings for this primitive. - pub color_bindings: ColorBindingStorage, } impl PrimitiveStore { @@ -1854,7 +1836,6 @@ impl PrimitiveStore { text_runs: TextRunStorage::new(stats.text_run_count), images: ImageInstanceStorage::new(stats.image_count), opacity_bindings: OpacityBindingStorage::new(stats.opacity_binding_count), - color_bindings: ColorBindingStorage::new(stats.color_binding_count), linear_gradients: LinearGradientStorage::new(stats.linear_gradient_count), } } @@ -1866,7 +1847,6 @@ impl PrimitiveStore { image_count: self.images.len(), opacity_binding_count: self.opacity_bindings.len(), linear_gradient_count: self.linear_gradients.len(), - color_binding_count: self.color_bindings.len(), } } @@ -2171,7 +2151,6 @@ impl PrimitiveStore { &self.pictures, frame_state.resource_cache, &self.opacity_bindings, - &self.color_bindings, &self.images, &frame_state.surface_stack, &frame_state.composite_state, @@ -2678,7 +2657,7 @@ impl PrimitiveStore { self.pictures[pic_index.0].requested_composite_mode = None; } - fn prepare_prim_for_render( + pub fn prepare_prim_for_render( &mut self, prim_instance: &mut PrimitiveInstance, prim_spatial_node_index: SpatialNodeIndex, @@ -2997,7 +2976,7 @@ impl PrimitiveStore { // Update the template this instane references, which may refresh the GPU // cache with any shared template data. - prim_data.update(frame_state, frame_context.scene_properties); + prim_data.update(frame_state); } PrimitiveInstanceKind::NormalBorder { data_handle, ref mut cache_handles, .. } => { let prim_data = &mut data_stores.normal_border[*data_handle]; @@ -3087,37 +3066,13 @@ impl PrimitiveStore { // cache with any shared template data. prim_data.kind.update(&mut prim_data.common, frame_state); } - PrimitiveInstanceKind::Rectangle { data_handle, segment_instance_index, opacity_binding_index, color_binding_index, .. } => { + PrimitiveInstanceKind::Rectangle { data_handle, segment_instance_index, opacity_binding_index, .. } => { let prim_data = &mut data_stores.prim[*data_handle]; prim_data.common.may_need_repetition = false; - if *color_binding_index != ColorBindingIndex::INVALID { - match self.color_bindings[*color_binding_index] { - PropertyBinding::Binding(..) => { - // We explicitly invalidate the gpu cache - // if the color is animating. - let gpu_cache_handle = - if *segment_instance_index == SegmentInstanceIndex::INVALID { - None - } else if *segment_instance_index == SegmentInstanceIndex::UNUSED { - Some(&prim_data.common.gpu_cache_handle) - } else { - Some(&scratch.segment_instances[*segment_instance_index].gpu_cache_handle) - }; - if let Some(gpu_cache_handle) = gpu_cache_handle { - frame_state.gpu_cache.invalidate(gpu_cache_handle); - } - } - PropertyBinding::Value(..) => {}, - } - } - // Update the template this instane references, which may refresh the GPU // cache with any shared template data. - prim_data.update( - frame_state, - frame_context.scene_properties, - ); + prim_data.update(frame_state); update_opacity_binding( &mut self.opacity_bindings, @@ -3133,7 +3088,6 @@ impl PrimitiveStore { |request| { prim_data.kind.write_prim_gpu_blocks( request, - frame_context.scene_properties, ); } ); @@ -4352,10 +4306,13 @@ fn update_opacity_binding( opacity_bindings: &mut OpacityBindingStorage, opacity_binding_index: OpacityBindingIndex, scene_properties: &SceneProperties, -) { - if opacity_binding_index != OpacityBindingIndex::INVALID { +) -> f32 { + if opacity_binding_index == OpacityBindingIndex::INVALID { + 1.0 + } else { let binding = &mut opacity_bindings[opacity_binding_index]; binding.update(scene_properties); + binding.current } } @@ -4389,8 +4346,8 @@ fn test_struct_sizes() { // be done with care, and after checking if talos performance regresses badly. assert_eq!(mem::size_of::(), 88, "PrimitiveInstance size changed"); assert_eq!(mem::size_of::(), 40, "PrimitiveInstanceKind size changed"); - assert_eq!(mem::size_of::(), 48, "PrimitiveTemplate size changed"); - assert_eq!(mem::size_of::(), 28, "PrimitiveTemplateKind size changed"); - assert_eq!(mem::size_of::(), 28, "PrimitiveKey size changed"); - assert_eq!(mem::size_of::(), 16, "PrimitiveKeyKind size changed"); + assert_eq!(mem::size_of::(), 40, "PrimitiveTemplate size changed"); + assert_eq!(mem::size_of::(), 20, "PrimitiveTemplateKind size changed"); + assert_eq!(mem::size_of::(), 20, "PrimitiveKey size changed"); + assert_eq!(mem::size_of::(), 5, "PrimitiveKeyKind size changed"); } diff --git a/webrender/src/scene.rs b/webrender/src/scene.rs index 0416ec8f68..f00cf5b7ca 100644 --- a/webrender/src/scene.rs +++ b/webrender/src/scene.rs @@ -22,7 +22,6 @@ use std::sync::Arc; pub struct SceneProperties { transform_properties: FastHashMap, float_properties: FastHashMap, - color_properties: FastHashMap, current_properties: DynamicProperties, pending_properties: Option, } @@ -32,7 +31,6 @@ impl SceneProperties { SceneProperties { transform_properties: FastHashMap::default(), float_properties: FastHashMap::default(), - color_properties: FastHashMap::default(), current_properties: DynamicProperties::default(), pending_properties: None, } @@ -80,11 +78,6 @@ impl SceneProperties { .insert(property.key.id, property.value); } - for property in &pending_properties.colors { - self.color_properties - .insert(property.key.id, property.value); - } - self.current_properties = pending_properties.clone(); properties_changed = true; } @@ -128,27 +121,6 @@ impl SceneProperties { pub fn float_properties(&self) -> &FastHashMap { &self.float_properties } - - /// Get the current value for a color property. - pub fn resolve_color( - &self, - property: &PropertyBinding - ) -> ColorF { - match *property { - PropertyBinding::Value(value) => value, - PropertyBinding::Binding(ref key, v) => { - self.color_properties - .get(&key.id) - .cloned() - .unwrap_or(v) - } - } - } - - pub fn color_properties(&self) -> &FastHashMap { - &self.color_properties - } - } /// A representation of the layout within the display port for a given document or iframe. diff --git a/webrender/src/scene_building.rs b/webrender/src/scene_building.rs index d94cb06bf7..2991c757ea 100644 --- a/webrender/src/scene_building.rs +++ b/webrender/src/scene_building.rs @@ -1214,7 +1214,7 @@ impl<'a> SceneBuilder<'a> { self.add_solid_rectangle( clip_and_scroll, &layout, - PropertyBinding::Value(ColorF::TRANSPARENT), + ColorF::TRANSPARENT, ); } DisplayItem::ClearRectangle(ref info) => { @@ -2749,19 +2749,13 @@ impl<'a> SceneBuilder<'a> { &mut self, clip_and_scroll: ScrollNodeAndClipChain, info: &LayoutPrimitiveInfo, - color: PropertyBinding, + color: ColorF, ) { - match color { - PropertyBinding::Value(value) => { - if value.a == 0.0 { - // Don't add transparent rectangles to the draw list, - // but do consider them for hit testing. This allows - // specifying invisible hit testing areas. - self.add_primitive_to_hit_testing_list(info, clip_and_scroll); - return; - } - }, - PropertyBinding::Binding(..) => {}, + if color.a == 0.0 { + // Don't add transparent rectangles to the draw list, but do consider them for hit + // testing. This allows specifying invisible hit testing areas. + self.add_primitive_to_hit_testing_list(info, clip_and_scroll); + return; } self.add_primitive( diff --git a/webrender_api/src/api.rs b/webrender_api/src/api.rs index 881b6f0613..302ab3d2e9 100644 --- a/webrender_api/src/api.rs +++ b/webrender_api/src/api.rs @@ -1154,7 +1154,7 @@ pub enum PrimitiveKeyKind { /// Rectangle { /// - color: PropertyBinding, + color: ColorU, }, } @@ -1821,7 +1821,7 @@ impl PropertyBindingId { /// A unique key that is used for connecting animated property /// values to bindings in the display list. #[repr(C)] -#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)] +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] pub struct PropertyBindingKey { /// pub id: PropertyBindingId, @@ -1853,7 +1853,7 @@ impl PropertyBindingKey { /// used for the case where the animation is still in-delay phase /// (i.e. the animation doesn't produce any animation values). #[repr(C)] -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)] +#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, PeekPoke)] pub enum PropertyBinding { /// Non-animated value. Value(T), @@ -1873,46 +1873,6 @@ impl From for PropertyBinding { } } -impl From> for PropertyBindingKey { - fn from(key: PropertyBindingKey) -> PropertyBindingKey { - PropertyBindingKey { - id: key.id.clone(), - _phantom: PhantomData, - } - } -} - -impl From> for PropertyBindingKey { - fn from(key: PropertyBindingKey) -> PropertyBindingKey { - PropertyBindingKey { - id: key.id.clone(), - _phantom: PhantomData, - } - } -} - -impl From> for PropertyBinding { - fn from(value: PropertyBinding) -> PropertyBinding { - match value { - PropertyBinding::Value(value) => PropertyBinding::Value(value.into()), - PropertyBinding::Binding(k, v) => { - PropertyBinding::Binding(k.into(), v.into()) - } - } - } -} - -impl From> for PropertyBinding { - fn from(value: PropertyBinding) -> PropertyBinding { - match value { - PropertyBinding::Value(value) => PropertyBinding::Value(value.into()), - PropertyBinding::Binding(k, v) => { - PropertyBinding::Binding(k.into(), v.into()) - } - } - } -} - /// The current value of an animated property. This is /// supplied by the calling code. #[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq)] @@ -1930,10 +1890,8 @@ pub struct PropertyValue { pub struct DynamicProperties { /// pub transforms: Vec>, - /// opacity + /// pub floats: Vec>, - /// background color - pub colors: Vec>, } /// A handler to integrate WebRender with the thread that contains the `Renderer`. diff --git a/webrender_api/src/display_item.rs b/webrender_api/src/display_item.rs index c87249df6d..3a3e1d9ad2 100644 --- a/webrender_api/src/display_item.rs +++ b/webrender_api/src/display_item.rs @@ -299,11 +299,11 @@ pub struct ScrollFrameDisplayItem { pub external_scroll_offset: LayoutVector2D, } -/// A solid or an animating color to draw (may not actually be a rectangle due to complex clips) +/// A solid color to draw (may not actually be a rectangle due to complex clips) #[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] pub struct RectangleDisplayItem { pub common: CommonItemProperties, - pub color: PropertyBinding, + pub color: ColorF, } /// Clears all colors from the area, making it possible to cut holes in the window. diff --git a/webrender_api/src/display_list.rs b/webrender_api/src/display_list.rs index 2f7d3f1aff..8d0a27d0e5 100644 --- a/webrender_api/src/display_list.rs +++ b/webrender_api/src/display_list.rs @@ -1116,19 +1116,7 @@ impl DisplayListBuilder { ) { let item = di::DisplayItem::Rectangle(di::RectangleDisplayItem { common: *common, - color: PropertyBinding::Value(color), - }); - self.push_item(&item); - } - - pub fn push_rect_with_animation( - &mut self, - common: &di::CommonItemProperties, - color: PropertyBinding, - ) { - let item = di::DisplayItem::Rectangle(di::RectangleDisplayItem { - common: *common, - color, + color }); self.push_item(&item); } diff --git a/wrench/src/scene.rs b/wrench/src/scene.rs index 818d000edc..d2e708a9b3 100644 --- a/wrench/src/scene.rs +++ b/wrench/src/scene.rs @@ -14,7 +14,6 @@ use webrender::api::units::{LayoutSize, LayoutTransform}; pub struct SceneProperties { transform_properties: HashMap, float_properties: HashMap, - color_properties: HashMap, } impl SceneProperties { @@ -22,7 +21,6 @@ impl SceneProperties { pub fn set_properties(&mut self, properties: &DynamicProperties) { self.transform_properties.clear(); self.float_properties.clear(); - self.color_properties.clear(); for property in &properties.transforms { self.transform_properties @@ -33,11 +31,6 @@ impl SceneProperties { self.float_properties .insert(property.key.id, property.value); } - - for property in &properties.colors { - self.color_properties - .insert(property.key.id, property.value); - } } /// Get the current value for a transform property. @@ -64,17 +57,6 @@ impl SceneProperties { .unwrap_or(v), } } - - /// Get the current value for a color property. - pub fn resolve_color(&self, property: &PropertyBinding) -> ColorF { - match *property { - PropertyBinding::Value(value) => value, - PropertyBinding::Binding(ref key, v) => self.color_properties - .get(&key.id) - .cloned() - .unwrap_or(v), - } - } } /// A representation of the layout within the display port for a given document or iframe. diff --git a/wrench/src/yaml_frame_writer.rs b/wrench/src/yaml_frame_writer.rs index 6a1da76809..c7b64055f5 100644 --- a/wrench/src/yaml_frame_writer.rs +++ b/wrench/src/yaml_frame_writer.rs @@ -1002,13 +1002,7 @@ impl YamlFrameWriter { DisplayItem::Rectangle(item) => { str_node(&mut v, "type", "rect"); common_node(&mut v, clip_id_mapper, &item.common); - - let key_label = match item.color { - PropertyBinding::Value(..) => "color", - PropertyBinding::Binding(..) => "animating-color", - }; - color_node(&mut v, key_label, - scene.properties.resolve_color(&item.color)); + color_node(&mut v, "color", item.color); } DisplayItem::HitTest(item) => { str_node(&mut v, "type", "hit-test"); From 13e86e25074cf4d6ea30ca851a9c37b2579a62c4 Mon Sep 17 00:00:00 2001 From: Hiroyuki Ikezoe Date: Thu, 27 Feb 2020 21:49:44 +0000 Subject: [PATCH 11/33] Bug 1510030 - Don't return the current opacity value from update_opacity_binding. r=gw The return value is not used at all. Differential Revision: https://phabricator.services.mozilla.com/D63600 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/fa80818e1861f83af3ee2d16de44aca23dd59294 --- webrender/src/prim_store/mod.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/webrender/src/prim_store/mod.rs b/webrender/src/prim_store/mod.rs index 9626d5413f..09c98e2402 100644 --- a/webrender/src/prim_store/mod.rs +++ b/webrender/src/prim_store/mod.rs @@ -4306,13 +4306,10 @@ fn update_opacity_binding( opacity_bindings: &mut OpacityBindingStorage, opacity_binding_index: OpacityBindingIndex, scene_properties: &SceneProperties, -) -> f32 { - if opacity_binding_index == OpacityBindingIndex::INVALID { - 1.0 - } else { +) { + if opacity_binding_index != OpacityBindingIndex::INVALID { let binding = &mut opacity_bindings[opacity_binding_index]; binding.update(scene_properties); - binding.current } } From ecd8232cf21959bd8f31b557c6a11d93c833a612 Mon Sep 17 00:00:00 2001 From: Hiroyuki Ikezoe Date: Thu, 27 Feb 2020 21:49:53 +0000 Subject: [PATCH 12/33] Bug 1510030 - Fix the comment for OpacityBinding::update(). r=gw Differential Revision: https://phabricator.services.mozilla.com/D63601 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/70a094437dfbacf8a110d015b32bdaa6936531c2 --- webrender/src/prim_store/mod.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/webrender/src/prim_store/mod.rs b/webrender/src/prim_store/mod.rs index 09c98e2402..dbcd4f8825 100644 --- a/webrender/src/prim_store/mod.rs +++ b/webrender/src/prim_store/mod.rs @@ -918,8 +918,7 @@ impl OpacityBinding { } // Resolve the current value of each opacity binding, and - // store that as a single combined opacity. Returns true - // if the opacity value changed from last time. + // store that as a single combined opacity. pub fn update(&mut self, scene_properties: &SceneProperties) { let mut new_opacity = 1.0; From b152ac5b4f5102f231e588b8fea66960416a6ef1 Mon Sep 17 00:00:00 2001 From: Hiroyuki Ikezoe Date: Thu, 27 Feb 2020 21:50:03 +0000 Subject: [PATCH 13/33] Bug 1510030 - Make prepare_prim_for_render private. r=gw Differential Revision: https://phabricator.services.mozilla.com/D63603 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/42d17390bcca6e518624bddb755433277dd64ba4 --- webrender/src/prim_store/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webrender/src/prim_store/mod.rs b/webrender/src/prim_store/mod.rs index dbcd4f8825..ed39fc39e2 100644 --- a/webrender/src/prim_store/mod.rs +++ b/webrender/src/prim_store/mod.rs @@ -2656,7 +2656,7 @@ impl PrimitiveStore { self.pictures[pic_index.0].requested_composite_mode = None; } - pub fn prepare_prim_for_render( + fn prepare_prim_for_render( &mut self, prim_instance: &mut PrimitiveInstance, prim_spatial_node_index: SpatialNodeIndex, From 7b1f4fae8369603135f61a42266d80c5e028fb53 Mon Sep 17 00:00:00 2001 From: Hiroyuki Ikezoe Date: Thu, 27 Feb 2020 21:50:14 +0000 Subject: [PATCH 14/33] Bug 1510030 - Implement WebRender backend to run background color animations on the compositor. r=gw,boris Differential Revision: https://phabricator.services.mozilla.com/D63604 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/9092c93a4ac6621e8fb255ae5b386046a0524c0a --- examples/animation.rs | 1 + webrender/src/box_shadow.rs | 5 +- webrender/src/picture.rs | 158 +++++++++++++++++++++++++++--- webrender/src/prim_store/mod.rs | 81 +++++++++++---- webrender/src/scene.rs | 28 ++++++ webrender/src/scene_building.rs | 20 ++-- webrender_api/src/api.rs | 50 +++++++++- webrender_api/src/display_item.rs | 4 +- webrender_api/src/display_list.rs | 14 ++- wrench/src/scene.rs | 18 ++++ wrench/src/yaml_frame_writer.rs | 8 +- 11 files changed, 337 insertions(+), 50 deletions(-) diff --git a/examples/animation.rs b/examples/animation.rs index 4cd7f343ce..d442ab0309 100644 --- a/examples/animation.rs +++ b/examples/animation.rs @@ -187,6 +187,7 @@ impl Example for App { value: self.opacity, } ], + colors: vec![], }, ); txn.generate_frame(); diff --git a/webrender/src/box_shadow.rs b/webrender/src/box_shadow.rs index 124d44d5de..eb8d4d0d4e 100644 --- a/webrender/src/box_shadow.rs +++ b/webrender/src/box_shadow.rs @@ -3,6 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use api::{BorderRadius, BoxShadowClipMode, ClipMode, ColorF, PrimitiveKeyKind}; +use api::PropertyBinding; use api::MAX_BLUR_RADIUS; use api::units::*; use crate::clip::{ClipItemKey, ClipItemKeyKind}; @@ -163,7 +164,7 @@ impl<'a> SceneBuilder<'a> { &LayoutPrimitiveInfo::with_clip_rect(final_prim_rect, prim_info.clip_rect), clips, PrimitiveKeyKind::Rectangle { - color: color.into(), + color: PropertyBinding::Value(color.into()), }, ); } else { @@ -189,7 +190,7 @@ impl<'a> SceneBuilder<'a> { // Draw the box-shadow as a solid rect, using a box-shadow // clip mask item. let prim = PrimitiveKeyKind::Rectangle { - color: color.into(), + color: PropertyBinding::Value(color.into()), }; // Create the box-shadow clip item. diff --git a/webrender/src/picture.rs b/webrender/src/picture.rs index b601793234..e48bb99663 100644 --- a/webrender/src/picture.rs +++ b/webrender/src/picture.rs @@ -96,7 +96,7 @@ use api::{MixBlendMode, PipelineId, PremultipliedColorF, FilterPrimitiveKind}; use api::{PropertyBinding, PropertyBindingId, FilterPrimitive, FontRenderMode}; -use api::{DebugFlags, RasterSpace, ImageKey, ColorF, PrimitiveFlags}; +use api::{DebugFlags, RasterSpace, ImageKey, ColorF, ColorU, PrimitiveFlags}; use api::units::*; use crate::box_shadow::{BLUR_SAMPLE_SCALE}; use crate::clip::{ClipStore, ClipChainInstance, ClipDataHandle, ClipChainId}; @@ -120,6 +120,7 @@ use crate::prim_store::{SpaceMapper, PrimitiveVisibilityMask, PointKey, Primitiv use crate::prim_store::{SpaceSnapper, PictureIndex, PrimitiveInstance, PrimitiveInstanceKind}; use crate::prim_store::{get_raster_rects, PrimitiveScratchBuffer, RectangleKey}; use crate::prim_store::{OpacityBindingStorage, ImageInstanceStorage, OpacityBindingIndex}; +use crate::prim_store::{ColorBindingStorage, ColorBindingIndex}; use crate::print_tree::{PrintTree, PrintTreePrinter}; use crate::render_backend::DataStores; use crate::render_task_graph::RenderTaskId; @@ -264,6 +265,8 @@ pub struct PictureCacheState { spatial_nodes: FastHashMap, /// State of opacity bindings from previous frame opacity_bindings: FastHashMap, + /// State of color bindings from previous frame + color_bindings: FastHashMap, /// The current transform of the picture cache root spatial node root_transform: TransformKey, /// The current tile size in device pixels @@ -278,6 +281,7 @@ pub struct PictureCacheState { pub struct PictureCacheRecycledAllocations { old_opacity_bindings: FastHashMap, + old_color_bindings: FastHashMap, compare_cache: FastHashMap, } @@ -401,33 +405,39 @@ fn clampf(value: f32, low: f32, high: f32) -> f32 { #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct PrimitiveDependencyIndex(pub u32); -/// Information about the state of an opacity binding. +/// Information about the state of a binding. #[derive(Debug)] -pub struct OpacityBindingInfo { +pub struct BindingInfo { /// The current value retrieved from dynamic scene properties. - value: f32, + value: T, /// True if it was changed (or is new) since the last frame build. changed: bool, } -/// Information stored in a tile descriptor for an opacity binding. +/// Information stored in a tile descriptor for a binding. #[derive(Debug, PartialEq, Clone, Copy)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] -pub enum OpacityBinding { - Value(f32), +pub enum Binding { + Value(T), Binding(PropertyBindingId), } -impl From> for OpacityBinding { - fn from(binding: PropertyBinding) -> OpacityBinding { +impl From> for Binding { + fn from(binding: PropertyBinding) -> Binding { match binding { - PropertyBinding::Binding(key, _) => OpacityBinding::Binding(key.id), - PropertyBinding::Value(value) => OpacityBinding::Value(value), + PropertyBinding::Binding(key, _) => Binding::Binding(key.id), + PropertyBinding::Value(value) => Binding::Value(value), } } } +pub type OpacityBinding = Binding; +pub type OpacityBindingInfo = BindingInfo; + +pub type ColorBinding = Binding; +pub type ColorBindingInfo = BindingInfo; + /// Information about the state of a spatial node value #[derive(Debug)] pub struct SpatialNodeDependency { @@ -476,6 +486,9 @@ struct TilePostUpdateContext<'a> { /// Information about opacity bindings from the picture cache. opacity_bindings: &'a FastHashMap, + /// Information about color bindings from the picture cache. + color_bindings: &'a FastHashMap, + /// Current size in device pixels of tiles for this cache current_tile_size: DeviceIntSize, @@ -515,6 +528,9 @@ struct PrimitiveDependencyInfo { /// Opacity bindings this primitive depends on. opacity_bindings: SmallVec<[OpacityBinding; 4]>, + /// Color binding this primitive depends on. + color_binding: Option, + /// Clips that this primitive depends on. clips: SmallVec<[ItemUid; 8]>, @@ -537,6 +553,7 @@ impl PrimitiveDependencyInfo { prim_origin, images: SmallVec::new(), opacity_bindings: SmallVec::new(), + color_binding: None, clip_by_tile: false, prim_clip_rect, clips: SmallVec::new(), @@ -688,6 +705,8 @@ pub enum PrimitiveCompareResult { Image, /// The value of an opacity binding changed OpacityBinding, + /// The value of a color binding changed + ColorBinding, } /// A more detailed version of PrimitiveCompareResult used when @@ -718,7 +737,11 @@ pub enum PrimitiveCompareResultDetail { /// The value of an opacity binding changed OpacityBinding { detail: CompareHelperResult, - } + }, + /// The value of a color binding changed + ColorBinding { + detail: CompareHelperResult, + }, } /// Debugging information about why a tile was invalidated @@ -886,6 +909,7 @@ impl Tile { state.resource_cache, ctx.spatial_nodes, ctx.opacity_bindings, + ctx.color_bindings, ); let mut dirty_rect = PictureRect::zero(); @@ -1054,6 +1078,12 @@ impl Tile { // Include any transforms that this primitive depends on. self.current_descriptor.transforms.extend_from_slice(&info.spatial_nodes); + // Include any color bindings this primitive depends on. + if info.color_binding.is_some() { + self.current_descriptor.color_bindings.insert( + self.current_descriptor.color_bindings.len(), info.color_binding.unwrap()); + } + // TODO(gw): The origin of background rects produced by APZ changes // in Gecko during scrolling. Consider investigating this so the // hack / workaround below is not required. @@ -1106,6 +1136,7 @@ impl Tile { clip_dep_count: info.clips.len() as u8, image_dep_count: info.images.len() as u8, opacity_binding_dep_count: info.opacity_bindings.len() as u8, + color_binding_dep_count: if info.color_binding.is_some() { 1 } else { 0 } as u8, }); // Add this primitive to the dirty rect quadtree. @@ -1317,6 +1348,7 @@ pub struct PrimitiveDescriptor { image_dep_count: u8, opacity_binding_dep_count: u8, clip_dep_count: u8, + color_binding_dep_count: u8, } impl PartialEq for PrimitiveDescriptor { @@ -1467,6 +1499,10 @@ pub struct TileDescriptor { /// Picture space rect that contains valid pixels region of this tile. local_valid_rect: PictureRect, + + /// List of the effects of color that we care about + /// tracking for this tile. + color_bindings: Vec, } impl TileDescriptor { @@ -1478,6 +1514,7 @@ impl TileDescriptor { images: Vec::new(), transforms: Vec::new(), local_valid_rect: PictureRect::zero(), + color_bindings: Vec::new(), } } @@ -1495,11 +1532,12 @@ impl TileDescriptor { prim.prim_clip_rect.w, prim.prim_clip_rect.h, )); - pt.add_item(format!("deps: t={} i={} o={} c={}", + pt.add_item(format!("deps: t={} i={} o={} c={} color={}", prim.transform_dep_count, prim.image_dep_count, prim.opacity_binding_dep_count, prim.clip_dep_count, + prim.color_binding_dep_count, )); pt.end_level(); } @@ -1542,6 +1580,15 @@ impl TileDescriptor { pt.end_level(); } + if !self.color_bindings.is_empty() { + pt.new_level("color_bindings".to_string()); + for color_binding in &self.color_bindings { + pt.new_level(format!("binding={:?}", color_binding)); + pt.end_level(); + } + pt.end_level(); + } + pt.end_level(); } @@ -1554,6 +1601,7 @@ impl TileDescriptor { self.images.clear(); self.transforms.clear(); self.local_valid_rect = PictureRect::zero(); + self.color_bindings.clear(); } } @@ -2026,6 +2074,11 @@ pub struct TileCacheInstance { /// calculate invalid relative transforms when building the spatial /// nodes hash above. used_spatial_nodes: FastHashSet, + /// List of color bindings, with some extra information + /// about whether they changed since last frame. + color_bindings: FastHashMap, + /// Switch back and forth between old and new bindings hashmaps to avoid re-allocating. + old_color_bindings: FastHashMap, /// The current dirty region tracker for this picture. pub dirty_region: DirtyRegion, /// Current size of tiles in picture units. @@ -2114,6 +2167,8 @@ impl TileCacheInstance { spatial_nodes: FastHashMap::default(), old_spatial_nodes: FastHashMap::default(), used_spatial_nodes: FastHashSet::default(), + color_bindings: FastHashMap::default(), + old_color_bindings: FastHashMap::default(), dirty_region: DirtyRegion::new(), tile_size: PictureSize::zero(), tile_rect: TileRect::zero(), @@ -2175,7 +2230,7 @@ impl TileCacheInstance { (p0, p1) } - /// Update transforms, opacity bindings and tile rects. + /// Update transforms, opacity, color bindings and tile rects. pub fn pre_update( &mut self, pic_rect: PictureRect, @@ -2257,6 +2312,7 @@ impl TileCacheInstance { self.root_transform = prev_state.root_transform; self.spatial_nodes = prev_state.spatial_nodes; self.opacity_bindings = prev_state.opacity_bindings; + self.color_bindings = prev_state.color_bindings; self.current_tile_size = prev_state.current_tile_size; self.native_surface_id = prev_state.native_surface_id; self.is_opaque = prev_state.is_opaque; @@ -2280,6 +2336,11 @@ impl TileCacheInstance { &mut self.old_opacity_bindings, prev_state.allocations.old_opacity_bindings, ); + recycle_map( + self.color_bindings.len(), + &mut self.old_color_bindings, + prev_state.allocations.old_color_bindings, + ); recycle_map( prev_state.allocations.compare_cache.len(), &mut self.compare_cache, @@ -2380,6 +2441,23 @@ impl TileCacheInstance { }); } + // Do a hacky diff of color binding values from the last frame. This is + // used later on during tile invalidation tests. + let current_properties = frame_context.scene_properties.color_properties(); + mem::swap(&mut self.color_bindings, &mut self.old_color_bindings); + + self.color_bindings.clear(); + for (id, value) in current_properties { + let changed = match self.old_color_bindings.get(id) { + Some(old_property) => old_property.value != (*value).into(), + None => true, + }; + self.color_bindings.insert(*id, ColorBindingInfo { + value: (*value).into(), + changed, + }); + } + let world_tile_size = WorldSize::new( self.current_tile_size.width as f32 / frame_context.global_device_pixel_scale.0, self.current_tile_size.height as f32 / frame_context.global_device_pixel_scale.0, @@ -2539,6 +2617,7 @@ impl TileCacheInstance { pictures: &[PicturePrimitive], resource_cache: &ResourceCache, opacity_binding_store: &OpacityBindingStorage, + color_bindings: &ColorBindingStorage, image_instances: &ImageInstanceStorage, surface_stack: &[SurfaceIndex], composite_state: &CompositeState, @@ -2675,13 +2754,15 @@ impl TileCacheInstance { prim_info.opacity_bindings.push(binding.into()); } } - PrimitiveInstanceKind::Rectangle { data_handle, opacity_binding_index, .. } => { + PrimitiveInstanceKind::Rectangle { data_handle, opacity_binding_index, color_binding_index, .. } => { if opacity_binding_index == OpacityBindingIndex::INVALID { // Rectangles can only form a backdrop candidate if they are known opaque. // TODO(gw): We could resolve the opacity binding here, but the common // case for background rects is that they don't have animated opacity. let color = match data_stores.prim[data_handle].kind { - PrimitiveTemplateKind::Rectangle { color, .. } => color, + PrimitiveTemplateKind::Rectangle { color, .. } => { + frame_context.scene_properties.resolve_color(&color) + } _ => unreachable!(), }; if color.a >= 1.0 { @@ -2694,6 +2775,10 @@ impl TileCacheInstance { } } + if color_binding_index != ColorBindingIndex::INVALID { + prim_info.color_binding = Some(color_bindings[color_binding_index].into()); + } + prim_info.clip_by_tile = true; } PrimitiveInstanceKind::Image { data_handle, image_instance_index, .. } => { @@ -3095,6 +3180,7 @@ impl TileCacheInstance { backdrop: self.backdrop, spatial_nodes: &self.spatial_nodes, opacity_bindings: &self.opacity_bindings, + color_bindings: &self.color_bindings, current_tile_size: self.current_tile_size, local_rect: self.local_rect, }; @@ -3978,12 +4064,14 @@ impl PicturePrimitive { tiles: tile_cache.tiles, spatial_nodes: tile_cache.spatial_nodes, opacity_bindings: tile_cache.opacity_bindings, + color_bindings: tile_cache.color_bindings, root_transform: tile_cache.root_transform, current_tile_size: tile_cache.current_tile_size, native_surface_id: tile_cache.native_surface_id.take(), is_opaque: tile_cache.is_opaque, allocations: PictureCacheRecycledAllocations { old_opacity_bindings: tile_cache.old_opacity_bindings, + old_color_bindings: tile_cache.old_color_bindings, compare_cache: tile_cache.compare_cache, }, }, @@ -5538,9 +5626,11 @@ struct PrimitiveComparer<'a> { transform_comparer: CompareHelper<'a, SpatialNodeIndex>, image_comparer: CompareHelper<'a, ImageDependency>, opacity_comparer: CompareHelper<'a, OpacityBinding>, + color_comparer: CompareHelper<'a, ColorBinding>, resource_cache: &'a ResourceCache, spatial_nodes: &'a FastHashMap, opacity_bindings: &'a FastHashMap, + color_bindings: &'a FastHashMap, } impl<'a> PrimitiveComparer<'a> { @@ -5550,6 +5640,7 @@ impl<'a> PrimitiveComparer<'a> { resource_cache: &'a ResourceCache, spatial_nodes: &'a FastHashMap, opacity_bindings: &'a FastHashMap, + color_bindings: &'a FastHashMap, ) -> Self { let clip_comparer = CompareHelper::new( &prev.clips, @@ -5571,14 +5662,21 @@ impl<'a> PrimitiveComparer<'a> { &curr.opacity_bindings, ); + let color_comparer = CompareHelper::new( + &prev.color_bindings, + &curr.color_bindings, + ); + PrimitiveComparer { clip_comparer, transform_comparer, image_comparer, opacity_comparer, + color_comparer, resource_cache, spatial_nodes, opacity_bindings, + color_bindings, } } @@ -5587,6 +5685,7 @@ impl<'a> PrimitiveComparer<'a> { self.transform_comparer.reset(); self.image_comparer.reset(); self.opacity_comparer.reset(); + self.color_comparer.reset(); } fn advance_prev(&mut self, prim: &PrimitiveDescriptor) { @@ -5594,6 +5693,7 @@ impl<'a> PrimitiveComparer<'a> { self.transform_comparer.advance_prev(prim.transform_dep_count); self.image_comparer.advance_prev(prim.image_dep_count); self.opacity_comparer.advance_prev(prim.opacity_binding_dep_count); + self.color_comparer.advance_prev(prim.color_binding_dep_count); } fn advance_curr(&mut self, prim: &PrimitiveDescriptor) { @@ -5601,6 +5701,7 @@ impl<'a> PrimitiveComparer<'a> { self.transform_comparer.advance_curr(prim.transform_dep_count); self.image_comparer.advance_curr(prim.image_dep_count); self.opacity_comparer.advance_curr(prim.opacity_binding_dep_count); + self.color_comparer.advance_curr(prim.color_binding_dep_count); } /// Check if two primitive descriptors are the same. @@ -5613,6 +5714,7 @@ impl<'a> PrimitiveComparer<'a> { let resource_cache = self.resource_cache; let spatial_nodes = self.spatial_nodes; let opacity_bindings = self.opacity_bindings; + let color_bindings = self.color_bindings; // Check equality of the PrimitiveDescriptor if prev != curr { @@ -5692,6 +5794,30 @@ impl<'a> PrimitiveComparer<'a> { return PrimitiveCompareResult::OpacityBinding; } + // Check if any of the color bindings this prim has are different. + let mut bind_result = CompareHelperResult::Equal; + if !self.color_comparer.is_same( + prev.color_binding_dep_count, + curr.color_binding_dep_count, + |curr| { + if let ColorBinding::Binding(id) = curr { + if color_bindings + .get(id) + .map_or(true, |info| info.changed) { + return true; + } + } + + true + }, + if opt_detail.is_some() { Some(&mut bind_result) } else { None }, + ) { + if let Some(detail) = opt_detail { + *detail = PrimitiveCompareResultDetail::ColorBinding{ detail: bind_result }; + } + return PrimitiveCompareResult::ColorBinding; + } + PrimitiveCompareResult::Equal } } diff --git a/webrender/src/prim_store/mod.rs b/webrender/src/prim_store/mod.rs index ed39fc39e2..63444097a2 100644 --- a/webrender/src/prim_store/mod.rs +++ b/webrender/src/prim_store/mod.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::{BorderRadius, ClipMode, ColorF}; +use api::{BorderRadius, ClipMode, ColorF, ColorU}; use api::{ImageRendering, RepeatMode, PrimitiveFlags}; use api::{PremultipliedColorF, PropertyBinding, Shadow, GradientStop}; use api::{BoxShadowClipMode, LineStyle, LineOrientation, BorderStyle}; @@ -720,7 +720,7 @@ impl intern::InternDebug for PrimitiveKey {} #[derive(MallocSizeOf)] pub enum PrimitiveTemplateKind { Rectangle { - color: ColorF, + color: PropertyBinding, }, Clear, } @@ -812,7 +812,8 @@ impl PrimitiveTemplateKind { /// Write any GPU blocks for the primitive template to the given request object. fn write_prim_gpu_blocks( &self, - request: &mut GpuDataRequest + request: &mut GpuDataRequest, + scene_properties: &SceneProperties, ) { match *self { PrimitiveTemplateKind::Clear => { @@ -820,7 +821,7 @@ impl PrimitiveTemplateKind { request.push(PremultipliedColorF::BLACK); } PrimitiveTemplateKind::Rectangle { ref color, .. } => { - request.push(color.premultiplied()); + request.push(scene_properties.resolve_color(color).premultiplied()) } } } @@ -834,9 +835,10 @@ impl PrimitiveTemplate { pub fn update( &mut self, frame_state: &mut FrameBuildingState, + scene_properties: &SceneProperties, ) { if let Some(mut request) = frame_state.gpu_cache.request(&mut self.common.gpu_cache_handle) { - self.kind.write_prim_gpu_blocks(&mut request); + self.kind.write_prim_gpu_blocks(&mut request, scene_properties); } self.opacity = match self.kind { @@ -844,7 +846,7 @@ impl PrimitiveTemplate { PrimitiveOpacity::translucent() } PrimitiveTemplateKind::Rectangle { ref color, .. } => { - PrimitiveOpacity::from_alpha(color.a) + PrimitiveOpacity::from_alpha(scene_properties.resolve_color(color).a) } }; } @@ -873,7 +875,7 @@ impl InternablePrimitive for PrimitiveKeyKind { fn make_instance_kind( key: PrimitiveKey, data_handle: PrimitiveDataHandle, - _: &mut PrimitiveStore, + prim_store: &mut PrimitiveStore, _reference_frame_relative_offset: LayoutVector2D, ) -> PrimitiveInstanceKind { match key.kind { @@ -882,11 +884,18 @@ impl InternablePrimitive for PrimitiveKeyKind { data_handle } } - PrimitiveKeyKind::Rectangle { .. } => { + PrimitiveKeyKind::Rectangle { color, .. } => { + let color_binding_index = match color { + PropertyBinding::Binding(..) => { + prim_store.color_bindings.push(color) + } + PropertyBinding::Value(..) => ColorBindingIndex::INVALID, + }; PrimitiveInstanceKind::Rectangle { data_handle, opacity_binding_index: OpacityBindingIndex::INVALID, segment_instance_index: SegmentInstanceIndex::INVALID, + color_binding_index, } } } @@ -1326,7 +1335,10 @@ impl IsVisible for PrimitiveKeyKind { true } PrimitiveKeyKind::Rectangle { ref color, .. } => { - color.a > 0 + match *color { + PropertyBinding::Value(value) => value.a > 0, + PropertyBinding::Binding(..) => true, + } } } } @@ -1343,7 +1355,7 @@ impl CreateShadow for PrimitiveKeyKind { match *self { PrimitiveKeyKind::Rectangle { .. } => { PrimitiveKeyKind::Rectangle { - color: shadow.color.into(), + color: PropertyBinding::Value(shadow.color.into()), } } PrimitiveKeyKind::Clear => { @@ -1404,6 +1416,7 @@ pub enum PrimitiveInstanceKind { data_handle: PrimitiveDataHandle, opacity_binding_index: OpacityBindingIndex, segment_instance_index: SegmentInstanceIndex, + color_binding_index: ColorBindingIndex, }, YuvImage { /// Handle to the common interned data for this primitive. @@ -1660,6 +1673,8 @@ pub type TextRunIndex = storage::Index; pub type TextRunStorage = storage::Storage; pub type OpacityBindingIndex = storage::Index; pub type OpacityBindingStorage = storage::Storage; +pub type ColorBindingIndex = storage::Index>; +pub type ColorBindingStorage = storage::Storage>; pub type BorderHandleStorage = storage::Storage; pub type SegmentStorage = storage::Storage; pub type SegmentsRange = storage::Range; @@ -1799,6 +1814,7 @@ pub struct PrimitiveStoreStats { opacity_binding_count: usize, image_count: usize, linear_gradient_count: usize, + color_binding_count: usize, } impl PrimitiveStoreStats { @@ -1809,6 +1825,7 @@ impl PrimitiveStoreStats { opacity_binding_count: 0, image_count: 0, linear_gradient_count: 0, + color_binding_count: 0, } } } @@ -1826,6 +1843,8 @@ pub struct PrimitiveStore { /// List of animated opacity bindings for a primitive. pub opacity_bindings: OpacityBindingStorage, + /// animated color bindings for this primitive. + pub color_bindings: ColorBindingStorage, } impl PrimitiveStore { @@ -1835,6 +1854,7 @@ impl PrimitiveStore { text_runs: TextRunStorage::new(stats.text_run_count), images: ImageInstanceStorage::new(stats.image_count), opacity_bindings: OpacityBindingStorage::new(stats.opacity_binding_count), + color_bindings: ColorBindingStorage::new(stats.color_binding_count), linear_gradients: LinearGradientStorage::new(stats.linear_gradient_count), } } @@ -1846,6 +1866,7 @@ impl PrimitiveStore { image_count: self.images.len(), opacity_binding_count: self.opacity_bindings.len(), linear_gradient_count: self.linear_gradients.len(), + color_binding_count: self.color_bindings.len(), } } @@ -2150,6 +2171,7 @@ impl PrimitiveStore { &self.pictures, frame_state.resource_cache, &self.opacity_bindings, + &self.color_bindings, &self.images, &frame_state.surface_stack, &frame_state.composite_state, @@ -2975,7 +2997,7 @@ impl PrimitiveStore { // Update the template this instane references, which may refresh the GPU // cache with any shared template data. - prim_data.update(frame_state); + prim_data.update(frame_state, frame_context.scene_properties); } PrimitiveInstanceKind::NormalBorder { data_handle, ref mut cache_handles, .. } => { let prim_data = &mut data_stores.normal_border[*data_handle]; @@ -3065,13 +3087,37 @@ impl PrimitiveStore { // cache with any shared template data. prim_data.kind.update(&mut prim_data.common, frame_state); } - PrimitiveInstanceKind::Rectangle { data_handle, segment_instance_index, opacity_binding_index, .. } => { + PrimitiveInstanceKind::Rectangle { data_handle, segment_instance_index, opacity_binding_index, color_binding_index, .. } => { let prim_data = &mut data_stores.prim[*data_handle]; prim_data.common.may_need_repetition = false; + if *color_binding_index != ColorBindingIndex::INVALID { + match self.color_bindings[*color_binding_index] { + PropertyBinding::Binding(..) => { + // We explicitly invalidate the gpu cache + // if the color is animating. + let gpu_cache_handle = + if *segment_instance_index == SegmentInstanceIndex::INVALID { + None + } else if *segment_instance_index == SegmentInstanceIndex::UNUSED { + Some(&prim_data.common.gpu_cache_handle) + } else { + Some(&scratch.segment_instances[*segment_instance_index].gpu_cache_handle) + }; + if let Some(gpu_cache_handle) = gpu_cache_handle { + frame_state.gpu_cache.invalidate(gpu_cache_handle); + } + } + PropertyBinding::Value(..) => {}, + } + } + // Update the template this instane references, which may refresh the GPU // cache with any shared template data. - prim_data.update(frame_state); + prim_data.update( + frame_state, + frame_context.scene_properties, + ); update_opacity_binding( &mut self.opacity_bindings, @@ -3087,6 +3133,7 @@ impl PrimitiveStore { |request| { prim_data.kind.write_prim_gpu_blocks( request, + frame_context.scene_properties, ); } ); @@ -4342,8 +4389,8 @@ fn test_struct_sizes() { // be done with care, and after checking if talos performance regresses badly. assert_eq!(mem::size_of::(), 88, "PrimitiveInstance size changed"); assert_eq!(mem::size_of::(), 40, "PrimitiveInstanceKind size changed"); - assert_eq!(mem::size_of::(), 40, "PrimitiveTemplate size changed"); - assert_eq!(mem::size_of::(), 20, "PrimitiveTemplateKind size changed"); - assert_eq!(mem::size_of::(), 20, "PrimitiveKey size changed"); - assert_eq!(mem::size_of::(), 5, "PrimitiveKeyKind size changed"); + assert_eq!(mem::size_of::(), 48, "PrimitiveTemplate size changed"); + assert_eq!(mem::size_of::(), 28, "PrimitiveTemplateKind size changed"); + assert_eq!(mem::size_of::(), 28, "PrimitiveKey size changed"); + assert_eq!(mem::size_of::(), 16, "PrimitiveKeyKind size changed"); } diff --git a/webrender/src/scene.rs b/webrender/src/scene.rs index f00cf5b7ca..0416ec8f68 100644 --- a/webrender/src/scene.rs +++ b/webrender/src/scene.rs @@ -22,6 +22,7 @@ use std::sync::Arc; pub struct SceneProperties { transform_properties: FastHashMap, float_properties: FastHashMap, + color_properties: FastHashMap, current_properties: DynamicProperties, pending_properties: Option, } @@ -31,6 +32,7 @@ impl SceneProperties { SceneProperties { transform_properties: FastHashMap::default(), float_properties: FastHashMap::default(), + color_properties: FastHashMap::default(), current_properties: DynamicProperties::default(), pending_properties: None, } @@ -78,6 +80,11 @@ impl SceneProperties { .insert(property.key.id, property.value); } + for property in &pending_properties.colors { + self.color_properties + .insert(property.key.id, property.value); + } + self.current_properties = pending_properties.clone(); properties_changed = true; } @@ -121,6 +128,27 @@ impl SceneProperties { pub fn float_properties(&self) -> &FastHashMap { &self.float_properties } + + /// Get the current value for a color property. + pub fn resolve_color( + &self, + property: &PropertyBinding + ) -> ColorF { + match *property { + PropertyBinding::Value(value) => value, + PropertyBinding::Binding(ref key, v) => { + self.color_properties + .get(&key.id) + .cloned() + .unwrap_or(v) + } + } + } + + pub fn color_properties(&self) -> &FastHashMap { + &self.color_properties + } + } /// A representation of the layout within the display port for a given document or iframe. diff --git a/webrender/src/scene_building.rs b/webrender/src/scene_building.rs index 2991c757ea..d94cb06bf7 100644 --- a/webrender/src/scene_building.rs +++ b/webrender/src/scene_building.rs @@ -1214,7 +1214,7 @@ impl<'a> SceneBuilder<'a> { self.add_solid_rectangle( clip_and_scroll, &layout, - ColorF::TRANSPARENT, + PropertyBinding::Value(ColorF::TRANSPARENT), ); } DisplayItem::ClearRectangle(ref info) => { @@ -2749,13 +2749,19 @@ impl<'a> SceneBuilder<'a> { &mut self, clip_and_scroll: ScrollNodeAndClipChain, info: &LayoutPrimitiveInfo, - color: ColorF, + color: PropertyBinding, ) { - if color.a == 0.0 { - // Don't add transparent rectangles to the draw list, but do consider them for hit - // testing. This allows specifying invisible hit testing areas. - self.add_primitive_to_hit_testing_list(info, clip_and_scroll); - return; + match color { + PropertyBinding::Value(value) => { + if value.a == 0.0 { + // Don't add transparent rectangles to the draw list, + // but do consider them for hit testing. This allows + // specifying invisible hit testing areas. + self.add_primitive_to_hit_testing_list(info, clip_and_scroll); + return; + } + }, + PropertyBinding::Binding(..) => {}, } self.add_primitive( diff --git a/webrender_api/src/api.rs b/webrender_api/src/api.rs index 302ab3d2e9..881b6f0613 100644 --- a/webrender_api/src/api.rs +++ b/webrender_api/src/api.rs @@ -1154,7 +1154,7 @@ pub enum PrimitiveKeyKind { /// Rectangle { /// - color: ColorU, + color: PropertyBinding, }, } @@ -1821,7 +1821,7 @@ impl PropertyBindingId { /// A unique key that is used for connecting animated property /// values to bindings in the display list. #[repr(C)] -#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)] pub struct PropertyBindingKey { /// pub id: PropertyBindingId, @@ -1853,7 +1853,7 @@ impl PropertyBindingKey { /// used for the case where the animation is still in-delay phase /// (i.e. the animation doesn't produce any animation values). #[repr(C)] -#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, PeekPoke)] +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)] pub enum PropertyBinding { /// Non-animated value. Value(T), @@ -1873,6 +1873,46 @@ impl From for PropertyBinding { } } +impl From> for PropertyBindingKey { + fn from(key: PropertyBindingKey) -> PropertyBindingKey { + PropertyBindingKey { + id: key.id.clone(), + _phantom: PhantomData, + } + } +} + +impl From> for PropertyBindingKey { + fn from(key: PropertyBindingKey) -> PropertyBindingKey { + PropertyBindingKey { + id: key.id.clone(), + _phantom: PhantomData, + } + } +} + +impl From> for PropertyBinding { + fn from(value: PropertyBinding) -> PropertyBinding { + match value { + PropertyBinding::Value(value) => PropertyBinding::Value(value.into()), + PropertyBinding::Binding(k, v) => { + PropertyBinding::Binding(k.into(), v.into()) + } + } + } +} + +impl From> for PropertyBinding { + fn from(value: PropertyBinding) -> PropertyBinding { + match value { + PropertyBinding::Value(value) => PropertyBinding::Value(value.into()), + PropertyBinding::Binding(k, v) => { + PropertyBinding::Binding(k.into(), v.into()) + } + } + } +} + /// The current value of an animated property. This is /// supplied by the calling code. #[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq)] @@ -1890,8 +1930,10 @@ pub struct PropertyValue { pub struct DynamicProperties { /// pub transforms: Vec>, - /// + /// opacity pub floats: Vec>, + /// background color + pub colors: Vec>, } /// A handler to integrate WebRender with the thread that contains the `Renderer`. diff --git a/webrender_api/src/display_item.rs b/webrender_api/src/display_item.rs index 3a3e1d9ad2..c87249df6d 100644 --- a/webrender_api/src/display_item.rs +++ b/webrender_api/src/display_item.rs @@ -299,11 +299,11 @@ pub struct ScrollFrameDisplayItem { pub external_scroll_offset: LayoutVector2D, } -/// A solid color to draw (may not actually be a rectangle due to complex clips) +/// A solid or an animating color to draw (may not actually be a rectangle due to complex clips) #[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] pub struct RectangleDisplayItem { pub common: CommonItemProperties, - pub color: ColorF, + pub color: PropertyBinding, } /// Clears all colors from the area, making it possible to cut holes in the window. diff --git a/webrender_api/src/display_list.rs b/webrender_api/src/display_list.rs index 8d0a27d0e5..2f7d3f1aff 100644 --- a/webrender_api/src/display_list.rs +++ b/webrender_api/src/display_list.rs @@ -1116,7 +1116,19 @@ impl DisplayListBuilder { ) { let item = di::DisplayItem::Rectangle(di::RectangleDisplayItem { common: *common, - color + color: PropertyBinding::Value(color), + }); + self.push_item(&item); + } + + pub fn push_rect_with_animation( + &mut self, + common: &di::CommonItemProperties, + color: PropertyBinding, + ) { + let item = di::DisplayItem::Rectangle(di::RectangleDisplayItem { + common: *common, + color, }); self.push_item(&item); } diff --git a/wrench/src/scene.rs b/wrench/src/scene.rs index d2e708a9b3..818d000edc 100644 --- a/wrench/src/scene.rs +++ b/wrench/src/scene.rs @@ -14,6 +14,7 @@ use webrender::api::units::{LayoutSize, LayoutTransform}; pub struct SceneProperties { transform_properties: HashMap, float_properties: HashMap, + color_properties: HashMap, } impl SceneProperties { @@ -21,6 +22,7 @@ impl SceneProperties { pub fn set_properties(&mut self, properties: &DynamicProperties) { self.transform_properties.clear(); self.float_properties.clear(); + self.color_properties.clear(); for property in &properties.transforms { self.transform_properties @@ -31,6 +33,11 @@ impl SceneProperties { self.float_properties .insert(property.key.id, property.value); } + + for property in &properties.colors { + self.color_properties + .insert(property.key.id, property.value); + } } /// Get the current value for a transform property. @@ -57,6 +64,17 @@ impl SceneProperties { .unwrap_or(v), } } + + /// Get the current value for a color property. + pub fn resolve_color(&self, property: &PropertyBinding) -> ColorF { + match *property { + PropertyBinding::Value(value) => value, + PropertyBinding::Binding(ref key, v) => self.color_properties + .get(&key.id) + .cloned() + .unwrap_or(v), + } + } } /// A representation of the layout within the display port for a given document or iframe. diff --git a/wrench/src/yaml_frame_writer.rs b/wrench/src/yaml_frame_writer.rs index c7b64055f5..6a1da76809 100644 --- a/wrench/src/yaml_frame_writer.rs +++ b/wrench/src/yaml_frame_writer.rs @@ -1002,7 +1002,13 @@ impl YamlFrameWriter { DisplayItem::Rectangle(item) => { str_node(&mut v, "type", "rect"); common_node(&mut v, clip_id_mapper, &item.common); - color_node(&mut v, "color", item.color); + + let key_label = match item.color { + PropertyBinding::Value(..) => "color", + PropertyBinding::Binding(..) => "animating-color", + }; + color_node(&mut v, key_label, + scene.properties.resolve_color(&item.color)); } DisplayItem::HitTest(item) => { str_node(&mut v, "type", "hit-test"); From 1ee6c0fd5db903987e5af785b8777db23cadf7c2 Mon Sep 17 00:00:00 2001 From: Jonathan Kew Date: Sat, 29 Feb 2020 04:14:11 +0000 Subject: [PATCH 15/33] Bug 1616995 - patch 2 - Support vertical skew for upright-vertical fonts in webrender. r=lsalzman Depends on D63893 Differential Revision: https://phabricator.services.mozilla.com/D64746 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/793e9dfa4b9fa40137cdf2a5d34c126d6cc9840d --- webrender/src/glyph_rasterizer/mod.rs | 31 ++++++++++++++++++++------ webrender/src/platform/macos/font.rs | 28 ++++++++++++++--------- webrender/src/platform/unix/font.rs | 22 +++++++++++++----- webrender/src/platform/windows/font.rs | 16 ++++++++----- webrender_api/src/font.rs | 1 + 5 files changed, 71 insertions(+), 27 deletions(-) diff --git a/webrender/src/glyph_rasterizer/mod.rs b/webrender/src/glyph_rasterizer/mod.rs index 2c89ba191b..e180ea9290 100644 --- a/webrender/src/glyph_rasterizer/mod.rs +++ b/webrender/src/glyph_rasterizer/mod.rs @@ -338,14 +338,27 @@ impl FontTransform { self.pre_scale(x_scale.recip() as f32, y_scale.recip() as f32) } - pub fn synthesize_italics(&self, angle: SyntheticItalics) -> Self { + pub fn synthesize_italics(&self, angle: SyntheticItalics, size: f64, vertical: bool) -> (Self, (f64, f64)) { let skew_factor = angle.to_skew(); - FontTransform::new( - self.scale_x, - self.skew_x - self.scale_x * skew_factor, - self.skew_y, - self.scale_y - self.skew_y * skew_factor, - ) + if vertical { + // origin delta to be applied so that we effectively skew around + // the middle rather than edge of the glyph + let (tx, ty) = (0.0, -size * 0.5 * skew_factor as f64); + (FontTransform::new( + self.scale_x + self.skew_x * skew_factor, + self.skew_x, + self.skew_y + self.scale_y * skew_factor, + self.scale_y, + ), (self.scale_x as f64 * tx + self.skew_x as f64 * ty, + self.skew_y as f64 * tx + self.scale_y as f64 * ty)) + } else { + (FontTransform::new( + self.scale_x, + self.skew_x - self.scale_x * skew_factor, + self.skew_y, + self.scale_y - self.skew_y * skew_factor, + ), (0.0, 0.0)) + } } pub fn swap_xy(&self) -> Self { @@ -552,6 +565,10 @@ impl FontInstance { 0 } } + + pub fn synthesize_italics(&self, transform: FontTransform, size: f64) -> (FontTransform, (f64, f64)) { + transform.synthesize_italics(self.synthetic_italics, size, self.flags.contains(FontInstanceFlags::VERTICAL)) + } } #[repr(u32)] diff --git a/webrender/src/platform/macos/font.rs b/webrender/src/platform/macos/font.rs index 73c051db39..e3f0c342bf 100644 --- a/webrender/src/platform/macos/font.rs +++ b/webrender/src/platform/macos/font.rs @@ -385,17 +385,21 @@ impl FontContext { if font.flags.contains(FontInstanceFlags::TRANSPOSE) { shape = shape.swap_xy(); } + let (mut tx, mut ty) = (0.0, 0.0); if font.synthetic_italics.is_enabled() { - shape = shape.synthesize_italics(font.synthetic_italics); + let (shape_, (tx_, ty_)) = font.synthesize_italics(shape, size.to_f64_px()); + shape = shape_; + tx = tx_; + ty = ty_; } - let transform = if !shape.is_identity() { + let transform = if !shape.is_identity() || (tx, ty) != (0.0, 0.0) { Some(CGAffineTransform { a: shape.scale_x as f64, b: -shape.skew_y as f64, c: -shape.skew_x as f64, d: shape.scale_y as f64, - tx: 0.0, - ty: 0.0, + tx: tx, + ty: -ty, }) } else { None @@ -521,17 +525,21 @@ impl FontContext { if font.flags.contains(FontInstanceFlags::TRANSPOSE) { shape = shape.swap_xy(); } + let (mut tx, mut ty) = (0.0, 0.0); if font.synthetic_italics.is_enabled() { - shape = shape.synthesize_italics(font.synthetic_italics); + let (shape_, (tx_, ty_)) = font.synthesize_italics(shape, size.to_f64_px()); + shape = shape_; + tx = tx_; + ty = ty_; } - let transform = if !shape.is_identity() { + let transform = if !shape.is_identity() || (tx, ty) != (0.0, 0.0) { Some(CGAffineTransform { a: shape.scale_x as f64, b: -shape.skew_y as f64, c: -shape.skew_x as f64, d: shape.scale_y as f64, - tx: 0.0, - ty: 0.0, + tx: tx, + ty: -ty, }) } else { None @@ -640,8 +648,8 @@ impl FontContext { // CG Origin is bottom left, WR is top left. Need -y offset let mut draw_origin = CGPoint { - x: -metrics.rasterized_left as f64 + x_offset, - y: metrics.rasterized_descent as f64 - y_offset, + x: -metrics.rasterized_left as f64 + x_offset + tx, + y: metrics.rasterized_descent as f64 - y_offset - ty, }; if let Some(transform) = transform { diff --git a/webrender/src/platform/unix/font.rs b/webrender/src/platform/unix/font.rs index a98068bcf1..e34aad4ea3 100644 --- a/webrender/src/platform/unix/font.rs +++ b/webrender/src/platform/unix/font.rs @@ -12,7 +12,7 @@ use freetype::freetype::{FT_F26Dot6, FT_Face, FT_Glyph_Format, FT_Long, FT_UInt} use freetype::freetype::{FT_GlyphSlot, FT_LcdFilter, FT_New_Face, FT_New_Memory_Face}; use freetype::freetype::{FT_Init_FreeType, FT_Load_Glyph, FT_Render_Glyph}; use freetype::freetype::{FT_Library, FT_Outline_Get_CBox, FT_Set_Char_Size, FT_Select_Size}; -use freetype::freetype::{FT_Fixed, FT_Matrix, FT_Set_Transform, FT_String, FT_ULong}; +use freetype::freetype::{FT_Fixed, FT_Matrix, FT_Set_Transform, FT_String, FT_ULong, FT_Vector}; use freetype::freetype::{FT_Err_Unimplemented_Feature, FT_MulFix, FT_Outline_Embolden}; use freetype::freetype::{FT_LOAD_COLOR, FT_LOAD_DEFAULT, FT_LOAD_FORCE_AUTOHINT}; use freetype::freetype::{FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH, FT_LOAD_NO_AUTOHINT}; @@ -224,7 +224,7 @@ pub struct FontContext { // a given FontContext so it is safe to move the latter between threads. unsafe impl Send for FontContext {} -fn get_skew_bounds(bottom: i32, top: i32, skew_factor: f32) -> (f32, f32) { +fn get_skew_bounds(bottom: i32, top: i32, skew_factor: f32, _vertical: bool) -> (f32, f32) { let skew_min = ((bottom as f32 + 0.5) * skew_factor).floor(); let skew_max = ((top as f32 - 0.5) * skew_factor).ceil(); (skew_min, skew_max) @@ -237,10 +237,11 @@ fn skew_bitmap( left: i32, top: i32, skew_factor: f32, + vertical: bool, // TODO: vertical skew not yet implemented! ) -> (Vec, usize, i32) { let stride = width * 4; // Calculate the skewed horizontal offsets of the bottom and top of the glyph. - let (skew_min, skew_max) = get_skew_bounds(top - height as i32, top, skew_factor); + let (skew_min, skew_max) = get_skew_bounds(top - height as i32, top, skew_factor, vertical); // Allocate enough extra width for the min/max skew offsets. let skew_width = width + (skew_max - skew_min) as usize; let mut skew_buffer = vec![0u8; skew_width * height * 4]; @@ -478,8 +479,12 @@ impl FontContext { if font.flags.contains(FontInstanceFlags::TRANSPOSE) { shape = shape.swap_xy(); } + let (mut tx, mut ty) = (0.0, 0.0); if font.synthetic_italics.is_enabled() { - shape = shape.synthesize_italics(font.synthetic_italics); + let (shape_, (tx_, ty_)) = font.synthesize_italics(shape, y_scale * req_size); + shape = shape_; + tx = tx_; + ty = ty_; }; let mut ft_shape = FT_Matrix { xx: (shape.scale_x * 65536.0) as FT_Fixed, @@ -487,8 +492,13 @@ impl FontContext { yx: (shape.skew_y * -65536.0) as FT_Fixed, yy: (shape.scale_y * 65536.0) as FT_Fixed, }; + // The delta vector for FT_Set_Transform is in units of 1/64 pixel. + let mut ft_delta = FT_Vector { + x: (tx * 64.0) as FT_F26Dot6, + y: (ty * -64.0) as FT_F26Dot6, + }; unsafe { - FT_Set_Transform(face, &mut ft_shape, ptr::null_mut()); + FT_Set_Transform(face, &mut ft_shape, &mut ft_delta); FT_Set_Char_Size( face, (req_size * x_scale * 64.0 + 0.5) as FT_F26Dot6, @@ -660,6 +670,7 @@ impl FontContext { top - height as i32, top, font.synthetic_italics.to_skew(), + font.flags.contains(FontInstanceFlags::VERTICAL), ); left += skew_min as i32; width += (skew_max - skew_min) as i32; @@ -949,6 +960,7 @@ impl FontContext { left, top, font.synthetic_italics.to_skew(), + font.flags.contains(FontInstanceFlags::VERTICAL), ); final_buffer = skew_buffer; actual_width = skew_width; diff --git a/webrender/src/platform/windows/font.rs b/webrender/src/platform/windows/font.rs index d07e4fdcee..2ab2d7e678 100644 --- a/webrender/src/platform/windows/font.rs +++ b/webrender/src/platform/windows/font.rs @@ -477,9 +477,9 @@ impl FontContext { fn get_glyph_parameters(font: &FontInstance, key: &GlyphKey) -> (f32, f64, bool, Option) { let (_, y_scale) = font.transform.compute_scale().unwrap_or((1.0, 1.0)); - let size = (font.size.to_f64_px() * y_scale) as f32; + let scaled_size = font.size.to_f64_px() * y_scale; let bitmaps = is_bitmap_font(font); - let (mut shape, (x_offset, y_offset)) = if bitmaps { + let (mut shape, (mut x_offset, mut y_offset)) = if bitmaps { (FontTransform::identity(), (0.0, 0.0)) } else { (font.transform.invert_scale(y_scale, y_scale), font.get_subpx_offset(key)) @@ -493,9 +493,15 @@ impl FontContext { if font.flags.contains(FontInstanceFlags::TRANSPOSE) { shape = shape.swap_xy(); } + let (mut tx, mut ty) = (0.0, 0.0); if font.synthetic_italics.is_enabled() { - shape = shape.synthesize_italics(font.synthetic_italics); - } + let (shape_, (tx_, ty_)) = font.synthesize_italics(shape, scaled_size); + shape = shape_; + tx = tx_; + ty = ty_; + }; + x_offset += tx; + y_offset += ty; let transform = if !shape.is_identity() || (x_offset, y_offset) != (0.0, 0.0) { Some(dwrote::DWRITE_MATRIX { m11: shape.scale_x, @@ -508,7 +514,7 @@ impl FontContext { } else { None }; - (size, y_scale, bitmaps, transform) + (scaled_size as f32, y_scale, bitmaps, transform) } pub fn rasterize_glyph(&mut self, font: &FontInstance, key: &GlyphKey) -> GlyphRasterResult { diff --git a/webrender_api/src/font.rs b/webrender_api/src/font.rs index 7363d81624..442f64cd60 100644 --- a/webrender_api/src/font.rs +++ b/webrender_api/src/font.rs @@ -179,6 +179,7 @@ bitflags! { const FLIP_X = 1 << 5; const FLIP_Y = 1 << 6; const SUBPIXEL_POSITION = 1 << 7; + const VERTICAL = 1 << 8; // Windows flags const FORCE_GDI = 1 << 16; From 85ccbbf3cb7f7fc39ba958f214151d9951e68337 Mon Sep 17 00:00:00 2001 From: cbrewster Date: Sat, 29 Feb 2020 04:14:21 +0000 Subject: [PATCH 16/33] Bug 1596513: Part 1: Take scale factors into account when rendering drop shadows in WebRender r=gfx-reviewers,kvark Differential Revision: https://phabricator.services.mozilla.com/D63441 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/a9d8b62a399349559ceff12111f5d7fd3fab4acc --- webrender/src/picture.rs | 10 +++++----- .../filters/filter-drop-shadow-scaled-ref.yaml | 11 +++++++++++ .../filters/filter-drop-shadow-scaled.yaml | 15 +++++++++++++++ wrench/reftests/filters/reftest.list | 1 + 4 files changed, 32 insertions(+), 5 deletions(-) create mode 100644 wrench/reftests/filters/filter-drop-shadow-scaled-ref.yaml create mode 100644 wrench/reftests/filters/filter-drop-shadow-scaled.yaml diff --git a/webrender/src/picture.rs b/webrender/src/picture.rs index e48bb99663..285eb3a38f 100644 --- a/webrender/src/picture.rs +++ b/webrender/src/picture.rs @@ -3554,7 +3554,7 @@ impl PictureCompositeMode { Filter::DropShadows(shadows) => { let mut max_inflation: f32 = 0.0; for shadow in shadows { - let inflation_factor = shadow.blur_radius.round() * BLUR_SAMPLE_SCALE; + let inflation_factor = shadow.blur_radius.ceil() * BLUR_SAMPLE_SCALE; max_inflation = max_inflation.max(inflation_factor); } result_rect = picture_rect.inflate(max_inflation, max_inflation); @@ -3571,7 +3571,7 @@ impl PictureCompositeMode { input.inflate(inflation_factor, inflation_factor) } FilterPrimitiveKind::DropShadow(ref primitive) => { - let inflation_factor = primitive.shadow.blur_radius.round() * BLUR_SAMPLE_SCALE; + let inflation_factor = primitive.shadow.blur_radius.ceil() * BLUR_SAMPLE_SCALE; let input = primitive.input.to_index(cur_index).map(|index| output_rects[index]).unwrap_or(picture_rect); let shadow_rect = input.inflate(inflation_factor, inflation_factor); input.union(&shadow_rect.translate(primitive.shadow.offset * Scale::new(1.0))) @@ -4329,7 +4329,6 @@ impl PicturePrimitive { max_std_deviation = f32::max(max_std_deviation, shadow.blur_radius * device_pixel_scale.0); } - max_std_deviation = max_std_deviation.round(); let max_blur_range = (max_std_deviation * BLUR_SAMPLE_SCALE).ceil(); // We cast clipped to f32 instead of casting unclipped to i32 // because unclipped can overflow an i32. @@ -4382,10 +4381,11 @@ impl PicturePrimitive { self.extra_gpu_data_handles.resize(shadows.len(), GpuCacheHandle::new()); let mut blur_render_task_id = picture_task_id; + let scale_factors = scale_factors(&transform); for shadow in shadows { - let std_dev = f32::round(shadow.blur_radius * device_pixel_scale.0); + let std_dev = shadow.blur_radius * device_pixel_scale.0; blur_render_task_id = RenderTask::new_blur( - DeviceSize::new(std_dev, std_dev), + DeviceSize::new(std_dev * scale_factors.0, std_dev * scale_factors.1), picture_task_id, frame_state.render_tasks, RenderTargetKind::Color, diff --git a/wrench/reftests/filters/filter-drop-shadow-scaled-ref.yaml b/wrench/reftests/filters/filter-drop-shadow-scaled-ref.yaml new file mode 100644 index 0000000000..9173528be4 --- /dev/null +++ b/wrench/reftests/filters/filter-drop-shadow-scaled-ref.yaml @@ -0,0 +1,11 @@ +# Ensure scales from enclosing SCs get applied to drop-shadows +--- +root: + items: + - type: stacking-context + bounds: [0, 0, 500, 500] + filters: drop-shadow([0, 0], 10, [255, 0, 0, 1]) + items: + - type: rect + bounds: [50, 50, 250, 250] + color: 0 255 0 1.0 diff --git a/wrench/reftests/filters/filter-drop-shadow-scaled.yaml b/wrench/reftests/filters/filter-drop-shadow-scaled.yaml new file mode 100644 index 0000000000..87fac1ee6d --- /dev/null +++ b/wrench/reftests/filters/filter-drop-shadow-scaled.yaml @@ -0,0 +1,15 @@ +# Ensure scales from enclosing SCs get applied to drop-shadows +--- +root: + items: + - type: reference-frame + bounds: [0, 0, 100, 100] + transform: [5, 0, 0, 0, 0, 5, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] + items: + - type: stacking-context + bounds: [0, 0, 400, 400] + filters: drop-shadow([0, 0], 2, [255, 0, 0, 1]) + items: + - type: rect + bounds: [10, 10, 50, 50] + color: 0 255 0 1.0 diff --git a/wrench/reftests/filters/reftest.list b/wrench/reftests/filters/reftest.list index ba15f50453..7222c7b5c4 100644 --- a/wrench/reftests/filters/reftest.list +++ b/wrench/reftests/filters/reftest.list @@ -36,6 +36,7 @@ platform(linux,mac) == filter-drop-shadow-on-viewport-edge.yaml filter-drop-shad platform(linux,mac) == blend-clipped.yaml blend-clipped.png platform(linux,mac) == filter-drop-shadow-clip.yaml filter-drop-shadow-clip.png platform(linux,mac) == filter-drop-shadow-clip-2.yaml filter-drop-shadow-clip-2.png +fuzzy(5,100000) == filter-drop-shadow-scaled.yaml filter-drop-shadow-scaled-ref.yaml == filter-segments.yaml filter-segments-ref.yaml == iframe-dropshadow.yaml iframe-dropshadow-ref.yaml skip_on(android,device) == filter-mix-blend-mode.yaml filter-mix-blend-mode-ref.yaml # fails on Pixel2 From 142de928608b91e55de3634cfce34a45ee613458 Mon Sep 17 00:00:00 2001 From: cbrewster Date: Sat, 29 Feb 2020 04:14:31 +0000 Subject: [PATCH 17/33] Bug 1596513: Part 3: Ensure drop shadow blur radius does not exceed MAX_BLUR after scale factors are applied r=gfx-reviewers,nical This was causing one of the large drop-shadow wrench reftests to timeout. This is only a partial fix, as we should be checking the scale factors earlier on when sanitizing the filter input. This will ensure we match what the non-WR backend is doing and will prevent overinflation. Differential Revision: https://phabricator.services.mozilla.com/D64197 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/b7ab4c947688fab1ee8de2ef13a6033e2307d590 --- webrender/src/picture.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/webrender/src/picture.rs b/webrender/src/picture.rs index 285eb3a38f..cdf19813c6 100644 --- a/webrender/src/picture.rs +++ b/webrender/src/picture.rs @@ -96,7 +96,7 @@ use api::{MixBlendMode, PipelineId, PremultipliedColorF, FilterPrimitiveKind}; use api::{PropertyBinding, PropertyBindingId, FilterPrimitive, FontRenderMode}; -use api::{DebugFlags, RasterSpace, ImageKey, ColorF, ColorU, PrimitiveFlags}; +use api::{DebugFlags, RasterSpace, ImageKey, ColorF, ColorU, PrimitiveFlags, MAX_BLUR_RADIUS}; use api::units::*; use crate::box_shadow::{BLUR_SAMPLE_SCALE}; use crate::clip::{ClipStore, ClipChainInstance, ClipDataHandle, ClipChainId}; @@ -4383,9 +4383,13 @@ impl PicturePrimitive { let mut blur_render_task_id = picture_task_id; let scale_factors = scale_factors(&transform); for shadow in shadows { - let std_dev = shadow.blur_radius * device_pixel_scale.0; + // TODO(cbrewster): We should take the scale factors into account when clamping the max + // std deviation for the blur earlier so that we don't overinflate. blur_render_task_id = RenderTask::new_blur( - DeviceSize::new(std_dev * scale_factors.0, std_dev * scale_factors.1), + DeviceSize::new( + f32::min(shadow.blur_radius * scale_factors.0, MAX_BLUR_RADIUS) * device_pixel_scale.0, + f32::min(shadow.blur_radius * scale_factors.1, MAX_BLUR_RADIUS) * device_pixel_scale.0, + ), picture_task_id, frame_state.render_tasks, RenderTargetKind::Color, From 6cffb1b466740013f103ba89732f3edb4d6bc754 Mon Sep 17 00:00:00 2001 From: Glenn Watson Date: Mon, 2 Mar 2020 09:56:22 +0000 Subject: [PATCH 18/33] Bug 1579235 - Part 6 - Support an opaque/alpha native surface per slice. r=Bert Previously, a native compositor surface was considered to be completely opaque, or completely translucent. This is due to a limitation in how alpha is handled in the DirectComposition API level. With this patch, each picture cache slice maintains both an opaque and translucent native surface handle. Tiles are assigned to one of those surfaces based on their current opacity. This is a performance optimization in some cases, since: - Even if part of a cache is translucent, opaque tiles can still participate in occlusion at the compositor level. - If a tile is changing from opaque to translucent, it now invalidates only that tile, rather than the entire surface. The primary benefit of this patch is that it allows compositor surfaces to be drawn sliced in between the opaque surface and any overlay / alpha tiles. Differential Revision: https://phabricator.services.mozilla.com/D64495 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/6814870342efc284b2edec7c9aeb45240cb0fe09 --- webrender/src/composite.rs | 44 ++++++---- webrender/src/frame_builder.rs | 5 +- webrender/src/picture.rs | 151 +++++++++++++++++++-------------- 3 files changed, 115 insertions(+), 85 deletions(-) diff --git a/webrender/src/composite.rs b/webrender/src/composite.rs index 58400c042d..bbd4c31256 100644 --- a/webrender/src/composite.rs +++ b/webrender/src/composite.rs @@ -236,7 +236,6 @@ struct Occluder { #[cfg_attr(feature = "replay", derive(Deserialize))] #[derive(PartialEq, Clone)] pub struct CompositeSurfaceDescriptor { - pub slice: usize, pub surface_id: Option, pub offset: DevicePoint, pub clip_rect: DeviceRect, @@ -385,7 +384,8 @@ impl CompositeState { gpu_cache: &mut GpuCache, deferred_resolves: &mut Vec, ) { - let mut visible_tile_count = 0; + let mut visible_opaque_tile_count = 0; + let mut visible_alpha_tile_count = 0; for tile in tile_cache.tiles.values() { if !tile.is_visible { @@ -393,34 +393,35 @@ impl CompositeState { continue; } - visible_tile_count += 1; - let device_rect = (tile.world_tile_rect * global_device_pixel_scale).round(); let surface = tile.surface.as_ref().expect("no tile surface set!"); - let (surface, is_opaque) = match surface { + let (surface, is_opaque, tile_id) = match surface { TileSurface::Color { color } => { - (CompositeTileSurface::Color { color: *color }, true) + (CompositeTileSurface::Color { color: *color }, true, None) } TileSurface::Clear => { - (CompositeTileSurface::Clear, false) + (CompositeTileSurface::Clear, false, None) } TileSurface::Texture { descriptor, .. } => { let surface = descriptor.resolve(resource_cache, tile_cache.current_tile_size); + let tile_id = match surface { + ResolvedSurfaceTexture::Native { id, .. } => Some(id), + ResolvedSurfaceTexture::TextureCache { .. } => None, + }; ( CompositeTileSurface::Texture { surface }, tile.is_opaque || tile_cache.is_opaque(), + tile_id, ) } }; - let tile_id = tile_cache.native_surface_id.map(|surface_id| { - NativeTileId { - surface_id, - x: tile.tile_offset.x, - y: tile.tile_offset.y, - } - }); + if is_opaque { + visible_opaque_tile_count += 1; + } else { + visible_alpha_tile_count += 1; + } // Determine ordering of this tile, based on presence of compositor // surfaces that intersect the tile. @@ -525,11 +526,20 @@ impl CompositeState { }); } - if visible_tile_count > 0 { + if visible_opaque_tile_count > 0 { + self.descriptor.surfaces.push( + CompositeSurfaceDescriptor { + surface_id: tile_cache.native_surface.as_ref().map(|s| s.opaque), + offset: tile_cache.device_position, + clip_rect: device_clip_rect, + } + ); + } + + if visible_alpha_tile_count > 0 { self.descriptor.surfaces.push( CompositeSurfaceDescriptor { - slice: tile_cache.slice, - surface_id: tile_cache.native_surface_id, + surface_id: tile_cache.native_surface.as_ref().map(|s| s.alpha), offset: tile_cache.device_position, clip_rect: device_clip_rect, } diff --git a/webrender/src/frame_builder.rs b/webrender/src/frame_builder.rs index 138aacab58..6f027572aa 100644 --- a/webrender/src/frame_builder.rs +++ b/webrender/src/frame_builder.rs @@ -402,8 +402,9 @@ impl FrameBuilder { // we need to manually clean up any native compositor surfaces that were // allocated by these tiles. for (_, mut cache_state) in visibility_state.retained_tiles.caches.drain() { - if let Some(native_surface_id) = cache_state.native_surface_id.take() { - visibility_state.resource_cache.destroy_compositor_surface(native_surface_id); + if let Some(native_surface) = cache_state.native_surface.take() { + visibility_state.resource_cache.destroy_compositor_surface(native_surface.opaque); + visibility_state.resource_cache.destroy_compositor_surface(native_surface.alpha); } } } diff --git a/webrender/src/picture.rs b/webrender/src/picture.rs index cdf19813c6..4d27b57993 100644 --- a/webrender/src/picture.rs +++ b/webrender/src/picture.rs @@ -274,9 +274,7 @@ pub struct PictureCacheState { /// Various allocations we want to avoid re-doing. allocations: PictureCacheRecycledAllocations, /// Currently allocated native compositor surface for this picture cache. - pub native_surface_id: Option, - /// True if the entire picture cache is opaque. - is_opaque: bool, + pub native_surface: Option, } pub struct PictureCacheRecycledAllocations { @@ -1212,7 +1210,26 @@ impl Tile { let clipped_rect = self.current_descriptor.local_valid_rect .intersection(&ctx.local_clip_rect) .unwrap_or_else(PictureRect::zero); - self.is_opaque = ctx.backdrop.rect.contains_rect(&clipped_rect); + let is_opaque = ctx.backdrop.rect.contains_rect(&clipped_rect); + + if is_opaque != self.is_opaque { + // If opacity changed, the native compositor surface and all tiles get invalidated. + // (this does nothing if not using native compositor mode). + // TODO(gw): This property probably changes very rarely, so it is OK to invalidate + // everything in this case. If it turns out that this isn't true, we could + // consider other options, such as per-tile opacity (natively supported + // on CoreAnimation, and supported if backed by non-virtual surfaces in + // DirectComposition). + if let Some(TileSurface::Texture { descriptor: SurfaceTextureDescriptor::Native { ref mut id, .. }, .. }) = self.surface { + if let Some(id) = id.take() { + state.resource_cache.destroy_compositor_tile(id); + } + } + + // Invalidate the entire tile to force a redraw. + self.invalidate(None, InvalidationReason::SurfaceOpacityChanged { became_opaque: is_opaque }); + self.is_opaque = is_opaque; + } // Check if the selected composite mode supports dirty rect updates. For Draw composite // mode, we can always update the content with smaller dirty rects. For native composite @@ -2038,6 +2055,24 @@ impl TileCacheLogger { } } +/// Represents the native surfaces created for a picture cache, if using +/// a native compositor. An opaque and alpha surface is always created, +/// but tiles are added to a surface based on current opacity. If the +/// calculated opacity of a tile changes, the tile is invalidated and +/// attached to a different native surface. This means that we don't +/// need to invalidate the entire surface if only some tiles are changing +/// opacity. It also means we can take advantage of opaque tiles on cache +/// slices where only some of the tiles are opaque. There is an assumption +/// that creating a native surface is cheap, and only when a tile is added +/// to a surface is there a significant cost. This assumption holds true +/// for the current native compositor implementations on Windows and Mac. +pub struct NativeSurface { + /// Native surface for opaque tiles + pub opaque: NativeSurfaceId, + /// Native surface for alpha tiles + pub alpha: NativeSurfaceId, +} + /// Represents a cache of tiles that make up a picture primitives. pub struct TileCacheInstance { /// Index of the tile cache / slice for this frame builder. It's determined @@ -2123,15 +2158,13 @@ pub struct TileCacheInstance { /// keep around the hash map used as compare_cache to avoid reallocating it each /// frame. compare_cache: FastHashMap, - /// The allocated compositor surface for this picture cache. May be None if + /// The allocated compositor surfaces for this picture cache. May be None if /// not using native compositor, or if the surface was destroyed and needs /// to be reallocated next time this surface contains valid tiles. - pub native_surface_id: Option, + pub native_surface: Option, /// The current device position of this cache. Used to set the compositor /// offset of the surface when building the visual tree. pub device_position: DevicePoint, - /// True if the entire picture cache surface is opaque. - is_opaque: bool, /// The currently considered tile size override. Used to check if we should /// re-evaluate tile size, even if the frame timer hasn't expired. tile_size_override: Option, @@ -2187,9 +2220,8 @@ impl TileCacheInstance { frames_until_size_eval: 0, fract_offset: PictureVector2D::zero(), compare_cache: FastHashMap::default(), - native_surface_id: None, + native_surface: None, device_position: DevicePoint::zero(), - is_opaque: true, tile_size_override: None, external_surfaces: Vec::new(), } @@ -2314,8 +2346,7 @@ impl TileCacheInstance { self.opacity_bindings = prev_state.opacity_bindings; self.color_bindings = prev_state.color_bindings; self.current_tile_size = prev_state.current_tile_size; - self.native_surface_id = prev_state.native_surface_id; - self.is_opaque = prev_state.is_opaque; + self.native_surface = prev_state.native_surface; fn recycle_map( ideal_len: usize, @@ -2377,8 +2408,9 @@ impl TileCacheInstance { if desired_tile_size != self.current_tile_size { // Destroy any native surfaces on the tiles that will be dropped due // to resizing. - if let Some(native_surface_id) = self.native_surface_id.take() { - frame_state.resource_cache.destroy_compositor_surface(native_surface_id); + if let Some(native_surface) = self.native_surface.take() { + frame_state.resource_cache.destroy_compositor_surface(native_surface.opaque); + frame_state.resource_cache.destroy_compositor_surface(native_surface.alpha); } self.tiles.clear(); self.current_tile_size = desired_tile_size; @@ -2572,22 +2604,25 @@ impl TileCacheInstance { } // If compositor mode is changed, need to drop all incompatible tiles. - match (frame_context.config.compositor_kind, self.native_surface_id) { - (CompositorKind::Draw { .. }, Some(_)) => { - frame_state.composite_state.destroy_native_tiles( - self.tiles.values_mut(), - frame_state.resource_cache, - ); + match frame_context.config.compositor_kind { + CompositorKind::Draw { .. } => { for tile in self.tiles.values_mut() { - tile.surface = None; - // Invalidate the entire tile to force a redraw. - tile.invalidate(None, InvalidationReason::CompositorKindChanged); + if let Some(TileSurface::Texture { descriptor: SurfaceTextureDescriptor::Native { ref mut id, .. }, .. }) = tile.surface { + if let Some(id) = id.take() { + frame_state.resource_cache.destroy_compositor_tile(id); + tile.surface = None; + // Invalidate the entire tile to force a redraw. + tile.invalidate(None, InvalidationReason::CompositorKindChanged); + } + } } - if let Some(native_surface_id) = self.native_surface_id.take() { - frame_state.resource_cache.destroy_compositor_surface(native_surface_id); + + if let Some(native_surface) = self.native_surface.take() { + frame_state.resource_cache.destroy_compositor_surface(native_surface.opaque); + frame_state.resource_cache.destroy_compositor_surface(native_surface.alpha); } } - (CompositorKind::Native { .. }, None) => { + CompositorKind::Native { .. } => { // This could hit even when compositor mode is not changed, // then we need to check if there are incompatible tiles. for tile in self.tiles.values_mut() { @@ -2598,7 +2633,6 @@ impl TileCacheInstance { } } } - (_, _) => {} } world_culling_rect @@ -3193,38 +3227,8 @@ impl TileCacheInstance { // Step through each tile and invalidate if the dependencies have changed. Determine // the current opacity setting and whether it's changed. - let mut tile_cache_is_opaque = true; for tile in self.tiles.values_mut() { - if tile.post_update(&ctx, &mut state, frame_context) { - tile_cache_is_opaque &= tile.is_opaque; - } - } - - // If opacity changed, the native compositor surface and all tiles get invalidated. - // (this does nothing if not using native compositor mode). - // TODO(gw): This property probably changes very rarely, so it is OK to invalidate - // everything in this case. If it turns out that this isn't true, we could - // consider other options, such as per-tile opacity (natively supported - // on CoreAnimation, and supported if backed by non-virtual surfaces in - // DirectComposition). - if self.is_opaque != tile_cache_is_opaque { - if let Some(native_surface_id) = self.native_surface_id.take() { - // Since the native surface will be destroyed, need to clear the compositor tile - // handle for all tiles. This means the tiles will be reallocated on demand - // when the tiles are added to render tasks. - for tile in self.tiles.values_mut() { - if let Some(TileSurface::Texture { descriptor: SurfaceTextureDescriptor::Native { ref mut id, .. }, .. }) = tile.surface { - *id = None; - } - // Invalidate the entire tile to force a redraw. - tile.invalidate(None, InvalidationReason::SurfaceOpacityChanged { - became_opaque: tile_cache_is_opaque }); - } - // Destroy the compositor surface. It will be reallocated with the correct - // opacity flag when render tasks are generated for tiles. - frame_state.resource_cache.destroy_compositor_surface(native_surface_id); - } - self.is_opaque = tile_cache_is_opaque; + tile.post_update(&ctx, &mut state, frame_context); } // When under test, record a copy of the dirty region to support @@ -4056,7 +4060,7 @@ impl PicturePrimitive { &mut self, retained_tiles: &mut RetainedTiles, ) { - if let Some(mut tile_cache) = self.tile_cache.take() { + if let Some(tile_cache) = self.tile_cache.take() { if !tile_cache.tiles.is_empty() { retained_tiles.caches.insert( tile_cache.slice, @@ -4067,8 +4071,7 @@ impl PicturePrimitive { color_bindings: tile_cache.color_bindings, root_transform: tile_cache.root_transform, current_tile_size: tile_cache.current_tile_size, - native_surface_id: tile_cache.native_surface_id.take(), - is_opaque: tile_cache.is_opaque, + native_surface: tile_cache.native_surface, allocations: PictureCacheRecycledAllocations { old_opacity_bindings: tile_cache.old_opacity_bindings, old_color_bindings: tile_cache.old_color_bindings, @@ -4624,20 +4627,36 @@ impl PicturePrimitive { // Allocate a native surface id if we're in native compositing mode, // and we don't have a surface yet (due to first frame, or destruction // due to tile size changing etc). - if tile_cache.native_surface_id.is_none() { - let surface_id = frame_state + if tile_cache.native_surface.is_none() { + let opaque = frame_state .resource_cache .create_compositor_surface( tile_cache.current_tile_size, - tile_cache.is_opaque, + true, ); - tile_cache.native_surface_id = Some(surface_id); + let alpha = frame_state + .resource_cache + .create_compositor_surface( + tile_cache.current_tile_size, + false, + ); + + tile_cache.native_surface = Some(NativeSurface { + opaque, + alpha, + }); } // Create the tile identifier and allocate it. + let surface_id = if tile.is_opaque { + tile_cache.native_surface.as_ref().unwrap().opaque + } else { + tile_cache.native_surface.as_ref().unwrap().alpha + }; + let tile_id = NativeTileId { - surface_id: tile_cache.native_surface_id.unwrap(), + surface_id, x: tile.tile_offset.x, y: tile.tile_offset.y, }; From 54d5fe896e3326e703c3575dfe6ae1a3479d8efd Mon Sep 17 00:00:00 2001 From: Glenn Watson Date: Mon, 2 Mar 2020 09:56:31 +0000 Subject: [PATCH 19/33] Bug 1579235 - Part 7 - Fix UV rect calculation for external textures. r=Bert This patch fixes an oversight in part 5 of this patch series that could result in an incorrect UV rect being used for an external texture that uses a custom UV rect. When the texture is an external texture, the UV rect is not known when the external surface descriptor is created, because external textures are not resolved until the lock() callback is invoked at the start of the frame render. To handle this, query the texture resolver for the UV rect if it's an external texture, otherwise use the default UV rect. Differential Revision: https://phabricator.services.mozilla.com/D64687 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/8f267ed8f2e3d09ced8c665af0a6db3694197116 --- webrender/res/composite.glsl | 6 ++--- webrender/src/composite.rs | 8 +++--- webrender/src/device/gl.rs | 20 +++++++++++++- webrender/src/gpu_types.rs | 6 ++--- webrender/src/renderer.rs | 51 +++++++++++++++++++++++++++++++----- webrender_api/src/units.rs | 9 +++++++ 6 files changed, 82 insertions(+), 18 deletions(-) diff --git a/webrender/res/composite.glsl b/webrender/res/composite.glsl index da19b76e78..a63df80e02 100644 --- a/webrender/res/composite.glsl +++ b/webrender/res/composite.glsl @@ -56,7 +56,7 @@ void main(void) { write_uv_rect( aUvRect0.xy, - aUvRect0.xy + aUvRect0.zw, + aUvRect0.zw, aTextureLayers.x, uv, TEX_SIZE(sColor0), @@ -65,7 +65,7 @@ void main(void) { ); write_uv_rect( aUvRect1.xy, - aUvRect1.xy + aUvRect1.zw, + aUvRect1.zw, aTextureLayers.y, uv, TEX_SIZE(sColor1), @@ -74,7 +74,7 @@ void main(void) { ); write_uv_rect( aUvRect2.xy, - aUvRect2.xy + aUvRect2.zw, + aUvRect2.zw, aTextureLayers.z, uv, TEX_SIZE(sColor2), diff --git a/webrender/src/composite.rs b/webrender/src/composite.rs index bbd4c31256..f02cbce692 100644 --- a/webrender/src/composite.rs +++ b/webrender/src/composite.rs @@ -4,7 +4,7 @@ use api::{ColorF, ImageKey, YuvColorSpace, YuvFormat, ImageRendering}; use api::units::{DeviceRect, DeviceIntSize, DeviceIntRect, DeviceIntPoint, WorldRect}; -use api::units::{DevicePixelScale, DevicePoint, PictureRect}; +use api::units::{DevicePixelScale, DevicePoint, PictureRect, TexelRect}; use crate::batch::{resolve_image, get_buffer_kind}; use crate::gpu_cache::GpuCache; use crate::gpu_types::{ZBufferId, ZBufferIdGenerator}; @@ -116,7 +116,7 @@ pub struct ExternalSurfaceDescriptor { pub struct YuvPlaneDescriptor { pub texture: TextureSource, pub texture_layer: i32, - pub uv_rect: DeviceRect, + pub uv_rect: TexelRect, } impl YuvPlaneDescriptor { @@ -124,7 +124,7 @@ impl YuvPlaneDescriptor { YuvPlaneDescriptor { texture: TextureSource::Invalid, texture_layer: 0, - uv_rect: DeviceRect::zero(), + uv_rect: TexelRect::invalid(), } } } @@ -491,7 +491,7 @@ impl CompositeState { *plane = YuvPlaneDescriptor { texture: cache_item.texture_id, texture_layer: cache_item.texture_layer, - uv_rect: cache_item.uv_rect.to_f32(), + uv_rect: cache_item.uv_rect.into(), }; } } diff --git a/webrender/src/device/gl.rs b/webrender/src/device/gl.rs index 202b699a9c..cd08775638 100644 --- a/webrender/src/device/gl.rs +++ b/webrender/src/device/gl.rs @@ -486,14 +486,21 @@ pub struct ExternalTexture { id: gl::GLuint, target: gl::GLuint, swizzle: Swizzle, + uv_rect: TexelRect, } impl ExternalTexture { - pub fn new(id: u32, target: TextureTarget, swizzle: Swizzle) -> Self { + pub fn new( + id: u32, + target: TextureTarget, + swizzle: Swizzle, + uv_rect: TexelRect, + ) -> Self { ExternalTexture { id, target: get_gl_target(target), swizzle, + uv_rect, } } @@ -501,6 +508,10 @@ impl ExternalTexture { pub fn internal_id(&self) -> gl::GLuint { self.id } + + pub fn get_uv_rect(&self) -> TexelRect { + self.uv_rect + } } bitflags! { @@ -618,6 +629,13 @@ impl Texture { id: self.id, target: self.target, swizzle: Swizzle::default(), + // TODO(gw): Support custom UV rect for external textures during captures + uv_rect: TexelRect::new( + 0.0, + 0.0, + self.size.width as f32, + self.size.height as f32, + ), }; self.id = 0; // don't complain, moved out ext diff --git a/webrender/src/gpu_types.rs b/webrender/src/gpu_types.rs index fb1f9c67cb..6906a80dd1 100644 --- a/webrender/src/gpu_types.rs +++ b/webrender/src/gpu_types.rs @@ -255,7 +255,7 @@ pub struct CompositeInstance { yuv_rescale: f32, // UV rectangles (pixel space) for color / yuv texture planes - uv_rects: [DeviceRect; 3], + uv_rects: [TexelRect; 3], // Texture array layers for color / yuv texture planes texture_layers: [f32; 3], @@ -278,7 +278,7 @@ impl CompositeInstance { yuv_format: 0.0, yuv_rescale: 0.0, texture_layers: [layer, 0.0, 0.0], - uv_rects: [DeviceRect::zero(); 3], + uv_rects: [TexelRect::invalid(); 3], } } @@ -290,7 +290,7 @@ impl CompositeInstance { yuv_format: YuvFormat, yuv_rescale: f32, texture_layers: [f32; 3], - uv_rects: [DeviceRect; 3], + uv_rects: [TexelRect; 3], ) -> Self { CompositeInstance { rect, diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index e49a1f6db4..7e2210bf51 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -1253,6 +1253,26 @@ impl TextureResolver { } } + // Retrieve the deferred / resolved UV rect if an external texture, otherwise + // return the default supplied UV rect. + fn get_uv_rect( + &self, + source: &TextureSource, + default_value: TexelRect, + ) -> TexelRect { + match source { + TextureSource::External(ref external_image) => { + let texture = self.external_images + .get(&(external_image.id, external_image.channel_index)) + .expect("BUG: External image should be resolved by now"); + texture.get_uv_rect() + } + _ => { + default_value + } + } + } + fn report_memory(&self) -> MemoryReport { let mut report = MemoryReport::default(); @@ -4373,6 +4393,17 @@ impl Renderer { } current_textures = textures; + // When the texture is an external texture, the UV rect is not known when + // the external surface descriptor is created, because external textures + // are not resolved until the lock() callback is invoked at the start of + // the frame render. To handle this, query the texture resolver for the + // UV rect if it's an external texture, otherwise use the default UV rect. + let uv_rects = [ + self.texture_resolver.get_uv_rect(&textures.colors[0], surface.yuv_planes[0].uv_rect), + self.texture_resolver.get_uv_rect(&textures.colors[1], surface.yuv_planes[1].uv_rect), + self.texture_resolver.get_uv_rect(&textures.colors[2], surface.yuv_planes[2].uv_rect), + ]; + // Create the instance and add to current batch let instance = CompositeInstance::new_yuv( surface.device_rect, @@ -4386,11 +4417,7 @@ impl Renderer { surface.yuv_planes[1].texture_layer as f32, surface.yuv_planes[2].texture_layer as f32, ], - [ - surface.yuv_planes[0].uv_rect, - surface.yuv_planes[1].uv_rect, - surface.yuv_planes[2].uv_rect, - ], + uv_rects, ); instances.push(instance); } @@ -5149,7 +5176,12 @@ impl Renderer { let texture = match image.source { ExternalImageSource::NativeTexture(texture_id) => { - ExternalTexture::new(texture_id, texture_target, Swizzle::default()) + ExternalTexture::new( + texture_id, + texture_target, + Swizzle::default(), + image.uv, + ) } ExternalImageSource::Invalid => { warn!("Invalid ext-image"); @@ -5159,7 +5191,12 @@ impl Renderer { ext_image.channel_index ); // Just use 0 as the gl handle for this failed case. - ExternalTexture::new(0, texture_target, Swizzle::default()) + ExternalTexture::new( + 0, + texture_target, + Swizzle::default(), + image.uv, + ) } ExternalImageSource::RawData(_) => { panic!("Raw external data is not expected for deferred resolves!"); diff --git a/webrender_api/src/units.rs b/webrender_api/src/units.rs index 9ae21f13b4..00546acd70 100644 --- a/webrender_api/src/units.rs +++ b/webrender_api/src/units.rs @@ -170,6 +170,15 @@ impl TexelRect { } } +impl Into for DeviceIntRect { + fn into(self) -> TexelRect { + TexelRect { + uv0: self.min().to_f32(), + uv1: self.max().to_f32(), + } + } +} + const MAX_AU_FLOAT: f32 = 1.0e6; pub trait AuHelpers { From ab7ca3e1c1e42d9f092f599f56ed2b9ecd10d22d Mon Sep 17 00:00:00 2001 From: Glenn Watson Date: Mon, 2 Mar 2020 21:37:27 +0000 Subject: [PATCH 20/33] Bug 1579235 - Part 8 - Remove overlay tiles, they can be alpha tiles instead. r=nical A previous patch in this series introduced overlay tiles. However, now that native surfaces exist for for the opaque and alpha tiles within a slice, we can remove the overlay tiles array and add these special tiles to the alpha surface. Differential Revision: https://phabricator.services.mozilla.com/D64899 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/38b7f4761f5041a0e3b2326c2be37f98da509e21 --- webrender/src/composite.rs | 50 +++++--------------------------------- webrender/src/picture.rs | 26 +++++++++++++++++--- 2 files changed, 28 insertions(+), 48 deletions(-) diff --git a/webrender/src/composite.rs b/webrender/src/composite.rs index f02cbce692..6593f8cef7 100644 --- a/webrender/src/composite.rs +++ b/webrender/src/composite.rs @@ -71,17 +71,6 @@ pub enum CompositeSurfaceFormat { Yuv, } -/// The ordering that this tile should be composited with. -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -#[derive(Debug, Copy, Clone)] -pub enum TileCompositeMode { - /// Normal z-ordering for this tile - Default, - /// This tile overlaps a compositor surface - draw it after them - Over, -} - /// Describes the geometry and surface of a tile to be composited #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] @@ -93,7 +82,6 @@ pub struct CompositeTile { pub valid_rect: DeviceRect, pub z_id: ZBufferId, pub tile_id: Option, - pub mode: TileCompositeMode, } /// Describes information about drawing a primitive as a compositor surface. @@ -423,24 +411,6 @@ impl CompositeState { visible_alpha_tile_count += 1; } - // Determine ordering of this tile, based on presence of compositor - // surfaces that intersect the tile. - let mut mode = TileCompositeMode::Default; - - if tile.has_compositor_surface { - // TODO(gw): This will almost always select over blend, due to the - // background rectangle. In future, we can optimize this - // case to only check items that come _after_ the compositor - // surface z_id? A better option might be to tweak the z_id - // values so that the alpha pixels get z-rejected? - for surface in &tile_cache.external_surfaces { - if surface.device_rect.intersects(&tile.device_valid_rect) { - mode = TileCompositeMode::Over; - break; - } - } - } - let tile = CompositeTile { surface, rect: device_rect, @@ -449,7 +419,6 @@ impl CompositeState { clip_rect: device_clip_rect, z_id, tile_id, - mode, }; self.push_tile(tile, is_opaque); @@ -564,19 +533,12 @@ impl CompositeState { self.clear_tiles.push(tile); } CompositeTileSurface::Texture { .. } => { - match tile.mode { - TileCompositeMode::Default => { - // Texture surfaces get bucketed by opaque/alpha, for z-rejection - // on the Draw compositor mode. - if is_opaque { - self.opaque_tiles.push(tile); - } else { - self.alpha_tiles.push(tile); - } - } - TileCompositeMode::Over => { - self.alpha_tiles.push(tile); - } + // Texture surfaces get bucketed by opaque/alpha, for z-rejection + // on the Draw compositor mode. + if is_opaque { + self.opaque_tiles.push(tile); + } else { + self.alpha_tiles.push(tile); } } } diff --git a/webrender/src/picture.rs b/webrender/src/picture.rs index 4d27b57993..746bca6c23 100644 --- a/webrender/src/picture.rs +++ b/webrender/src/picture.rs @@ -69,7 +69,7 @@ //! content will all be squashed into a single slice, in order to save GPU memory //! and compositing performance. //! -//! ## Overlay Tiles +//! ## Compositor Surfaces //! //! Sometimes, a primitive would prefer to exist as a native compositor surface. //! This allows a large and/or regularly changing primitive (such as a video, or @@ -79,12 +79,12 @@ //! Since drawing a primitive as a compositor surface alters the ordering of //! primitives in a tile, we use 'overlay tiles' to ensure correctness. If a //! tile has a compositor surface, _and_ that tile has primitives that overlap -//! the compositor surface rect, the tile switches to be drawn in overlay mode. +//! the compositor surface rect, the tile switches to be drawn in alpha mode. //! //! We rely on only promoting compositor surfaces that are opaque primitives. //! With this assumption, the tile(s) that intersect the compositor surface get //! a 'cutout' in the rectangle where the compositor surface exists (not the -//! entire tile), allowing that tile to be drawn as an overlay after the +//! entire tile), allowing that tile to be drawn as an alpha tile after the //! compositor surface. //! //! Tiles are only drawn in overlay mode if there is content that exists on top @@ -492,6 +492,9 @@ struct TilePostUpdateContext<'a> { /// The local rect of the overall picture cache local_rect: PictureRect, + + /// A list of the external surfaces that are present on this slice + external_surfaces: &'a [ExternalSurfaceDescriptor], } // Mutable state passed to picture cache tiles during post_update @@ -1210,7 +1213,21 @@ impl Tile { let clipped_rect = self.current_descriptor.local_valid_rect .intersection(&ctx.local_clip_rect) .unwrap_or_else(PictureRect::zero); - let is_opaque = ctx.backdrop.rect.contains_rect(&clipped_rect); + let mut is_opaque = ctx.backdrop.rect.contains_rect(&clipped_rect); + + if self.has_compositor_surface { + // TODO(gw): This will almost always select over blend, due to the + // background rectangle. In future, we can optimize this + // case to only check items that come _after_ the compositor + // surface z_id? A better option might be to tweak the z_id + // values so that the alpha pixels get z-rejected? + for surface in ctx.external_surfaces { + if surface.device_rect.intersects(&self.device_valid_rect) { + is_opaque = false; + break; + } + } + } if is_opaque != self.is_opaque { // If opacity changed, the native compositor surface and all tiles get invalidated. @@ -3217,6 +3234,7 @@ impl TileCacheInstance { color_bindings: &self.color_bindings, current_tile_size: self.current_tile_size, local_rect: self.local_rect, + external_surfaces: &self.external_surfaces, }; let mut state = TilePostUpdateState { From 49df0ec6f1469a83d7b2ae3d562c8637ccd4503d Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Mon, 2 Mar 2020 21:37:35 +0000 Subject: [PATCH 21/33] Bug 1619265 - Bump rust versions for github CI. r=jrmuizel The appveyor.yml change bumps it for windows. The macOS worker has the rust version bumped out-of-band and the change to .taskcluster.yml just updates the documentation. Differential Revision: https://phabricator.services.mozilla.com/D64945 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/3ca5d53055d7f8665a29eed0b6fd6d992d834ce5 --- .taskcluster.yml | 4 ++-- appveyor.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.taskcluster.yml b/.taskcluster.yml index 3a874006e1..92230b20e9 100644 --- a/.taskcluster.yml +++ b/.taskcluster.yml @@ -119,8 +119,8 @@ tasks: # Mozilla releng doesn't have any spare OS X machines for us at this time. # Talk to :kats or :jrmuizel if you need more details about this. The machines # are hooked up to taskcluster using taskcluster-worker; they use a workerpool - # of webrender/ci-macos. They are set up with all the dependencies needed - # to build and test webrender, including Rust (currently 1.30), servo-tidy, + # of proj-webrender/ci-macos. They are set up with all the dependencies needed + # to build and test webrender, including Rust (currently 1.41.1), servo-tidy, # mako, zlib, etc. Note that unlike the docker-worker used for Linux, these # machines WILL persist state from one run to the next, so any cleanup needs # to be handled explicitly. diff --git a/appveyor.yml b/appveyor.yml index 4b6507292f..2137e7a5a2 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -9,8 +9,8 @@ environment: TARGET: x86_64-pc-windows-msvc install: - - ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-1.36.0-${env:TARGET}.msi" - - msiexec /passive /i "rust-1.36.0-%TARGET%.msi" ADDLOCAL=Rustc,Cargo,Std INSTALLDIR=C:\Rust + - ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-1.41.1-${env:TARGET}.msi" + - msiexec /passive /i "rust-1.41.1-%TARGET%.msi" ADDLOCAL=Rustc,Cargo,Std INSTALLDIR=C:\Rust - rustc -V - cargo -V From ad0cd127e3c1d9ba8d0ded408fdca8a15be6f4dd Mon Sep 17 00:00:00 2001 From: Glenn Watson Date: Tue, 3 Mar 2020 04:00:49 +0000 Subject: [PATCH 22/33] Bug 1579235 - Part 9 - Optimize compositor surface overlays. r=Bert This patch improves the performance of compositor surfaces in two ways: (1) Ignore primitives behind the first compositor surface when determining whether a tile needs to be moved to the overlay (alpha) pass. This means WR only moves a tile to the alpha pass when it has primitives that overlap with the compositor surface bounding rect, and are ordered after that compositor surface. In practice, this means most tiles are able to remain in the fast (opaque) path. Typically, a small number of tiles that contain overlay video controls are moved to the alpha pass. (2) Register the opaque compositor surfaces as potential occluders. This allows tiles that are completely covered by a compositor surface to be removed from the compositor visual tree, which helps both the simple and native compositor modes. Between them, these optimizations typically mean that when watching video in full-screen, nothing is composited except the video surface itself, and some small region(s) where video overlay controls are currently active. Differential Revision: https://phabricator.services.mozilla.com/D64909 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/f6dc6b38288cfcb9f5f70932830d4a3d2b3759b2 --- webrender/src/batch.rs | 2 - webrender/src/composite.rs | 30 +++++----- webrender/src/gpu_types.rs | 2 +- webrender/src/picture.rs | 100 ++++++++++++++++++++++++++++---- webrender/src/prim_store/mod.rs | 2 +- 5 files changed, 105 insertions(+), 31 deletions(-) diff --git a/webrender/src/batch.rs b/webrender/src/batch.rs index f9f5330181..db81681852 100644 --- a/webrender/src/batch.rs +++ b/webrender/src/batch.rs @@ -1227,12 +1227,10 @@ impl BatchBuilder { .map(&prim_info.combined_local_clip_rect) .expect("bug: unable to map clip rect"); let device_clip_rect = (world_clip_rect * ctx.global_device_pixel_scale).round(); - let z_id = composite_state.z_generator.next(); composite_state.push_surface( tile_cache, device_clip_rect, - z_id, ctx.global_device_pixel_scale, ctx.resource_cache, gpu_cache, diff --git a/webrender/src/composite.rs b/webrender/src/composite.rs index 6593f8cef7..aedd21a077 100644 --- a/webrender/src/composite.rs +++ b/webrender/src/composite.rs @@ -89,6 +89,7 @@ pub struct CompositeTile { /// this will also support RGBA images. pub struct ExternalSurfaceDescriptor { pub local_rect: PictureRect, + pub world_rect: WorldRect, pub device_rect: DeviceRect, pub clip_rect: DeviceRect, pub image_keys: [ImageKey; 3], @@ -96,6 +97,7 @@ pub struct ExternalSurfaceDescriptor { pub yuv_color_space: YuvColorSpace, pub yuv_format: YuvFormat, pub yuv_rescale: f32, + pub z_id: ZBufferId, } /// Information about a plane in a YUV surface. @@ -215,7 +217,7 @@ impl Default for CompositorKind { #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] struct Occluder { - slice: usize, + z_id: ZBufferId, device_rect: DeviceIntRect, } @@ -319,22 +321,22 @@ impl CompositeState { /// used during frame building to occlude tiles. pub fn register_occluder( &mut self, - slice: usize, + z_id: ZBufferId, rect: WorldRect, ) { let device_rect = (rect * self.global_device_pixel_scale).round().to_i32(); self.occluders.push(Occluder { device_rect, - slice, + z_id, }); } - /// Returns true if a tile with the specified rectangle and slice + /// Returns true if a tile with the specified rectangle and z_id /// is occluded by an opaque surface in front of it. pub fn is_tile_occluded( &self, - slice: usize, + z_id: ZBufferId, device_rect: DeviceRect, ) -> bool { // It's often the case that a tile is only occluded by considering multiple @@ -354,7 +356,7 @@ impl CompositeState { let ref_area = device_rect.size.width * device_rect.size.height; // Calculate the non-overlapping area of the valid occluders. - let cover_area = area_of_occluders(&self.occluders, slice, &device_rect); + let cover_area = area_of_occluders(&self.occluders, z_id, &device_rect); debug_assert!(cover_area <= ref_area); // Check if the tile area is completely covered @@ -366,7 +368,6 @@ impl CompositeState { &mut self, tile_cache: &TileCacheInstance, device_clip_rect: DeviceRect, - z_id: ZBufferId, global_device_pixel_scale: DevicePixelScale, resource_cache: &ResourceCache, gpu_cache: &mut GpuCache, @@ -417,7 +418,7 @@ impl CompositeState { valid_rect: tile.device_valid_rect.translate(-device_rect.origin.to_vector()), dirty_rect: tile.device_dirty_rect.translate(-device_rect.origin.to_vector()), clip_rect: device_clip_rect, - z_id, + z_id: tile.z_id, tile_id, }; @@ -479,14 +480,13 @@ impl CompositeState { .intersection(&device_clip_rect) .unwrap_or_else(DeviceRect::zero); - // z_id for compositor surfaces can be the same as the surface it - // exists on, because we use LessEqual depth function. We could - // in future consider disabling z-read completely for drawing - // surface overlay tiles, since it doesn't do anything useful. + // Get a new z_id for each compositor surface, to ensure correct ordering + // when drawing with the simple (Draw) compositor. + self.external_surfaces.push(ResolvedExternalSurface { device_rect: external_surface.device_rect, clip_rect, - z_id, + z_id: external_surface.z_id, yuv_color_space: external_surface.yuv_color_space, yuv_format: external_surface.yuv_format, yuv_rescale: external_surface.yuv_rescale, @@ -689,7 +689,7 @@ pub trait Compositor { /// overlapping areas between those rectangles. fn area_of_occluders( occluders: &[Occluder], - slice: usize, + z_id: ZBufferId, clip_rect: &DeviceIntRect, ) -> i32 { // This implementation is based on the article https://leetcode.com/articles/rectangle-area-ii/. @@ -730,7 +730,7 @@ fn area_of_occluders( let mut events = Vec::with_capacity(occluders.len() * 2); for occluder in occluders { // Only consider occluders in front of this rect - if occluder.slice > slice { + if occluder.z_id.0 > z_id.0 { // Clip the source rect to the rectangle we care about, since we only // want to record area for the tile we are comparing to. if let Some(rect) = occluder.device_rect.intersection(clip_rect) { diff --git a/webrender/src/gpu_types.rs b/webrender/src/gpu_types.rs index 6906a80dd1..57b9042a9a 100644 --- a/webrender/src/gpu_types.rs +++ b/webrender/src/gpu_types.rs @@ -23,7 +23,7 @@ pub const VECS_PER_TRANSFORM: usize = 8; #[repr(C)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] -pub struct ZBufferId(i32); +pub struct ZBufferId(pub i32); // We get 24 bits of Z value - use up 22 bits of it to give us // 4 bits to account for GPU issues. This seems to manifest on diff --git a/webrender/src/picture.rs b/webrender/src/picture.rs index 746bca6c23..616414ff29 100644 --- a/webrender/src/picture.rs +++ b/webrender/src/picture.rs @@ -114,7 +114,7 @@ use crate::intern::ItemUid; use crate::internal_types::{FastHashMap, FastHashSet, PlaneSplitter, Filter, PlaneSplitAnchor, TextureSource}; use crate::frame_builder::{FrameBuildingContext, FrameBuildingState, PictureState, PictureContext}; use crate::gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle}; -use crate::gpu_types::UvRectKind; +use crate::gpu_types::{UvRectKind, ZBufferId}; use plane_split::{Clipper, Polygon, Splitter}; use crate::prim_store::{SpaceMapper, PrimitiveVisibilityMask, PointKey, PrimitiveTemplateKind}; use crate::prim_store::{SpaceSnapper, PictureIndex, PrimitiveInstance, PrimitiveInstanceKind}; @@ -495,6 +495,16 @@ struct TilePostUpdateContext<'a> { /// A list of the external surfaces that are present on this slice external_surfaces: &'a [ExternalSurfaceDescriptor], + + /// Pre-allocated z-id to assign to opaque tiles during post_update. We + /// use a different z-id for opaque/alpha tiles, so that compositor + /// surfaces (such as videos) can have a z-id between these values, + /// which allows compositor surfaces to occlude opaque tiles, but not + /// alpha tiles. + z_id_opaque: ZBufferId, + + /// Pre-allocated z-id to assign to alpha tiles during post_update + z_id_alpha: ZBufferId, } // Mutable state passed to picture cache tiles during post_update @@ -856,6 +866,14 @@ pub struct Tile { invalidation_reason: Option, /// If true, this tile has one or more compositor surfaces affecting it. pub has_compositor_surface: bool, + /// The local space valid rect for any primitives found prior to the first compositor + /// surface that affects this tile. + bg_local_valid_rect: PictureRect, + /// The local space valid rect for any primitives found after the first compositor + /// surface that affects this tile. + fg_local_valid_rect: PictureRect, + /// z-buffer id for this tile, which is one of z_id_opaque or z_id_alpha, depending on tile opacity + pub z_id: ZBufferId, } impl Tile { @@ -882,6 +900,9 @@ impl Tile { background_color: None, invalidation_reason: None, has_compositor_surface: false, + bg_local_valid_rect: PictureRect::zero(), + fg_local_valid_rect: PictureRect::zero(), + z_id: ZBufferId::invalid(), } } @@ -999,6 +1020,8 @@ impl Tile { ), ctx.tile_size, ); + self.bg_local_valid_rect = PictureRect::zero(); + self.fg_local_valid_rect = PictureRect::zero(); self.invalidation_reason = None; self.has_compositor_surface = false; @@ -1063,8 +1086,17 @@ impl Tile { // Incorporate the bounding rect of the primitive in the local valid rect // for this tile. This is used to minimize the size of the scissor rect // during rasterization and the draw rect during composition of partial tiles. - self.current_descriptor.local_valid_rect = - self.current_descriptor.local_valid_rect.union(&info.prim_clip_rect); + + // Once we have encountered 1+ compositor surfaces affecting this tile, include + // this bounding rect in the foreground. Otherwise, include in the background rect. + // This allows us to determine if we found any primitives that are on top of the + // compositor surface(s) for this tile. If so, we need to draw the tile with alpha + // blending as an overlay. + if self.has_compositor_surface { + self.fg_local_valid_rect = self.fg_local_valid_rect.union(&info.prim_clip_rect); + } else { + self.bg_local_valid_rect = self.bg_local_valid_rect.union(&info.prim_clip_rect); + } } // Include any image keys this tile depends on. @@ -1159,6 +1191,11 @@ impl Tile { return false; } + // Calculate the overall valid rect for this tile, including both the foreground + // and background local valid rects. + self.current_descriptor.local_valid_rect = + self.bg_local_valid_rect.union(&self.fg_local_valid_rect); + // TODO(gw): In theory, the local tile rect should always have an // intersection with the overall picture rect. In practice, // due to some accuracy issues with how fract_offset (and @@ -1216,19 +1253,29 @@ impl Tile { let mut is_opaque = ctx.backdrop.rect.contains_rect(&clipped_rect); if self.has_compositor_surface { - // TODO(gw): This will almost always select over blend, due to the - // background rectangle. In future, we can optimize this - // case to only check items that come _after_ the compositor - // surface z_id? A better option might be to tweak the z_id - // values so that the alpha pixels get z-rejected? + // If we found primitive(s) that are ordered _after_ the first compositor + // surface, _and_ intersect with any compositor surface, then we will need + // to draw this tile with alpha blending, as an overlay to the compositor surface. + let fg_world_valid_rect = ctx.pic_to_world_mapper + .map(&self.fg_local_valid_rect) + .expect("bug: map fg local valid rect"); + let fg_device_valid_rect = fg_world_valid_rect * ctx.global_device_pixel_scale; + for surface in ctx.external_surfaces { - if surface.device_rect.intersects(&self.device_valid_rect) { + if surface.device_rect.intersects(&fg_device_valid_rect) { is_opaque = false; break; } } } + // Set the correct z_id for this tile based on opacity + if is_opaque { + self.z_id = ctx.z_id_opaque; + } else { + self.z_id = ctx.z_id_alpha; + } + if is_opaque != self.is_opaque { // If opacity changed, the native compositor surface and all tiles get invalidated. // (this does nothing if not using native compositor mode). @@ -2188,6 +2235,8 @@ pub struct TileCacheInstance { /// List of external surfaces that have been promoted from primitives /// in this tile cache. pub external_surfaces: Vec, + /// z-buffer ID assigned to opaque tiles in this slice + pub z_id_opaque: ZBufferId, } impl TileCacheInstance { @@ -2241,6 +2290,7 @@ impl TileCacheInstance { device_position: DevicePoint::zero(), tile_size_override: None, external_surfaces: Vec::new(), + z_id_opaque: ZBufferId::invalid(), } } @@ -2292,6 +2342,11 @@ impl TileCacheInstance { self.local_rect = pic_rect; self.local_clip_rect = PictureRect::max_rect(); + // Opaque surfaces get the first z_id. Compositor surfaces then get + // allocated a z_id each. After all compositor surfaces are added, + // then we allocate a z_id for alpha tiles. + self.z_id_opaque = frame_state.composite_state.z_generator.next(); + // Reset the opaque rect + subpixel mode, as they are calculated // during the prim dependency checks. self.backdrop = BackdropInfo::empty(); @@ -2671,7 +2726,7 @@ impl TileCacheInstance { color_bindings: &ColorBindingStorage, image_instances: &ImageInstanceStorage, surface_stack: &[SurfaceIndex], - composite_state: &CompositeState, + composite_state: &mut CompositeState, ) -> bool { // This primitive exists on the last element on the current surface stack. let prim_surface_index = *surface_stack.last().unwrap(); @@ -2945,8 +3000,10 @@ impl TileCacheInstance { let device_rect = (world_rect * frame_context.global_device_pixel_scale).round(); let clip_rect = (world_clip_rect * frame_context.global_device_pixel_scale).round(); + // Each compositor surface allocates a unique z-id self.external_surfaces.push(ExternalSurfaceDescriptor { local_rect: prim_info.prim_clip_rect, + world_rect, image_keys: prim_data.kind.yuv_key, image_rendering: prim_data.kind.image_rendering, device_rect, @@ -2954,6 +3011,7 @@ impl TileCacheInstance { yuv_color_space: prim_data.kind.color_space, yuv_format: prim_data.kind.format, yuv_rescale: prim_data.kind.color_depth.rescaling_factor(), + z_id: composite_state.z_generator.next(), }); } } else { @@ -3160,13 +3218,26 @@ impl TileCacheInstance { .map(&backdrop_rect) .expect("bug: unable to map backdrop to world space"); + // Since we register the entire backdrop rect, use the opaque z-id for the + // picture cache slice. frame_state.composite_state.register_occluder( - self.slice, + self.z_id_opaque, world_backdrop_rect, ); } } + // Register any external compositor surfaces as potential occluders. This + // is especially useful when viewing video in full-screen mode, as it is + // able to occlude every background tile (avoiding allocation, rasterizion + // and compositing). + for external_surface in &self.external_surfaces { + frame_state.composite_state.register_occluder( + external_surface.z_id, + external_surface.world_rect, + ); + } + // Detect if the picture cache was scrolled or scaled. In this case, // the device space dirty rects aren't applicable (until we properly // integrate with OS compositors that can handle scrolling slices). @@ -3224,6 +3295,9 @@ impl TileCacheInstance { frame_context.spatial_tree, ); + // All compositor surfaces have allocated a z_id, so reserve a z_id for alpha tiles. + let z_id_alpha = frame_state.composite_state.z_generator.next(); + let ctx = TilePostUpdateContext { pic_to_world_mapper, global_device_pixel_scale: frame_context.global_device_pixel_scale, @@ -3235,6 +3309,8 @@ impl TileCacheInstance { current_tile_size: self.current_tile_size, local_rect: self.local_rect, external_surfaces: &self.external_surfaces, + z_id_opaque: self.z_id_opaque, + z_id_alpha, }; let mut state = TilePostUpdateState { @@ -4538,7 +4614,7 @@ impl PicturePrimitive { // then mark it as not visible and skip drawing. When it's not occluded // it will fail this test, and get rasterized by the render task setup // code below. - if frame_state.composite_state.is_tile_occluded(tile_cache.slice, device_draw_rect) { + if frame_state.composite_state.is_tile_occluded(tile.z_id, device_draw_rect) { // If this tile has an allocated native surface, free it, since it's completely // occluded. We will need to re-allocate this surface if it becomes visible, // but that's likely to be rare (e.g. when there is no content display list diff --git a/webrender/src/prim_store/mod.rs b/webrender/src/prim_store/mod.rs index 63444097a2..e9818e3afc 100644 --- a/webrender/src/prim_store/mod.rs +++ b/webrender/src/prim_store/mod.rs @@ -2174,7 +2174,7 @@ impl PrimitiveStore { &self.color_bindings, &self.images, &frame_state.surface_stack, - &frame_state.composite_state, + &mut frame_state.composite_state, ) { prim_instance.visibility_info = PrimitiveVisibilityIndex::INVALID; // Ensure the primitive clip is popped - perhaps we can use From 4d776c9d6d65ef2f8bc112d6316ca8b20998fb79 Mon Sep 17 00:00:00 2001 From: Bert Peers Date: Tue, 3 Mar 2020 04:00:58 +0000 Subject: [PATCH 23/33] Bug 1613260 - Support per-task scale for local space rasterization r=gw,aosmond Differential Revision: https://phabricator.services.mozilla.com/D63434 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/12389e959ebb20da037a8800c2797b871afd29ea --- webrender/res/brush_mix_blend.glsl | 6 +- webrender/src/picture.rs | 132 +++++- webrender/src/prim_store/mod.rs | 5 + webrender/src/prim_store/text_run.rs | 8 +- webrender/src/render_task.rs | 2 +- webrender/src/renderer.rs | 8 +- webrender_api/src/api.rs | 4 + wrench/reftests/text/raster_root_C_8192.yaml | 385 ++++++++++++++++++ wrench/reftests/text/raster_root_C_ref.yaml | 385 ++++++++++++++++++ wrench/reftests/text/reftest.list | 6 + .../transforms/raster_root_A_8192.yaml | 20 + .../transforms/raster_root_A_ref.yaml | 20 + .../transforms/raster_root_B_8192.yaml | 14 + .../transforms/raster_root_B_ref.yaml | 14 + wrench/reftests/transforms/reftest.list | 5 + 15 files changed, 1001 insertions(+), 13 deletions(-) create mode 100644 wrench/reftests/text/raster_root_C_8192.yaml create mode 100644 wrench/reftests/text/raster_root_C_ref.yaml create mode 100644 wrench/reftests/transforms/raster_root_A_8192.yaml create mode 100644 wrench/reftests/transforms/raster_root_A_ref.yaml create mode 100644 wrench/reftests/transforms/raster_root_B_8192.yaml create mode 100644 wrench/reftests/transforms/raster_root_B_ref.yaml diff --git a/webrender/res/brush_mix_blend.glsl b/webrender/res/brush_mix_blend.glsl index c73f2e3f0d..774f2fcb67 100644 --- a/webrender/res/brush_mix_blend.glsl +++ b/webrender/res/brush_mix_blend.glsl @@ -38,16 +38,18 @@ void mix_blend_brush_vs( V_OP = prim_user_data.x; PictureTask src_task = fetch_picture_task(prim_user_data.z); - vec2 src_uv = device_pos + + vec2 src_device_pos = vi.world_pos.xy * (src_task.device_pixel_scale / max(0.0, vi.world_pos.w)); + vec2 src_uv = src_device_pos + src_task.common_data.task_rect.p0 - src_task.content_origin; V_SRC_UV = src_uv / texture_size; V_SRC_LAYER = src_task.common_data.texture_layer_index; RenderTaskCommonData backdrop_task = fetch_render_task_common_data(prim_user_data.y); + float src_to_backdrop_scale = pic_task.device_pixel_scale / src_task.device_pixel_scale; vec2 backdrop_uv = device_pos + backdrop_task.task_rect.p0 - - src_task.content_origin; + src_task.content_origin * src_to_backdrop_scale; V_BACKDROP_UV = backdrop_uv / texture_size; V_BACKDROP_LAYER = backdrop_task.texture_layer_index; } diff --git a/webrender/src/picture.rs b/webrender/src/picture.rs index 616414ff29..41f440bde9 100644 --- a/webrender/src/picture.rs +++ b/webrender/src/picture.rs @@ -3602,6 +3602,13 @@ pub struct RasterConfig { pub surface_index: SurfaceIndex, /// Whether this picture establishes a rasterization root. pub establishes_raster_root: bool, + /// Scaling factor applied to fit within MAX_SURFACE_SIZE when + /// establishing a raster root. + /// Most code doesn't need to know about it, since it is folded + /// into device_pixel_scale when the rendertask is set up. + /// However e.g. text rasterization uses it to ensure consistent + /// on-screen font size. + pub root_scaling_factor: f32, } bitflags! { @@ -4316,14 +4323,14 @@ impl PicturePrimitive { }; match self.raster_config { - Some(ref raster_config) => { + Some(ref mut raster_config) => { let pic_rect = PictureRect::from_untyped(&self.precise_local_rect.to_untyped()); - let device_pixel_scale = frame_state + let mut device_pixel_scale = frame_state .surfaces[raster_config.surface_index.0] .device_pixel_scale; - let (clipped, unclipped) = match get_raster_rects( + let (mut clipped, mut unclipped) = match get_raster_rects( pic_rect, &map_pic_to_raster, &map_raster_to_world, @@ -4337,11 +4344,68 @@ impl PicturePrimitive { }; let transform = map_pic_to_raster.get_transform(); + /// If the picture (raster_config) establishes a raster root, + /// its requested resolution won't be clipped by the parent or + /// viewport; so we need to make sure the requested resolution is + /// "reasonable", ie. <= MAX_SURFACE_SIZE. If not, scale the + /// picture down until it fits that limit. This results in a new + /// device_rect, a new unclipped rect, and a new device_pixel_scale. + /// + /// Since the adjusted device_pixel_scale is passed into the + /// RenderTask (and then the shader via RenderTaskData) this mostly + /// works transparently, reusing existing support for variable DPI + /// support. The on-the-fly scaling can be seen as on-the-fly, + /// per-task DPI adjustment. Logical pixels are unaffected. + /// + /// The scaling factor is returned to the caller; blur radius, + /// font size, etc. need to be scaled accordingly. + fn adjust_scale_for_max_surface_size( + raster_config: &RasterConfig, + pic_rect: PictureRect, + map_pic_to_raster: &SpaceMapper, + map_raster_to_world: &SpaceMapper, + clipped_prim_bounding_rect: WorldRect, + device_pixel_scale : &mut DevicePixelScale, + device_rect: &mut DeviceIntRect, + unclipped: &mut DeviceRect) -> Option + { + if raster_config.establishes_raster_root && + (device_rect.size.width > (MAX_SURFACE_SIZE as i32) || + device_rect.size.height > (MAX_SURFACE_SIZE as i32)) + { + // round_out will grow by 1 integer pixel if origin is on a + // fractional position, so keep that margin for error with -1: + let scale = (MAX_SURFACE_SIZE as f32 - 1.0) / + (i32::max(device_rect.size.width, device_rect.size.height) as f32); + *device_pixel_scale = *device_pixel_scale * Scale::new(scale); + let new_device_rect = device_rect.to_f32() * Scale::new(scale); + *device_rect = new_device_rect.round_out().try_cast::().unwrap(); + + *unclipped = match get_raster_rects( + pic_rect, + &map_pic_to_raster, + &map_raster_to_world, + clipped_prim_bounding_rect, + *device_pixel_scale + ) { + Some(info) => info.1, + None => { + return None + } + }; + Some(scale) + } + else + { + None + } + } + let dep_info = match raster_config.composite_mode { PictureCompositeMode::Filter(Filter::Blur(blur_radius)) => { let blur_std_deviation = blur_radius * device_pixel_scale.0; let scale_factors = scale_factors(&transform); - let blur_std_deviation = DeviceSize::new( + let mut blur_std_deviation = DeviceSize::new( blur_std_deviation * scale_factors.0, blur_std_deviation * scale_factors.1 ); @@ -4374,7 +4438,7 @@ impl PicturePrimitive { clipped }; - let original_size = device_rect.size; + let mut original_size = device_rect.size; // Adjust the size to avoid introducing sampling errors during the down-scaling passes. // what would be even better is to rasterize the picture at the down-scaled size @@ -4384,6 +4448,16 @@ impl PicturePrimitive { blur_std_deviation, ); + if let Some(scale) = adjust_scale_for_max_surface_size( + raster_config, pic_rect, &map_pic_to_raster, &map_raster_to_world, + clipped_prim_bounding_rect, + &mut device_pixel_scale, &mut device_rect, &mut unclipped) + { + blur_std_deviation = blur_std_deviation * scale; + original_size = (original_size.to_f32() * scale).try_cast::().unwrap(); + raster_config.root_scaling_factor = scale; + } + let uv_rect_kind = calculate_uv_rect_kind( &pic_rect, &transform, @@ -4446,6 +4520,15 @@ impl PicturePrimitive { DeviceSize::new(max_std_deviation, max_std_deviation), ); + if let Some(scale) = adjust_scale_for_max_surface_size( + raster_config, pic_rect, &map_pic_to_raster, &map_raster_to_world, + clipped_prim_bounding_rect, + &mut device_pixel_scale, &mut device_rect, &mut unclipped) + { + // std_dev adjusts automatically from using device_pixel_scale + raster_config.root_scaling_factor = scale; + } + let uv_rect_kind = calculate_uv_rect_kind( &pic_rect, &transform, @@ -4536,6 +4619,15 @@ impl PicturePrimitive { Some((render_task_id, render_task_id)) } PictureCompositeMode::Filter(..) => { + + if let Some(scale) = adjust_scale_for_max_surface_size( + raster_config, pic_rect, &map_pic_to_raster, &map_raster_to_world, + clipped_prim_bounding_rect, + &mut device_pixel_scale, &mut clipped, &mut unclipped) + { + raster_config.root_scaling_factor = scale; + } + let uv_rect_kind = calculate_uv_rect_kind( &pic_rect, &transform, @@ -4561,6 +4653,14 @@ impl PicturePrimitive { Some((render_task_id, render_task_id)) } PictureCompositeMode::ComponentTransferFilter(..) => { + if let Some(scale) = adjust_scale_for_max_surface_size( + raster_config, pic_rect, &map_pic_to_raster, &map_raster_to_world, + clipped_prim_bounding_rect, + &mut device_pixel_scale, &mut clipped, &mut unclipped) + { + raster_config.root_scaling_factor = scale; + } + let uv_rect_kind = calculate_uv_rect_kind( &pic_rect, &transform, @@ -4856,6 +4956,14 @@ impl PicturePrimitive { } PictureCompositeMode::MixBlend(..) | PictureCompositeMode::Blit(_) => { + if let Some(scale) = adjust_scale_for_max_surface_size( + raster_config, pic_rect, &map_pic_to_raster, &map_raster_to_world, + clipped_prim_bounding_rect, + &mut device_pixel_scale, &mut clipped, &mut unclipped) + { + raster_config.root_scaling_factor = scale; + } + let uv_rect_kind = calculate_uv_rect_kind( &pic_rect, &transform, @@ -4881,6 +4989,15 @@ impl PicturePrimitive { Some((render_task_id, render_task_id)) } PictureCompositeMode::SvgFilter(ref primitives, ref filter_datas) => { + + if let Some(scale) = adjust_scale_for_max_surface_size( + raster_config, pic_rect, &map_pic_to_raster, &map_raster_to_world, + clipped_prim_bounding_rect, + &mut device_pixel_scale, &mut clipped, &mut unclipped) + { + raster_config.root_scaling_factor = scale; + } + let uv_rect_kind = calculate_uv_rect_kind( &pic_rect, &transform, @@ -5325,6 +5442,7 @@ impl PicturePrimitive { composite_mode, establishes_raster_root, surface_index: state.push_surface(surface), + root_scaling_factor: 1.0, }); } @@ -5477,7 +5595,9 @@ impl PicturePrimitive { // Check if any of the surfaces can't be rasterized in local space but want to. if raster_config.establishes_raster_root && (surface_rect.size.width > MAX_SURFACE_SIZE - || surface_rect.size.height > MAX_SURFACE_SIZE) { + || surface_rect.size.height > MAX_SURFACE_SIZE) + && frame_context.debug_flags.contains(DebugFlags::DISABLE_RASTER_ROOT_SCALING) + { raster_config.establishes_raster_root = false; state.are_raster_roots_assigned = false; } diff --git a/webrender/src/prim_store/mod.rs b/webrender/src/prim_store/mod.rs index e9818e3afc..d50a2ad5ee 100644 --- a/webrender/src/prim_store/mod.rs +++ b/webrender/src/prim_store/mod.rs @@ -2968,6 +2968,10 @@ impl PrimitiveStore { let raster_space = pic.get_raster_space(frame_context.spatial_tree); let surface = &frame_state.surfaces[pic_context.surface_index.0]; let prim_info = &scratch.prim_info[prim_instance.visibility_info.0 as usize]; + let root_scaling_factor = match pic.raster_config { + Some(ref raster_config) => raster_config.root_scaling_factor, + None => 1.0 + }; run.request_resources( prim_offset, @@ -2978,6 +2982,7 @@ impl PrimitiveStore { surface, prim_spatial_node_index, raster_space, + root_scaling_factor, &pic_context.subpixel_mode, frame_state.resource_cache, frame_state.gpu_cache, diff --git a/webrender/src/prim_store/text_run.rs b/webrender/src/prim_store/text_run.rs index d541c016d3..be0817291e 100644 --- a/webrender/src/prim_store/text_run.rs +++ b/webrender/src/prim_store/text_run.rs @@ -229,6 +229,7 @@ impl TextRunPrimitive { subpixel_mode: &SubpixelMode, raster_space: RasterSpace, prim_rect: PictureRect, + root_scaling_factor: f32, spatial_tree: &SpatialTree, ) -> bool { // If local raster space is specified, include that in the scale @@ -239,7 +240,10 @@ impl TextRunPrimitive { // will no longer be required. let raster_scale = raster_space.local_scale().unwrap_or(1.0).max(0.001); - let dps = surface.device_pixel_scale.0; + // root_scaling_factor is used to scale very large pictures that establish + // a raster root back to something sane, thus scale the device size accordingly. + // to the shader it looks like a change in DPI which it already supports. + let dps = surface.device_pixel_scale.0 * root_scaling_factor; let glyph_raster_scale = dps * raster_scale; let font_size = specified_font.size.to_f32_px(); let device_font_size = font_size * glyph_raster_scale; @@ -365,6 +369,7 @@ impl TextRunPrimitive { surface: &SurfaceInfo, spatial_node_index: SpatialNodeIndex, raster_space: RasterSpace, + root_scaling_factor: f32, subpixel_mode: &SubpixelMode, resource_cache: &mut ResourceCache, gpu_cache: &mut GpuCache, @@ -380,6 +385,7 @@ impl TextRunPrimitive { subpixel_mode, raster_space, prim_rect, + root_scaling_factor, spatial_tree, ); diff --git a/webrender/src/render_task.rs b/webrender/src/render_task.rs index 018e44acca..ac40b32fb0 100644 --- a/webrender/src/render_task.rs +++ b/webrender/src/render_task.rs @@ -142,7 +142,7 @@ pub struct PictureTask { pub uv_rect_handle: GpuCacheHandle, pub surface_spatial_node_index: SpatialNodeIndex, uv_rect_kind: UvRectKind, - device_pixel_scale: DevicePixelScale, + pub device_pixel_scale: DevicePixelScale, /// A bitfield that describes which dirty regions should be included /// in batches built for this picture task. pub vis_mask: PrimitiveVisibilityMask, diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index 7e2210bf51..366ca8bb2f 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -3799,8 +3799,8 @@ impl Renderer { // composite operation in this batch. let (readback_rect, readback_layer) = readback.get_target_rect(); let (backdrop_rect, _) = backdrop.get_target_rect(); - let backdrop_screen_origin = match backdrop.kind { - RenderTaskKind::Picture(ref task_info) => task_info.content_origin, + let (backdrop_screen_origin, backdrop_scale) = match backdrop.kind { + RenderTaskKind::Picture(ref task_info) => (task_info.content_origin, task_info.device_pixel_scale), _ => panic!("bug: composite on non-picture?"), }; let source_screen_origin = match source.kind { @@ -3818,8 +3818,10 @@ impl Renderer { false, ); + let source_in_backdrop_space = source_screen_origin.to_f32() * backdrop_scale.0; + let mut src = DeviceIntRect::new( - source_screen_origin + (backdrop_rect.origin - backdrop_screen_origin), + (source_in_backdrop_space + (backdrop_rect.origin - backdrop_screen_origin).to_f32()).to_i32(), readback_rect.size, ); let mut dest = readback_rect.to_i32(); diff --git a/webrender_api/src/api.rs b/webrender_api/src/api.rs index 881b6f0613..3a2d5b6d61 100644 --- a/webrender_api/src/api.rs +++ b/webrender_api/src/api.rs @@ -1427,6 +1427,10 @@ bitflags! { const INVALIDATION_DBG = 1 << 28; /// Log tile cache to memory for later saving as part of wr-capture const TILE_CACHE_LOGGING_DBG = 1 << 29; + /// For debugging, force-disable automatic scaling of establishes_raster_root + /// pictures that are too large (ie go back to old behavior that prevents those + /// large pictures from establishing a raster root). + const DISABLE_RASTER_ROOT_SCALING = 1 << 30; } } diff --git a/wrench/reftests/text/raster_root_C_8192.yaml b/wrench/reftests/text/raster_root_C_8192.yaml new file mode 100644 index 0000000000..016d481504 --- /dev/null +++ b/wrench/reftests/text/raster_root_C_8192.yaml @@ -0,0 +1,385 @@ +root: + items: + - type: "stacking-context" + transform: scale(0.125) + items: + - type: "stacking-context" + perspective: 100 + perspective-origin: 100 50 + items: + - image: checkerboard(0, 512, 16); + bounds: [1600, 1600, 8192, 8192] + - type: rect + color: [180, 140, 120, 0.4] + bounds: 2400 2400 8192 8192 + - type: "stacking-context" + bounds: [0, 0, 8192, 8192] + filters: [invert(1)] + mix-blend-mode: exclusion + complex-clip: + rect: [1920, 1920, 4096, 4096] + radius: [2048, 2048] + items: + - type: "stacking-context" + transform: scale(24) + items: + - type: line + baseline: 16 + start: 16 + end: 208 + width: 1 + orientation: horizontal + color: black + style: solid + - type: line + baseline: 24 + start: 16 + end: 208 + width: 1 + orientation: horizontal + color: blue + style: dashed + - type: line + baseline: 32 + start: 16 + end: 208 + width: 1 + orientation: horizontal + color: green + style: dotted + - type: line + baseline: 40 + start: 16 + end: 208 + width: 4 + thickness: 1 + orientation: horizontal + color: red + style: wavy + + - type: line + baseline: 48 + start: 16 + end: 208 + width: 2 + orientation: horizontal + color: black + style: solid + - type: line + baseline: 64 + start: 16 + end: 208 + width: 2 + orientation: horizontal + color: blue + style: dashed + - type: line + baseline: 80 + start: 16 + end: 207 # pruposefully cut off + width: 2 + orientation: horizontal + color: green + style: dotted + - type: line + baseline: 96 + start: 16 + end: 208 + width: 6 + thickness: 2 + orientation: horizontal + color: red + style: wavy + + - + type: "shadow" + bounds: [8, 100, 225, 50] + blur-radius: 0 + offset: [2, 2] + color: red + - type: line + baseline: 112 + start: 16 + end: 208 + width: 1 + orientation: horizontal + color: [0,0,0,0] + style: solid + - type: line + baseline: 120 + start: 16 + end: 208 + width: 1 + orientation: horizontal + color: [0,0,0,0] + style: dashed + - type: line + baseline: 128 + start: 16 + end: 209 + width: 1 + orientation: horizontal + color: [0,0,0,0] + style: dotted + - type: line + baseline: 136 + start: 16 + end: 208 + width: 4 + thickness: 1 + orientation: horizontal + color: [0,0,0,0] + style: wavy + - + type: pop-all-shadows + + - + type: "shadow" + bounds: [8, 145, 225, 65] + blur-radius: 1 + offset: [2, 3] + color: red + - type: line + baseline: 160 + start: 16 + end: 208 + width: 2 + orientation: horizontal + color: black + style: solid + - type: line + baseline: 168 + start: 16 + end: 208 + width: 2 + orientation: horizontal + color: blue + style: dashed + - type: line + baseline: 184 + start: 16 + end: 207 # purposefully cut off + width: 2 + orientation: horizontal + color: green + style: dotted + - type: line + baseline: 192 + start: 16 + end: 208 + width: 6 + thickness: 2 + orientation: horizontal + color: red + style: wavy + - + type: pop-all-shadows + + - + type: "shadow" + bounds: [8, 220, 225, 40] + blur-radius: 0 + offset: [5, 7] + color: red + - type: line + baseline: 232 + start: 16 + end: 208 + width: 8 + orientation: horizontal + color: black + style: solid + - type: line + baseline: 248 + start: 16 + end: 208 + width: 8 + orientation: horizontal + color: blue + style: dashed + - type: line + baseline: 272 + start: 16 + end: 205 # purposefully cut off + width: 8 + orientation: horizontal + color: green + style: dotted + - type: line + baseline: 296 + start: 16 + end: 208 + width: 12 + thickness: 3 + orientation: horizontal + color: black + style: wavy + - + type: "pop-all-shadows" + + - + type: "shadow" + bounds: [0, 320, 240, 140] + blur-radius: 3 + offset: [5, 7] + color: red + - type: line + baseline: 320 + start: 16 + end: 208 + width: 8 + orientation: horizontal + color: black + style: solid + - type: line + baseline: 352 + start: 16 + end: 208 + width: 8 + orientation: horizontal + color: blue + style: dashed + - type: line + baseline: 368 + start: 16 + end: 205 # purposefully cut off + width: 8 + orientation: horizontal + color: green + style: dotted + - type: line + baseline: 392 + start: 16 + end: 208 + width: 16 + thickness: 4 + orientation: horizontal + color: black + style: wavy + - + type: "pop-all-shadows" + + - type: line + baseline: 224 + start: 16 + end: 208 + width: 1 + orientation: vertical + color: black + style: solid + - type: line + baseline: 232 + start: 16 + end: 208 + width: 1 + orientation: vertical + color: blue + style: dashed + - type: line + baseline: 240 + start: 16 + end: 208 + width: 1 + orientation: vertical + color: green + style: dotted + - type: line + baseline: 256 + start: 16 + end: 208 + thickness: 1 + width: 4 + orientation: vertical + color: red + style: wavy + + - type: line + baseline: 272 + start: 16 + end: 208 + width: 2 + orientation: vertical + color: black + style: solid + - type: line + baseline: 296 + start: 16 + end: 208 + width: 2 + orientation: vertical + color: blue + style: dashed + - type: line + baseline: 320 + start: 16 + end: 207 # purposefully cut off + width: 2 + orientation: vertical + color: green + style: dotted + - type: line + baseline: 336 + start: 16 + end: 208 + thickness: 2 + width: 6 + orientation: vertical + color: red + style: wavy + + - + type: "shadow" + bounds: [350, 0, 120, 240] + blur-radius: 3 + offset: [5, 2] + color: black + - type: line + baseline: 384 + start: 16 + end: 208 + width: 8 + orientation: vertical + color: yellow + style: solid + - type: line + baseline: 400 + start: 16 + end: 208 + width: 8 + orientation: vertical + color: blue + style: dashed + - type: line + baseline: 416 + start: 16 + end: 205 # purposefully cut off + width: 8 + orientation: vertical + color: green + style: dotted + - type: line + baseline: 440 + start: 16 + end: 208 + thickness: 4 + width: 16 + orientation: vertical + color: red + style: wavy + - + type: "pop-all-shadows" + - text: "side-left" + origin: 80 120 + size: 32 + transpose: true + flip-x: true + font: "VeraBd.ttf" + color: [40,40,40,1.0] + - text: "side-right" + origin: 240 240 + size: 32 + color: [190,180,200,1.0] + transpose: true + flip-y: true + font: "FreeSans.ttf" + diff --git a/wrench/reftests/text/raster_root_C_ref.yaml b/wrench/reftests/text/raster_root_C_ref.yaml new file mode 100644 index 0000000000..2712186a77 --- /dev/null +++ b/wrench/reftests/text/raster_root_C_ref.yaml @@ -0,0 +1,385 @@ +root: + items: + - type: "stacking-context" + transform: scale(0.5) + items: + - type: "stacking-context" + perspective: 100 + perspective-origin: 100 50 + items: + - image: checkerboard(0, 128, 16); + bounds: [400, 400, 2048, 2048] + - type: rect + color: [180, 140, 120, 0.4] + bounds: 600 600 2048 2048 + - type: "stacking-context" + bounds: [0, 0, 2048, 2048] + filters: [invert(1)] + mix-blend-mode: exclusion + complex-clip: + rect: [480, 480, 1024, 1024] + radius: [512, 512] + items: + - type: "stacking-context" + transform: scale(6) + items: + - type: line + baseline: 16 + start: 16 + end: 208 + width: 1 + orientation: horizontal + color: black + style: solid + - type: line + baseline: 24 + start: 16 + end: 208 + width: 1 + orientation: horizontal + color: blue + style: dashed + - type: line + baseline: 32 + start: 16 + end: 208 + width: 1 + orientation: horizontal + color: green + style: dotted + - type: line + baseline: 40 + start: 16 + end: 208 + width: 4 + thickness: 1 + orientation: horizontal + color: red + style: wavy + + - type: line + baseline: 48 + start: 16 + end: 208 + width: 2 + orientation: horizontal + color: black + style: solid + - type: line + baseline: 64 + start: 16 + end: 208 + width: 2 + orientation: horizontal + color: blue + style: dashed + - type: line + baseline: 80 + start: 16 + end: 207 # pruposefully cut off + width: 2 + orientation: horizontal + color: green + style: dotted + - type: line + baseline: 96 + start: 16 + end: 208 + width: 6 + thickness: 2 + orientation: horizontal + color: red + style: wavy + + - + type: "shadow" + bounds: [8, 100, 225, 50] + blur-radius: 0 + offset: [2, 2] + color: red + - type: line + baseline: 112 + start: 16 + end: 208 + width: 1 + orientation: horizontal + color: [0,0,0,0] + style: solid + - type: line + baseline: 120 + start: 16 + end: 208 + width: 1 + orientation: horizontal + color: [0,0,0,0] + style: dashed + - type: line + baseline: 128 + start: 16 + end: 209 + width: 1 + orientation: horizontal + color: [0,0,0,0] + style: dotted + - type: line + baseline: 136 + start: 16 + end: 208 + width: 4 + thickness: 1 + orientation: horizontal + color: [0,0,0,0] + style: wavy + - + type: pop-all-shadows + + - + type: "shadow" + bounds: [8, 145, 225, 65] + blur-radius: 1 + offset: [2, 3] + color: red + - type: line + baseline: 160 + start: 16 + end: 208 + width: 2 + orientation: horizontal + color: black + style: solid + - type: line + baseline: 168 + start: 16 + end: 208 + width: 2 + orientation: horizontal + color: blue + style: dashed + - type: line + baseline: 184 + start: 16 + end: 207 # purposefully cut off + width: 2 + orientation: horizontal + color: green + style: dotted + - type: line + baseline: 192 + start: 16 + end: 208 + width: 6 + thickness: 2 + orientation: horizontal + color: red + style: wavy + - + type: pop-all-shadows + + - + type: "shadow" + bounds: [8, 220, 225, 40] + blur-radius: 0 + offset: [5, 7] + color: red + - type: line + baseline: 232 + start: 16 + end: 208 + width: 8 + orientation: horizontal + color: black + style: solid + - type: line + baseline: 248 + start: 16 + end: 208 + width: 8 + orientation: horizontal + color: blue + style: dashed + - type: line + baseline: 272 + start: 16 + end: 205 # purposefully cut off + width: 8 + orientation: horizontal + color: green + style: dotted + - type: line + baseline: 296 + start: 16 + end: 208 + width: 12 + thickness: 3 + orientation: horizontal + color: black + style: wavy + - + type: "pop-all-shadows" + + - + type: "shadow" + bounds: [0, 320, 240, 140] + blur-radius: 3 + offset: [5, 7] + color: red + - type: line + baseline: 320 + start: 16 + end: 208 + width: 8 + orientation: horizontal + color: black + style: solid + - type: line + baseline: 352 + start: 16 + end: 208 + width: 8 + orientation: horizontal + color: blue + style: dashed + - type: line + baseline: 368 + start: 16 + end: 205 # purposefully cut off + width: 8 + orientation: horizontal + color: green + style: dotted + - type: line + baseline: 392 + start: 16 + end: 208 + width: 16 + thickness: 4 + orientation: horizontal + color: black + style: wavy + - + type: "pop-all-shadows" + + - type: line + baseline: 224 + start: 16 + end: 208 + width: 1 + orientation: vertical + color: black + style: solid + - type: line + baseline: 232 + start: 16 + end: 208 + width: 1 + orientation: vertical + color: blue + style: dashed + - type: line + baseline: 240 + start: 16 + end: 208 + width: 1 + orientation: vertical + color: green + style: dotted + - type: line + baseline: 256 + start: 16 + end: 208 + thickness: 1 + width: 4 + orientation: vertical + color: red + style: wavy + + - type: line + baseline: 272 + start: 16 + end: 208 + width: 2 + orientation: vertical + color: black + style: solid + - type: line + baseline: 296 + start: 16 + end: 208 + width: 2 + orientation: vertical + color: blue + style: dashed + - type: line + baseline: 320 + start: 16 + end: 207 # purposefully cut off + width: 2 + orientation: vertical + color: green + style: dotted + - type: line + baseline: 336 + start: 16 + end: 208 + thickness: 2 + width: 6 + orientation: vertical + color: red + style: wavy + + - + type: "shadow" + bounds: [350, 0, 120, 240] + blur-radius: 3 + offset: [5, 2] + color: black + - type: line + baseline: 384 + start: 16 + end: 208 + width: 8 + orientation: vertical + color: yellow + style: solid + - type: line + baseline: 400 + start: 16 + end: 208 + width: 8 + orientation: vertical + color: blue + style: dashed + - type: line + baseline: 416 + start: 16 + end: 205 # purposefully cut off + width: 8 + orientation: vertical + color: green + style: dotted + - type: line + baseline: 440 + start: 16 + end: 208 + thickness: 4 + width: 16 + orientation: vertical + color: red + style: wavy + - + type: "pop-all-shadows" + - text: "side-left" + origin: 80 120 + size: 32 + transpose: true + flip-x: true + font: "VeraBd.ttf" + color: [40,40,40,1.0] + - text: "side-right" + origin: 240 240 + size: 32 + color: [190,180,200,1.0] + transpose: true + flip-y: true + font: "FreeSans.ttf" + diff --git a/wrench/reftests/text/reftest.list b/wrench/reftests/text/reftest.list index 3f045bc4a6..db4fba8962 100644 --- a/wrench/reftests/text/reftest.list +++ b/wrench/reftests/text/reftest.list @@ -75,3 +75,9 @@ fuzzy(1,39) options(disable-subpixel) == raster-space-snap.yaml raster-space-sna # == intermediate-transform.yaml intermediate-transform-ref.yaml # fails because of AA inavailable with an intermediate surface platform(linux) allow_sacrificing_subpixel_aa(false) == text-fixed-slice.yaml text-fixed-slice-slow.png platform(linux) allow_sacrificing_subpixel_aa(true) == text-fixed-slice.yaml text-fixed-slice-fast.png + +# a 8544×8544 raster root vs. 2136×2136 +# most pixels are off by a small amount, but a few pixels on the edge vary by a lot, pushing up the fuzzy max-diff; +# the main goal of the test is that everything is in the same place, at the same scale, clipped the same way, +# despite 4x on-the-fly scale change. +skip_on(android) fuzzy(115,23498) == raster_root_C_8192.yaml raster_root_C_ref.yaml diff --git a/wrench/reftests/transforms/raster_root_A_8192.yaml b/wrench/reftests/transforms/raster_root_A_8192.yaml new file mode 100644 index 0000000000..034631d031 --- /dev/null +++ b/wrench/reftests/transforms/raster_root_A_8192.yaml @@ -0,0 +1,20 @@ +root: + items: + - type: "stacking-context" + transform: scale(0.125) + items: + - type: "stacking-context" + perspective: 100 + perspective-origin: 100 50 + items: + - image: checkerboard(0, 512, 16); + bounds: [1600, 1600, 8192, 8192] + - type: "stacking-context" + bounds: [0, 0, 8192, 8192] + mix-blend-mode: difference + complex-clip: + rect: [2048, 2048, 4096, 4096] + radius: [1024, 1024] + items: + - image: checkerboard(0, 4096, 2); + bounds: [0, 0, 8192, 8192] diff --git a/wrench/reftests/transforms/raster_root_A_ref.yaml b/wrench/reftests/transforms/raster_root_A_ref.yaml new file mode 100644 index 0000000000..b5e28256bf --- /dev/null +++ b/wrench/reftests/transforms/raster_root_A_ref.yaml @@ -0,0 +1,20 @@ +root: + items: + - type: "stacking-context" + transform: scale(0.5) + items: + - type: "stacking-context" + perspective: 100 + perspective-origin: 100 50 + items: + - image: checkerboard(0, 128, 16); + bounds: 400 400 2048 2048 + - type: "stacking-context" + bounds: [0, 0, 2048, 2048] + mix-blend-mode: difference + complex-clip: + rect: [512, 512, 1024, 1024] + radius: [256, 256] + items: + - image: checkerboard(0, 1024, 2); + bounds: [0, 0, 2048, 2048] diff --git a/wrench/reftests/transforms/raster_root_B_8192.yaml b/wrench/reftests/transforms/raster_root_B_8192.yaml new file mode 100644 index 0000000000..9f8a58f5cc --- /dev/null +++ b/wrench/reftests/transforms/raster_root_B_8192.yaml @@ -0,0 +1,14 @@ +root: + items: + - type: stacking-context + bounds: [0, 0, 600, 600] + perspective: 100 + items: + - type: stacking-context + transform: rotate-z(20) rotate-x(60) + filters: [invert(1)] + mix-blend-mode: difference + items: + - type: rect + bounds: [0, 0, 20000, 100] + color: [20, 120, 18, 1.0] diff --git a/wrench/reftests/transforms/raster_root_B_ref.yaml b/wrench/reftests/transforms/raster_root_B_ref.yaml new file mode 100644 index 0000000000..3fea3a19db --- /dev/null +++ b/wrench/reftests/transforms/raster_root_B_ref.yaml @@ -0,0 +1,14 @@ +root: + items: + - type: stacking-context + bounds: [0, 0, 600, 600] + perspective: 100 + items: + - type: stacking-context + transform: rotate-z(20) rotate-x(60) + filters: [invert(1)] + mix-blend-mode: difference + items: + - type: rect + bounds: [0, 0, 4000, 100] + color: [20, 120, 18, 1.0] diff --git a/wrench/reftests/transforms/reftest.list b/wrench/reftests/transforms/reftest.list index 8d2f0c3be2..a6dcf01a70 100644 --- a/wrench/reftests/transforms/reftest.list +++ b/wrench/reftests/transforms/reftest.list @@ -42,3 +42,8 @@ platform(linux,mac) fuzzy(1,74) == border-scale-4.yaml border-scale-4.png == flatten-twice.yaml flatten-twice-ref.yaml == strange-w.yaml strange-w-ref.yaml == big-axis-aligned-scale.yaml big-axis-aligned-scale-ref.yaml +# Compare ~8K raster root (>MAX_SURFACE_SIZE) with ~2K raster root. fuzzy due to lerping on edges. +skip_on(android) fuzzy(93,3692) == raster_root_A_8192.yaml raster_root_A_ref.yaml +# Same as large-raster-root.yaml but resulting in a 10302×100 raster root (= >4096) vs 4000x100 in ref: +skip_on(android) fuzzy(29,333) == raster_root_B_8192.yaml raster_root_B_ref.yaml + From 517a4b68d8dfef3c3bf347aebb0d47cc710b8788 Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Tue, 3 Mar 2020 21:50:01 +0000 Subject: [PATCH 24/33] Bug 1619293 - Re-enable rust flags in github CI. r=jrmuizel Differential Revision: https://phabricator.services.mozilla.com/D65007 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/9cf4382f88826e7d458a1a9ec8b8470c9652cd83 --- .taskcluster.yml | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/.taskcluster.yml b/.taskcluster.yml index 92230b20e9..f08f4f2b29 100644 --- a/.taskcluster.yml +++ b/.taskcluster.yml @@ -64,12 +64,7 @@ tasks: image: 'staktrace/webrender-test:debian-v3' env: RUST_BACKTRACE: 'full' - ############################################################# - ### Temporarily disable until https://bugzil.la/1564873 lands - ### in order to unblock https://bugzil.la/1575648 (which is - ### time-critical) - # RUSTFLAGS: '--deny warnings' - ############################################################# + RUSTFLAGS: '--deny warnings' command: - /bin/bash - '--login' @@ -96,12 +91,7 @@ tasks: image: 'staktrace/webrender-test:debian-v3' env: RUST_BACKTRACE: 'full' - ############################################################# - ### Temporarily disable until https://bugzil.la/1564873 lands - ### in order to unblock https://bugzil.la/1575648 (which is - ### time-critical) - # RUSTFLAGS: '--deny warnings' - ############################################################# + RUSTFLAGS: '--deny warnings' command: - /bin/bash - '--login' @@ -145,12 +135,7 @@ tasks: source $HOME/servotidy-venv/bin/activate servo-tidy export RUST_BACKTRACE=full - ############################################################# - ### Temporarily disable until https://bugzil.la/1564873 lands - ### in order to unblock https://bugzil.la/1575648 (which is - ### time-critical) - # export RUSTFLAGS='--deny warnings' - ############################################################# + export RUSTFLAGS='--deny warnings' export PKG_CONFIG_PATH="/usr/local/opt/zlib/lib/pkgconfig:$PKG_CONFIG_PATH" echo 'exec make -j1 "$@"' > $HOME/make # See #2638 chmod +x $HOME/make @@ -179,12 +164,7 @@ tasks: source $HOME/servotidy-venv/bin/activate servo-tidy export RUST_BACKTRACE=full - ############################################################# - ### Temporarily disable until https://bugzil.la/1564873 lands - ### in order to unblock https://bugzil.la/1575648 (which is - ### time-critical) - # export RUSTFLAGS='--deny warnings' - ############################################################# + export RUSTFLAGS='--deny warnings' export PKG_CONFIG_PATH="/usr/local/opt/zlib/lib/pkgconfig:$PKG_CONFIG_PATH" echo 'exec make -j1 "$@"' > $HOME/make # See #2638 chmod +x $HOME/make From 7ed1505c3c4ce7e24a5c0d9b6d60504efcc9e69a Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Wed, 4 Mar 2020 03:51:48 +0000 Subject: [PATCH 25/33] Bug 1613260 - Increase allowed fuzz on new tests so they pass in AppVeyor CI. r=Bert Differential Revision: https://phabricator.services.mozilla.com/D65164 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/438e07547213297357465701a32a7419d6ead18f --- wrench/reftests/text/reftest.list | 2 +- wrench/reftests/transforms/reftest.list | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/wrench/reftests/text/reftest.list b/wrench/reftests/text/reftest.list index db4fba8962..a13157c2b5 100644 --- a/wrench/reftests/text/reftest.list +++ b/wrench/reftests/text/reftest.list @@ -80,4 +80,4 @@ platform(linux) allow_sacrificing_subpixel_aa(true) == text-fixed-slice.yaml tex # most pixels are off by a small amount, but a few pixels on the edge vary by a lot, pushing up the fuzzy max-diff; # the main goal of the test is that everything is in the same place, at the same scale, clipped the same way, # despite 4x on-the-fly scale change. -skip_on(android) fuzzy(115,23498) == raster_root_C_8192.yaml raster_root_C_ref.yaml +skip_on(android) fuzzy(115,23500) == raster_root_C_8192.yaml raster_root_C_ref.yaml diff --git a/wrench/reftests/transforms/reftest.list b/wrench/reftests/transforms/reftest.list index a6dcf01a70..e38df233ab 100644 --- a/wrench/reftests/transforms/reftest.list +++ b/wrench/reftests/transforms/reftest.list @@ -45,5 +45,5 @@ platform(linux,mac) fuzzy(1,74) == border-scale-4.yaml border-scale-4.png # Compare ~8K raster root (>MAX_SURFACE_SIZE) with ~2K raster root. fuzzy due to lerping on edges. skip_on(android) fuzzy(93,3692) == raster_root_A_8192.yaml raster_root_A_ref.yaml # Same as large-raster-root.yaml but resulting in a 10302×100 raster root (= >4096) vs 4000x100 in ref: -skip_on(android) fuzzy(29,333) == raster_root_B_8192.yaml raster_root_B_ref.yaml +skip_on(android) fuzzy(60,917) == raster_root_B_8192.yaml raster_root_B_ref.yaml From bf0119650c60c6e80e118092c9b828f04e33de79 Mon Sep 17 00:00:00 2001 From: Jim Blandy Date: Wed, 4 Mar 2020 03:51:57 +0000 Subject: [PATCH 26/33] Bug 1618319: Segregate intern::UpdateList insertions and removals. r=gw Rather than treating webrender::intern::UpdateList as a sequence of operations, each of which might be an insertion or a removal, and using a side table to supply extra data for insertions, it's simpler to segregate insertions and removals into two separate vectors. This avoids the need for an enum whose discriminant needs to be checked within the loop, and allows a few loops that are only looking for one kind of operation to skip over the others entirely. Ultimately, there should be no change in the order in which operations occur. In practice, the old UpdateList always held a contiguous run of insertions, followed by a run of removals (removals are consumed by apply_updates directly after being generated by end_frame_and_get_pending_updates). Differential Revision: https://phabricator.services.mozilla.com/D64444 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/06e238addde26dee65ee29f3cb676d7d47ff4e6e --- tileview/src/main.rs | 25 ++++----- webrender/src/intern.rs | 109 ++++++++++++++++++++------------------- webrender/src/lib.rs | 2 +- webrender/src/picture.rs | 17 ++---- 4 files changed, 70 insertions(+), 83 deletions(-) diff --git a/tileview/src/main.rs b/tileview/src/main.rs index 38ebfbfc22..20758185b4 100644 --- a/tileview/src/main.rs +++ b/tileview/src/main.rs @@ -15,7 +15,7 @@ use webrender::{TileNode, TileNodeKind, InvalidationReason, TileOffset}; use webrender::{TileSerializer, TileCacheInstanceSerializer, TileCacheLoggerUpdateLists}; -use webrender::{PrimitiveCompareResultDetail, CompareHelperResult, UpdateKind, ItemUid}; +use webrender::{PrimitiveCompareResultDetail, CompareHelperResult, ItemUid}; use serde::Deserialize; use std::fs::File; use std::io::prelude::*; @@ -516,20 +516,15 @@ macro_rules! updatelist_to_html_macro { html += &format!("
{}
\n
\n", stringify!($name)); for list in &update_lists.$name.1 { - let mut insert_count = 0; - for update in &list.updates { - match update.kind { - UpdateKind::Insert => { - html += &format!("
{} {}
\n", - update.uid.get_uid(), - format!("({:?})", list.data[insert_count])); - insert_count = insert_count + 1; - } - _ => { - html += &format!("
{}
\n", - update.uid.get_uid()); - } - }; + for insertion in &list.insertions { + html += &format!("
{} {}
\n", + insertion.uid.get_uid(), + format!("({:?})", insertion.value)); + } + + for removal in &list.removals { + html += &format!("
{}
\n", + removal.uid.get_uid()); } } html += "

\n"; diff --git a/webrender/src/intern.rs b/webrender/src/intern.rs index 884421c532..db7fd0c1c9 100644 --- a/webrender/src/intern.rs +++ b/webrender/src/intern.rs @@ -52,11 +52,46 @@ struct Epoch(u64); /// provided by the interning structure. #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] +#[derive(MallocSizeOf)] pub struct UpdateList { - /// The additions and removals to apply. - pub updates: Vec, - /// Actual new data to insert. - pub data: Vec, + /// Items to insert. + pub insertions: Vec>, + + /// Items to remove. + pub removals: Vec, +} + +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +#[derive(MallocSizeOf)] +pub struct Insertion { + pub index: usize, + pub uid: ItemUid, + pub value: S, +} + +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +#[derive(MallocSizeOf)] +pub struct Removal { + pub index: usize, + pub uid: ItemUid, +} + +impl UpdateList { + fn new() -> UpdateList { + UpdateList { + insertions: Vec::new(), + removals: Vec::new(), + } + } + + fn take_and_preallocate(&mut self) -> UpdateList { + UpdateList { + insertions: self.insertions.take_and_preallocate(), + removals: self.removals.take_and_preallocate(), + } + } } lazy_static! { @@ -112,23 +147,6 @@ impl Handle { } } -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -#[derive(MallocSizeOf)] -pub enum UpdateKind { - Insert, - Remove, -} - -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -#[derive(MallocSizeOf)] -pub struct Update { - pub index: usize, - pub uid: ItemUid, - pub kind: UpdateKind, -} - pub trait InternDebug { fn on_interned(&self, _uid: ItemUid) {} } @@ -158,25 +176,18 @@ impl DataStore { update_list: UpdateList, profile_counter: &mut ResourceProfileCounter, ) { - let mut data_iter = update_list.data.into_iter(); - for update in update_list.updates { - match update.kind { - UpdateKind::Insert => { - let value = data_iter.next().unwrap().into(); - self.items - .entry(update.index) - .set(Some(value)); - } - UpdateKind::Remove => { - self.items[update.index] = None; - } - } + for insertion in update_list.insertions { + self.items + .entry(insertion.index) + .set(Some(insertion.value.into())); + } + + for removal in update_list.removals { + self.items[removal.index] = None; } let per_item_size = mem::size_of::() + mem::size_of::(); profile_counter.set(self.items.len(), per_item_size * self.items.len()); - - debug_assert!(data_iter.next().is_none()); } } @@ -210,9 +221,7 @@ pub struct Interner { /// List of free slots in the data store for re-use. free_list: Vec, /// Pending list of updates that need to be applied. - updates: Vec, - /// Pending new data to insert. - update_data: Vec, + update_list: UpdateList, /// The current epoch for the interner. current_epoch: Epoch, /// The information associated with each interned @@ -225,8 +234,7 @@ impl Default for Interner { Interner { map: FastHashMap::default(), free_list: Vec::new(), - updates: Vec::new(), - update_data: Vec::new(), + update_list: UpdateList::new(), current_epoch: Epoch(1), local_data: Vec::new(), } @@ -265,12 +273,11 @@ impl Interner { let uid = ItemUid::next_uid(); // Add a pending update to insert the new data. - self.updates.push(Update { + self.update_list.insertions.push(Insertion { index, uid, - kind: UpdateKind::Insert, + value: data.clone(), }); - self.update_data.alloc().init(data.clone()); // Generate a handle for access via the data store. let handle = Handle { @@ -298,8 +305,7 @@ impl Interner { /// that need to be applied to the data store. Also run /// a GC step that removes old entries. pub fn end_frame_and_get_pending_updates(&mut self) -> UpdateList { - let mut updates = self.updates.take_and_preallocate(); - let data = self.update_data.take_and_preallocate(); + let mut update_list = self.update_list.take_and_preallocate(); let free_list = &mut self.free_list; let current_epoch = self.current_epoch.0; @@ -315,28 +321,23 @@ impl Interner { if handle.epoch.0 + 10 < current_epoch { // To expire an item: // - Add index to the free-list for re-use. - // - Add an update to the data store to invalidate this slow. + // - Add an update to the data store to invalidate this slot. // - Remove from the hash map. free_list.push(handle.index as usize); - updates.push(Update { + update_list.removals.push(Removal { index: handle.index as usize, uid: handle.uid, - kind: UpdateKind::Remove, }); return false; } true }); - let updates = UpdateList { - updates, - data, - }; // Begin the next epoch self.current_epoch = Epoch(self.current_epoch.0 + 1); - updates + update_list } } diff --git a/webrender/src/lib.rs b/webrender/src/lib.rs index 8a704d8c1c..98c11cb92a 100644 --- a/webrender/src/lib.rs +++ b/webrender/src/lib.rs @@ -224,4 +224,4 @@ pub use webrender_build::shader::ProgramSourceDigest; pub use crate::picture::{TileDescriptor, TileId, InvalidationReason}; pub use crate::picture::{PrimitiveCompareResult, PrimitiveCompareResultDetail, CompareHelperResult}; pub use crate::picture::{TileNode, TileNodeKind, TileSerializer, TileCacheInstanceSerializer, TileOffset, TileCacheLoggerUpdateLists}; -pub use crate::intern::{Update, UpdateKind, ItemUid}; +pub use crate::intern::ItemUid; diff --git a/webrender/src/picture.rs b/webrender/src/picture.rs index 41f440bde9..7f8dc37832 100644 --- a/webrender/src/picture.rs +++ b/webrender/src/picture.rs @@ -141,8 +141,6 @@ use ron; use crate::scene_builder_thread::InternerUpdates; #[cfg(any(feature = "capture", feature = "replay"))] use crate::intern::{Internable, UpdateList}; -#[cfg(any(feature = "replay"))] -use crate::intern::{UpdateKind}; #[cfg(any(feature = "capture", feature = "replay"))] use api::{ClipIntern, FilterDataIntern, PrimitiveKeyKind}; #[cfg(any(feature = "capture", feature = "replay"))] @@ -1947,17 +1945,10 @@ macro_rules! declare_tile_cache_logger_updatelists { $( { for list in &self.$name.1 { - let mut insert_count = 0; - for update in &list.updates { - match update.kind { - UpdateKind::Insert => { - itemuid_to_string.insert( - update.uid, - format!("{:?}", list.data[insert_count])); - insert_count = insert_count + 1; - }, - _ => {} - } + for insertion in &list.insertions { + itemuid_to_string.insert( + insertion.uid, + format!("{:?}", insertion.value)); } } } From a1991bc0832b25902d2a916f4a2e68f73ffe39ee Mon Sep 17 00:00:00 2001 From: Glenn Watson Date: Wed, 4 Mar 2020 08:50:49 +0000 Subject: [PATCH 27/33] Bug 1579235 - Part 10 - Fix incorrect skipping of composites. r=Bert Ensure that the image keys and image generations for external compositor surfaces are included in the composite descriptor, which is used to determine if a composite is required or can be skipped. Differential Revision: https://phabricator.services.mozilla.com/D65216 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/5f2a8eb4989755c3cf987738ea04034ac5bea896 --- webrender/src/composite.rs | 38 ++++++++++++++++++++++++++++++++++---- webrender/src/picture.rs | 23 ++++++++++++++++++++--- 2 files changed, 54 insertions(+), 7 deletions(-) diff --git a/webrender/src/composite.rs b/webrender/src/composite.rs index aedd21a077..15f6e2dd67 100644 --- a/webrender/src/composite.rs +++ b/webrender/src/composite.rs @@ -2,14 +2,14 @@ * 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::{ColorF, ImageKey, YuvColorSpace, YuvFormat, ImageRendering}; +use api::{ColorF, YuvColorSpace, YuvFormat, ImageRendering}; use api::units::{DeviceRect, DeviceIntSize, DeviceIntRect, DeviceIntPoint, WorldRect}; use api::units::{DevicePixelScale, DevicePoint, PictureRect, TexelRect}; use crate::batch::{resolve_image, get_buffer_kind}; use crate::gpu_cache::GpuCache; use crate::gpu_types::{ZBufferId, ZBufferIdGenerator}; use crate::internal_types::TextureSource; -use crate::picture::{ResolvedSurfaceTexture, TileCacheInstance, TileSurface}; +use crate::picture::{ImageDependency, ResolvedSurfaceTexture, TileCacheInstance, TileSurface}; use crate::prim_store::DeferredResolve; use crate::renderer::ImageBufferKind; use crate::resource_cache::{ImageRequest, ResourceCache}; @@ -92,7 +92,7 @@ pub struct ExternalSurfaceDescriptor { pub world_rect: WorldRect, pub device_rect: DeviceRect, pub clip_rect: DeviceRect, - pub image_keys: [ImageKey; 3], + pub image_dependencies: [ImageDependency; 3], pub image_rendering: ImageRendering, pub yuv_color_space: YuvColorSpace, pub yuv_format: YuvFormat, @@ -132,6 +132,7 @@ pub struct ResolvedExternalSurface { pub z_id: ZBufferId, // YUV specific information + pub image_dependencies: [ImageDependency; 3], pub yuv_planes: [YuvPlaneDescriptor; 3], pub yuv_color_space: YuvColorSpace, pub yuv_format: YuvFormat, @@ -229,6 +230,11 @@ pub struct CompositeSurfaceDescriptor { pub surface_id: Option, pub offset: DevicePoint, pub clip_rect: DeviceRect, + // A list of image keys and generations that this compositor surface + // depends on. This avoids composites being skipped when the only + // thing that has changed is the generation of an compositor surface + // image dependency. + pub image_dependencies: [ImageDependency; 3], } /// Describes surface properties used to composite a frame. This @@ -439,7 +445,7 @@ impl CompositeState { let mut valid_plane_count = 0; for i in 0 .. required_plane_count { - let key = external_surface.image_keys[i]; + let key = external_surface.image_dependencies[i].key; let plane = &mut yuv_planes[i]; let request = ImageRequest { @@ -491,6 +497,7 @@ impl CompositeState { yuv_format: external_surface.yuv_format, yuv_rescale: external_surface.yuv_rescale, image_buffer_kind: get_buffer_kind(yuv_planes[0].texture), + image_dependencies: external_surface.image_dependencies, yuv_planes, }); } @@ -501,16 +508,39 @@ impl CompositeState { surface_id: tile_cache.native_surface.as_ref().map(|s| s.opaque), offset: tile_cache.device_position, clip_rect: device_clip_rect, + image_dependencies: [ImageDependency::INVALID; 3], } ); } + // TODO(gw): When we merge support for native compositor surfaces, surfaces + // need to be added here for both modes. + if let CompositorKind::Draw { .. } = self.compositor_kind { + // Add a surface descriptor for each compositor surface. For the Draw + // compositor, this is used to avoid composites being skipped by adding + // a dependency on the compositor surface external image keys / generations. + for external_surface in &self.external_surfaces { + self.descriptor.surfaces.push( + CompositeSurfaceDescriptor { + // TODO(gw): When we add native compositor surfaces, this be + // need to be set, as that's how native compositor + // surfaces are added to the visual tree. + surface_id: None, + offset: external_surface.device_rect.origin, + clip_rect: external_surface.clip_rect, + image_dependencies: external_surface.image_dependencies, + } + ); + } + } + if visible_alpha_tile_count > 0 { self.descriptor.surfaces.push( CompositeSurfaceDescriptor { surface_id: tile_cache.native_surface.as_ref().map(|s| s.alpha), offset: tile_cache.device_position, clip_rect: device_clip_rect, + image_dependencies: [ImageDependency::INVALID; 3], } ); } diff --git a/webrender/src/picture.rs b/webrender/src/picture.rs index 7f8dc37832..18edf5d958 100644 --- a/webrender/src/picture.rs +++ b/webrender/src/picture.rs @@ -2991,11 +2991,21 @@ impl TileCacheInstance { let device_rect = (world_rect * frame_context.global_device_pixel_scale).round(); let clip_rect = (world_clip_rect * frame_context.global_device_pixel_scale).round(); + // Build dependency for each YUV plane, with current image generation for + // later detection of when the composited surface has changed. + let mut image_dependencies = [ImageDependency::INVALID; 3]; + for (key, dep) in prim_data.kind.yuv_key.iter().cloned().zip(image_dependencies.iter_mut()) { + *dep = ImageDependency { + key, + generation: resource_cache.get_image_generation(key), + } + } + // Each compositor surface allocates a unique z-id self.external_surfaces.push(ExternalSurfaceDescriptor { local_rect: prim_info.prim_clip_rect, world_rect, - image_keys: prim_data.kind.yuv_key, + image_dependencies, image_rendering: prim_data.kind.image_rendering, device_rect, clip_rect, @@ -5844,8 +5854,15 @@ struct PrimitiveComparisonKey { #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct ImageDependency { - key: ImageKey, - generation: ImageGeneration, + pub key: ImageKey, + pub generation: ImageGeneration, +} + +impl ImageDependency { + pub const INVALID: ImageDependency = ImageDependency { + key: ImageKey::DUMMY, + generation: ImageGeneration::INVALID, + }; } /// A helper struct to compare a primitive and all its sub-dependencies. From dab8afd0dbd44194da409ef4e69a9f0ef4ec4b79 Mon Sep 17 00:00:00 2001 From: Bert Peers Date: Thu, 5 Mar 2020 04:23:28 +0000 Subject: [PATCH 28/33] Bug 1619393 - Reftest improvements for fuzzy tests r=gw Add support for a `fuzzy-range` keyword in reftest.list. It is similar to `fuzzy` but it allows multiple pairs of `max_difference, num_differences` numbers that introduce multiple buckets of allowed differences. For example, `fuzzy-range(5,100,20,10)` allows at most 100 pixels with a difference of at most 5, _plus_ an extra 10 pixels at most that have a difference more than 5 but less than or equal to 20. The total number of differing pixels allowed is thus 110, but only if 100 of those differ by <= 5 and the remaining 10 by <= 20. 110 pixels with a difference <= 5 will still fail. This is intentional to encourage tighter bounds in tests where many pixels are slightly off and a few outliers are off by a lot. The number of parameters is arbitrary; longer lists can get confusing so this change also introduces optional support for writing `<=` in front of the max difference and `*` in front of the max pixel count, eg. `fuzzy-range(<=5,*100,<=20,*10)` (no spaces). Any pixels that exceed the highest maximum will fail the test, similar to `fuzzy`. Steps tested: 1. the same tests fail in exactly the same way before and after; 2. reordered the `fuzzy` statements for raster_root_A/B/C to no longer be sorted by max difference, and verified that the tests pass/fail the same way; (then sort them again which is easier to understand); 3. tests using the new feature still fail when the ref no longer matches (deliberately broke the _ref version and verified test failed); Differential Revision: https://phabricator.services.mozilla.com/D65247 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/8c07488c9a3f0a76e01ce34e060d071a2310612d --- wrench/reftests/text/reftest.list | 4 +- wrench/reftests/transforms/reftest.list | 2 +- wrench/src/parse_function.rs | 6 +- wrench/src/rawtest.rs | 2 +- wrench/src/reftest.rs | 167 ++++++++++++++++++++++-- 5 files changed, 161 insertions(+), 20 deletions(-) diff --git a/wrench/reftests/text/reftest.list b/wrench/reftests/text/reftest.list index a13157c2b5..b42024f5cc 100644 --- a/wrench/reftests/text/reftest.list +++ b/wrench/reftests/text/reftest.list @@ -76,8 +76,8 @@ fuzzy(1,39) options(disable-subpixel) == raster-space-snap.yaml raster-space-sna platform(linux) allow_sacrificing_subpixel_aa(false) == text-fixed-slice.yaml text-fixed-slice-slow.png platform(linux) allow_sacrificing_subpixel_aa(true) == text-fixed-slice.yaml text-fixed-slice-fast.png -# a 8544×8544 raster root vs. 2136×2136 +# a 8544x8544 raster root vs. 2136x2136 # most pixels are off by a small amount, but a few pixels on the edge vary by a lot, pushing up the fuzzy max-diff; # the main goal of the test is that everything is in the same place, at the same scale, clipped the same way, # despite 4x on-the-fly scale change. -skip_on(android) fuzzy(115,23500) == raster_root_C_8192.yaml raster_root_C_ref.yaml +skip_on(android) fuzzy-range(<=3,*20568,<=20,*2525,<=115,*543) == raster_root_C_8192.yaml raster_root_C_ref.yaml diff --git a/wrench/reftests/transforms/reftest.list b/wrench/reftests/transforms/reftest.list index e38df233ab..3630e6bb31 100644 --- a/wrench/reftests/transforms/reftest.list +++ b/wrench/reftests/transforms/reftest.list @@ -43,7 +43,7 @@ platform(linux,mac) fuzzy(1,74) == border-scale-4.yaml border-scale-4.png == strange-w.yaml strange-w-ref.yaml == big-axis-aligned-scale.yaml big-axis-aligned-scale-ref.yaml # Compare ~8K raster root (>MAX_SURFACE_SIZE) with ~2K raster root. fuzzy due to lerping on edges. -skip_on(android) fuzzy(93,3692) == raster_root_A_8192.yaml raster_root_A_ref.yaml +skip_on(android) fuzzy-range(<=3,*3077,<=10,*133,<=93,*490) == raster_root_A_8192.yaml raster_root_A_ref.yaml # Same as large-raster-root.yaml but resulting in a 10302×100 raster root (= >4096) vs 4000x100 in ref: skip_on(android) fuzzy(60,917) == raster_root_B_8192.yaml raster_root_B_ref.yaml diff --git a/wrench/src/parse_function.rs b/wrench/src/parse_function.rs index 6e695547e1..6308232b09 100644 --- a/wrench/src/parse_function.rs +++ b/wrench/src/parse_function.rs @@ -4,12 +4,12 @@ use std::str::CharIndices; -// support arguments like '4', 'ab', '4.0', '>=10.14' +// support arguments like '4', 'ab', '4.0', '>=10.14', '*123' fn acceptable_arg_character(c: char) -> bool { - c.is_alphanumeric() || c == '.' || c == '-' || c == '<' || c == '>' || c == '=' + c.is_alphanumeric() || c == '.' || c == '-' || c == '<' || c == '>' || c == '=' || c == '*' } -// A crapy parser for parsing strings like "translate(1, 3) blahblah" +// A crappy parser for parsing strings like "translate(1, 3) blahblah" // Returns a tuple with three components: // - First component is the function name (e.g. "translate") // - Second component is the list of arguments (e.g. vec!["1", "3"]) diff --git a/wrench/src/rawtest.rs b/wrench/src/rawtest.rs index eeb3af87af..030ccac79a 100644 --- a/wrench/src/rawtest.rs +++ b/wrench/src/rawtest.rs @@ -68,7 +68,7 @@ impl<'a> RawtestHarness<'a> { match image1.compare(&image2) { ReftestImageComparison::Equal => {} - ReftestImageComparison::NotEqual { max_difference, count_different } => { + ReftestImageComparison::NotEqual { max_difference, count_different, .. } => { let t = "rawtest"; println!( "{} | {} | {}: {}, {}: {}", diff --git a/wrench/src/reftest.rs b/wrench/src/reftest.rs index 98bc22451e..3fceda1eca 100644 --- a/wrench/src/reftest.rs +++ b/wrench/src/reftest.rs @@ -97,13 +97,17 @@ impl ExtraCheck { } } +pub struct RefTestFuzzy { + max_difference: usize, + num_differences: usize, +} + pub struct Reftest { op: ReftestOp, test: Vec, reference: PathBuf, font_render_mode: Option, - max_difference: usize, - num_differences: usize, + fuzziness: Vec, extra_checks: Vec, disable_dual_source_blending: bool, allow_mipmaps: bool, @@ -123,16 +127,100 @@ impl Reftest { ReftestImageComparison::Equal => { true } - ReftestImageComparison::NotEqual { max_difference, count_different } => { - if max_difference > self.max_difference || count_different > self.num_differences { + ReftestImageComparison::NotEqual { difference_histogram, max_difference, count_different } => { + // Each entry in the sorted self.fuzziness list represents a bucket which + // allows at most num_differences pixels with a difference of at most + // max_difference -- but with the caveat that a difference which is small + // enough to be less than a max_difference of an earlier bucket, must be + // counted against that bucket. + // + // Thus the test will fail if the number of pixels with a difference + // > fuzzy[j-1].max_difference and <= fuzzy[j].max_difference + // exceeds fuzzy[j].num_differences. + // + // (For the first entry, consider fuzzy[j-1] to allow zero pixels of zero + // difference). + // + // For example, say we have this histogram of differences: + // + // | [0] [1] [2] [3] [4] [5] [6] ... [255] + // ------+------------------------------------------ + // Hist. | 0 3 2 1 6 2 0 ... 0 + // + // Ie. image comparison found 3 pixels that differ by 1, 2 that differ by 2, etc. + // (Note that entry 0 is always zero, we don't count matching pixels.) + // + // First we calculate an inclusive prefix sum: + // + // | [0] [1] [2] [3] [4] [5] [6] ... [255] + // ------+------------------------------------------ + // Hist. | 0 3 2 1 6 2 0 ... 0 + // Sum | 0 3 5 6 12 14 14 ... 14 + // + // Let's say the fuzzy statements are: + // Fuzzy( 2, 6 ) -- allow up to 6 pixels that differ by 2 or less + // Fuzzy( 4, 8 ) -- allow up to 8 pixels that differ by 4 or less _but_ + // also by more than 2 (= by 3 or 4). + // + // The first check is Sum[2] <= max 6 which passes: 5 <= 6. + // The second check is Sum[4] - Sum[2] <= max 8 which passes: 12-5 <= 8. + // Finally we check if there are any pixels that exceed the max difference (4) + // by checking Sum[255] - Sum[4] which shows there are 14-12 == 2 so we fail. + + let prefix_sum = difference_histogram.iter() + .scan(0, |sum, i| { *sum += i; Some(*sum) }) + .collect::>(); + + // check each fuzzy statement for violations. + assert_eq!(0, difference_histogram[0]); + assert_eq!(0, prefix_sum[0]); + + // loop invariant: this is the max_difference of the previous iteration's 'fuzzy' + let mut previous_max_diff = 0; + + // loop invariant: this is the number of pixels to ignore as they have been counted + // against previous iterations' fuzzy statements. + let mut previous_sum_fail = 0; // == prefix_sum[previous_max_diff] + + let mut is_failing = false; + let mut fail_text = String::new(); + + for fuzzy in &self.fuzziness { + let fuzzy_max_difference = cmp::min(255, fuzzy.max_difference); + let num_differences = prefix_sum[fuzzy_max_difference] - previous_sum_fail; + if num_differences > fuzzy.num_differences { + fail_text.push_str( + &format!("{} differences > {} and <= {} (allowed {}); ", + num_differences, + previous_max_diff, fuzzy_max_difference, + fuzzy.num_differences)); + is_failing = true; + } + previous_max_diff = fuzzy_max_difference; + previous_sum_fail = prefix_sum[previous_max_diff]; + } + // do we have any pixels with a difference above the highest allowed + // max difference? if so, we fail the test: + let num_differences = prefix_sum[255] - previous_sum_fail; + if num_differences > 0 { + fail_text.push_str( + &format!("{} num_differences > {} and <= {} (allowed {}); ", + num_differences, + previous_max_diff, 255, + 0)); + is_failing = true; + } + + if is_failing { println!( - "{} | {} | {}: {}, {}: {}", + "{} | {} | {}: {}, {}: {} | {}", "REFTEST TEST-UNEXPECTED-FAIL", self, "image comparison, max difference", max_difference, "number of differing pixels", - count_different + count_different, + fail_text, ); println!("REFTEST IMAGE 1 (TEST): {}", test.clone().create_data_uri()); println!( @@ -175,10 +263,12 @@ pub struct ReftestImage { pub size: DeviceIntSize, } -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Clone)] pub enum ReftestImageComparison { Equal, NotEqual { + /// entry[j] = number of pixels with a difference of exactly j + difference_histogram: Vec, max_difference: usize, count_different: usize, }, @@ -190,6 +280,7 @@ impl ReftestImage { assert_eq!(self.data.len(), other.data.len()); assert_eq!(self.data.len() % 4, 0); + let mut histogram = [0usize; 256]; let mut count = 0; let mut max = 0; @@ -202,12 +293,19 @@ impl ReftestImage { .unwrap(); count += 1; + assert!(pixel_max < 256, "pixel values are not 8 bit, update the histogram binning code"); + // deliberately avoid counting pixels that match -- + // histogram[0] stays at zero. + // this helps our prefix sum later during analysis to + // only count actual differences. + histogram[pixel_max as usize] += 1; max = cmp::max(max, pixel_max); } } if count != 0 { ReftestImageComparison::NotEqual { + difference_histogram: histogram.to_vec(), max_difference: max, count_different: count, } @@ -267,8 +365,7 @@ impl ReftestManifest { let tokens: Vec<&str> = s.split_whitespace().collect(); - let mut max_difference = 0; - let mut max_count = 0; + let mut fuzziness = Vec::new(); let mut op = None; let mut font_render_mode = None; let mut extra_checks = vec![]; @@ -314,10 +411,29 @@ impl ReftestManifest { let (_, args, _) = parse_function(function); allow_sacrificing_subpixel_aa = Some(args[0].parse().unwrap()); } + function if function.starts_with("fuzzy-range") => { // make sure this comes before 'fuzzy' + let (_, args, _) = parse_function(function); + let num_range = args.len() / 2; + for range in 0..num_range { + let mut max = args[range * 2 + 0]; + let mut num = args[range * 2 + 1]; + if max.starts_with("<=") { // trim_start_matches would allow <=<=123 + max = &max[2..]; + } + if num.starts_with("*") { + num = &num[1..]; + } + let max_difference = max.parse().unwrap(); + let num_differences = num.parse().unwrap(); + fuzziness.push(RefTestFuzzy { max_difference, num_differences }); + } + } function if function.starts_with("fuzzy") => { let (_, args, _) = parse_function(function); - max_difference = args[0].parse().unwrap(); - max_count = args[1].parse().unwrap(); + let max_difference = args[0].parse().unwrap(); + let num_differences = args[1].parse().unwrap(); + assert!(fuzziness.is_empty()); // if this fires, consider fuzzy-range instead + fuzziness.push(RefTestFuzzy { max_difference, num_differences }); } function if function.starts_with("draw_calls") => { let (_, args, _) = parse_function(function); @@ -389,13 +505,38 @@ impl ReftestManifest { let reference = paths.pop().unwrap(); let test = paths; + // to avoid changing the meaning of existing tests, the case of + // only a single (or no) 'fuzzy' keyword means we use the max + // of that fuzzy and options.allow_.. (we don't want that to + // turn into a test that allows fuzzy.allow_ *plus* options.allow_): + match fuzziness.len() { + 0 => fuzziness.push(RefTestFuzzy { + max_difference: options.allow_max_difference, + num_differences: options.allow_num_differences }), + 1 => { + let mut fuzzy = &mut fuzziness[0]; + fuzzy.max_difference = cmp::max(fuzzy.max_difference, options.allow_max_difference); + fuzzy.num_differences = cmp::max(fuzzy.num_differences, options.allow_num_differences); + }, + _ => { + // ignore options, use multiple fuzzy keywords instead. make sure + // the list is sorted to speed up counting violations. + fuzziness.sort_by(|a, b| a.max_difference.cmp(&b.max_difference)); + for pair in fuzziness.windows(2) { + if pair[0].max_difference == pair[1].max_difference { + println!("Warning: repeated fuzzy of max_difference {} ignored.", + pair[1].max_difference); + } + } + } + } + reftests.push(Reftest { op, test, reference, font_render_mode, - max_difference: cmp::max(max_difference, options.allow_max_difference), - num_differences: cmp::max(max_count, options.allow_num_differences), + fuzziness, extra_checks, disable_dual_source_blending, allow_mipmaps, From 8d121d881bf84f66f94f33d6271e16d575aab78d Mon Sep 17 00:00:00 2001 From: Tim Nguyen Date: Thu, 5 Mar 2020 10:04:20 +0000 Subject: [PATCH 29/33] Bug 1616255 - Handle start and end offsets in conic-gradient WR shader. r=gw Differential Revision: https://phabricator.services.mozilla.com/D65391 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/010e798483903efee1caac88c01ba2b172d13c09 --- webrender/res/brush_conic_gradient.glsl | 19 ++++++++++----- webrender/src/prim_store/gradient.rs | 32 ++++++++++++++----------- webrender/src/prim_store/mod.rs | 6 ++--- webrender/src/scene_building.rs | 10 ++++++-- webrender_api/src/gradient_builder.rs | 2 -- wrench/reftests/gradient/reftest.list | 10 ++++---- 6 files changed, 46 insertions(+), 33 deletions(-) diff --git a/webrender/res/brush_conic_gradient.glsl b/webrender/res/brush_conic_gradient.glsl index fcc6d4912a..56bd3c25a9 100644 --- a/webrender/res/brush_conic_gradient.glsl +++ b/webrender/res/brush_conic_gradient.glsl @@ -13,7 +13,9 @@ #define V_GRADIENT_ADDRESS flat_varying_highp_int_address_0 #define V_CENTER flat_varying_vec4_0.xy -#define V_ANGLE flat_varying_vec4_0.z +#define V_START_OFFSET flat_varying_vec4_0.z +#define V_END_OFFSET flat_varying_vec4_0.w +#define V_ANGLE flat_varying_vec4_1.w // Size of the gradient pattern's rectangle, used to compute horizontal and vertical // repetitions. Not to be confused with another kind of repetition of the pattern @@ -29,12 +31,13 @@ #define V_TILE_REPEAT flat_varying_vec4_2.xy #endif -#define PI 3.1415926538 +#define PI 3.141592653589793 #ifdef WR_VERTEX_SHADER struct ConicGradient { vec2 center_point; + vec2 start_end_offset; float angle; int extend_mode; vec2 stretch_size; @@ -44,9 +47,10 @@ ConicGradient fetch_gradient(int address) { vec4 data[2] = fetch_from_gpu_cache_2(address); return ConicGradient( data[0].xy, - float(data[0].z), - int(data[1].x), - data[1].yz + data[0].zw, + float(data[1].x), + int(data[1].y), + data[1].zw ); } @@ -74,6 +78,8 @@ void conic_gradient_brush_vs( V_CENTER = gradient.center_point; V_ANGLE = gradient.angle; + V_START_OFFSET = gradient.start_end_offset.x; + V_END_OFFSET = gradient.start_end_offset.y; vec2 tile_repeat = local_rect.size / gradient.stretch_size; V_REPEATED_SIZE = gradient.stretch_size; @@ -115,7 +121,8 @@ Fragment conic_gradient_brush_fs() { vec2 current_dir = pos - V_CENTER; float current_angle = atan(current_dir.y, current_dir.x) + (PI / 2.0 - V_ANGLE); - float offset = mod(current_angle / (2.0 * PI), 1.0); + float offset = mod(current_angle / (2.0 * PI), 1.0) - V_START_OFFSET; + offset = offset / (V_END_OFFSET - V_START_OFFSET); vec4 color = sample_gradient(V_GRADIENT_ADDRESS, offset, diff --git a/webrender/src/prim_store/gradient.rs b/webrender/src/prim_store/gradient.rs index 58021ace07..b8d59c8030 100644 --- a/webrender/src/prim_store/gradient.rs +++ b/webrender/src/prim_store/gradient.rs @@ -568,15 +568,19 @@ impl IsVisible for RadialGradient { #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] #[derive(Debug, Clone, MallocSizeOf, PartialEq)] -pub struct ConicGradientAngle { +pub struct ConicGradientParams { pub angle: f32, // in radians + pub start_offset: f32, + pub end_offset: f32, } -impl Eq for ConicGradientAngle {} +impl Eq for ConicGradientParams {} -impl hash::Hash for ConicGradientAngle { +impl hash::Hash for ConicGradientParams { fn hash(&self, state: &mut H) { self.angle.to_bits().hash(state); + self.start_offset.to_bits().hash(state); + self.end_offset.to_bits().hash(state); } } @@ -588,7 +592,7 @@ pub struct ConicGradientKey { pub common: PrimKeyCommonData, pub extend_mode: ExtendMode, pub center: PointKey, - pub angle: ConicGradientAngle, + pub params: ConicGradientParams, pub stretch_size: SizeKey, pub stops: Vec, pub tile_spacing: SizeKey, @@ -608,7 +612,7 @@ impl ConicGradientKey { }, extend_mode: conic_grad.extend_mode, center: conic_grad.center, - angle: conic_grad.angle, + params: conic_grad.params, stretch_size: conic_grad.stretch_size, stops: conic_grad.stops, tile_spacing: conic_grad.tile_spacing, @@ -626,7 +630,7 @@ pub struct ConicGradientTemplate { pub common: PrimTemplateCommonData, pub extend_mode: ExtendMode, pub center: LayoutPoint, - pub angle: ConicGradientAngle, + pub params: ConicGradientParams, pub stretch_size: LayoutSize, pub tile_spacing: LayoutSize, pub brush_segments: Vec, @@ -667,7 +671,7 @@ impl From for ConicGradientTemplate { common, center: item.center.into(), extend_mode: item.extend_mode, - angle: item.angle, + params: item.params, stretch_size: item.stretch_size.into(), tile_spacing: item.tile_spacing.into(), brush_segments, @@ -692,14 +696,14 @@ impl ConicGradientTemplate { request.push([ self.center.x, self.center.y, - self.angle.angle, - 0.0, + self.params.start_offset, + self.params.end_offset, ]); request.push([ + self.params.angle, pack_as_float(self.extend_mode as u32), self.stretch_size.width, self.stretch_size.height, - 0.0, ]); // write_segment_gpu_blocks @@ -732,7 +736,7 @@ pub type ConicGradientDataHandle = InternHandle; pub struct ConicGradient { pub extend_mode: ExtendMode, pub center: PointKey, - pub angle: ConicGradientAngle, + pub params: ConicGradientParams, pub stretch_size: SizeKey, pub stops: Vec, pub tile_spacing: SizeKey, @@ -1000,7 +1004,7 @@ fn test_struct_sizes() { assert_eq!(mem::size_of::(), 120, "RadialGradientTemplate size changed"); assert_eq!(mem::size_of::(), 88, "RadialGradientKey size changed"); - assert_eq!(mem::size_of::(), 64, "ConicGradient size changed"); - assert_eq!(mem::size_of::(), 112, "ConicGradientTemplate size changed"); - assert_eq!(mem::size_of::(), 80, "ConicGradientKey size changed"); + assert_eq!(mem::size_of::(), 72, "ConicGradient size changed"); + assert_eq!(mem::size_of::(), 120, "ConicGradientTemplate size changed"); + assert_eq!(mem::size_of::(), 88, "ConicGradientKey size changed"); } diff --git a/webrender/src/prim_store/mod.rs b/webrender/src/prim_store/mod.rs index d50a2ad5ee..3b53b710a3 100644 --- a/webrender/src/prim_store/mod.rs +++ b/webrender/src/prim_store/mod.rs @@ -3440,14 +3440,14 @@ impl PrimitiveStore { request.push([ prim_data.center.x, prim_data.center.y, - prim_data.angle.angle, - 0.0, + prim_data.params.start_offset, + prim_data.params.end_offset, ]); request.push([ + prim_data.params.angle, pack_as_float(prim_data.extend_mode as u32), prim_data.stretch_size.width, prim_data.stretch_size.height, - 0.0, ]); }, ); diff --git a/webrender/src/scene_building.rs b/webrender/src/scene_building.rs index d94cb06bf7..04c3ad498b 100644 --- a/webrender/src/scene_building.rs +++ b/webrender/src/scene_building.rs @@ -31,7 +31,7 @@ use crate::prim_store::{register_prim_chase_id, get_line_decoration_size}; use crate::prim_store::{SpaceSnapper}; use crate::prim_store::backdrop::Backdrop; use crate::prim_store::borders::{ImageBorder, NormalBorderPrim}; -use crate::prim_store::gradient::{GradientStopKey, LinearGradient, RadialGradient, RadialGradientParams, ConicGradient, ConicGradientAngle}; +use crate::prim_store::gradient::{GradientStopKey, LinearGradient, RadialGradient, RadialGradientParams, ConicGradient, ConicGradientParams}; use crate::prim_store::image::{Image, YuvImage}; use crate::prim_store::line_dec::{LineDecoration, LineDecorationCacheKey}; use crate::prim_store::picture::{Picture, PictureCompositeKey, PictureKey}; @@ -1326,6 +1326,8 @@ impl<'a> SceneBuilder<'a> { &layout, info.gradient.center, info.gradient.angle, + info.gradient.start_offset, + info.gradient.end_offset, item.gradient_stops(), info.gradient.extend_mode, tile_size, @@ -2940,6 +2942,8 @@ impl<'a> SceneBuilder<'a> { &info, gradient.center, gradient.angle, + gradient.start_offset, + gradient.end_offset, gradient_stops, gradient.extend_mode, LayoutSize::new(border.height as f32, border.width as f32), @@ -3072,6 +3076,8 @@ impl<'a> SceneBuilder<'a> { info: &LayoutPrimitiveInfo, center: LayoutPoint, angle: f32, + start_offset: f32, + end_offset: f32, stops: ItemRange, extend_mode: ExtendMode, stretch_size: LayoutSize, @@ -3091,7 +3097,7 @@ impl<'a> SceneBuilder<'a> { ConicGradient { extend_mode, center: center.into(), - angle: ConicGradientAngle { angle }, + params: ConicGradientParams { angle, start_offset, end_offset }, stretch_size: stretch_size.into(), tile_spacing: tile_spacing.into(), nine_patch, diff --git a/webrender_api/src/gradient_builder.rs b/webrender_api/src/gradient_builder.rs index ef3dec8d16..883acbafa3 100644 --- a/webrender_api/src/gradient_builder.rs +++ b/webrender_api/src/gradient_builder.rs @@ -106,8 +106,6 @@ impl GradientBuilder { angle: f32, extend_mode: di::ExtendMode, ) -> di::ConicGradient { - // XXX(ntim): Possibly handle negative angles ? - let (start_offset, end_offset) = self.normalize(extend_mode); diff --git a/wrench/reftests/gradient/reftest.list b/wrench/reftests/gradient/reftest.list index 8b5789964f..79fef2120c 100644 --- a/wrench/reftests/gradient/reftest.list +++ b/wrench/reftests/gradient/reftest.list @@ -42,9 +42,8 @@ platform(linux,mac) fuzzy(1,80000) == radial-ellipse.yaml radial-ellipse-ref.png == norm-conic-1.yaml norm-conic-1-ref.yaml == norm-conic-2.yaml norm-conic-2-ref.yaml -# Bug 1616255 - These should pass -# == norm-conic-3.yaml norm-conic-3-ref.yaml -# == norm-conic-4.yaml norm-conic-4-ref.yaml +== norm-conic-3.yaml norm-conic-3-ref.yaml +== norm-conic-4.yaml norm-conic-4-ref.yaml == norm-conic-degenerate.yaml norm-conic-degenerate-ref.yaml # fuzzy because of differences from normalization @@ -53,9 +52,8 @@ fuzzy(255,1200) == repeat-linear.yaml repeat-linear-ref.yaml fuzzy(255,1200) == repeat-linear-reverse.yaml repeat-linear-ref.yaml fuzzy(255,2664) == repeat-radial.yaml repeat-radial-ref.yaml fuzzy(255,2664) == repeat-radial-negative.yaml repeat-radial-ref.yaml -# Bug 1616255 - These should pass -# == repeat-conic.yaml repeat-conic-ref.yaml -# == repeat-conic-negative.yaml repeat-conic-ref.yaml +fuzzy(255,1652) == repeat-conic.yaml repeat-conic-ref.yaml +fuzzy(255,1652) == repeat-conic-negative.yaml repeat-conic-ref.yaml # fuzzy because of thin spaced out column of pixels that are 1 off fuzzy(1,83164) == tiling-linear-1.yaml tiling-linear-1-ref.yaml From 256215eb5ed5a4ad52343dc2482e4c3d56e00037 Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Fri, 6 Mar 2020 09:57:19 +0000 Subject: [PATCH 30/33] Bug 1619393 - Increase fuzz by one to allow reftest to pass on AppVeyor. r=Bert Differential Revision: https://phabricator.services.mozilla.com/D65513 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/3c67e9f15e9361e595477f16e13e018b9aab35c0 --- wrench/reftests/text/reftest.list | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wrench/reftests/text/reftest.list b/wrench/reftests/text/reftest.list index b42024f5cc..60dc7e3d3c 100644 --- a/wrench/reftests/text/reftest.list +++ b/wrench/reftests/text/reftest.list @@ -80,4 +80,4 @@ platform(linux) allow_sacrificing_subpixel_aa(true) == text-fixed-slice.yaml tex # most pixels are off by a small amount, but a few pixels on the edge vary by a lot, pushing up the fuzzy max-diff; # the main goal of the test is that everything is in the same place, at the same scale, clipped the same way, # despite 4x on-the-fly scale change. -skip_on(android) fuzzy-range(<=3,*20568,<=20,*2525,<=115,*543) == raster_root_C_8192.yaml raster_root_C_ref.yaml +skip_on(android) fuzzy-range(<=3,*20569,<=20,*2525,<=115,*543) == raster_root_C_8192.yaml raster_root_C_ref.yaml From 5aad73f5dc132497ddcd75d62d639161f9f13937 Mon Sep 17 00:00:00 2001 From: Glenn Watson Date: Fri, 6 Mar 2020 09:57:28 +0000 Subject: [PATCH 31/33] Bug 1579235 - Part 11 - Refactor how external surfaces are composited. r=Bert,sotaro This patch refactors how external surfaces are stored in the CompositeState structure. This is primarily to simplify integration with native compositor mode, but also simplifies the Draw compositor path. Previously, the ResolvedExternalSurface struct contained information that was used to rasterize the external surface (YUV planes etc) and also the information to composite it (device rect, clip rect, z_id). Now, ResolvedExternalSurface contains just the information required to rasterize the external surface, while the compositing information is handled by adding the external surface as a regular tile. This makes it possible to unify how external surfaces are drawn, via the common draw_tile_list method. Differential Revision: https://phabricator.services.mozilla.com/D65269 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/4bbb79cbf9cc9efc7d599d1fe8d68bf620601fc9 --- webrender/src/composite.rs | 97 +++++++------- webrender/src/renderer.rs | 260 +++++++++++++++++-------------------- 2 files changed, 170 insertions(+), 187 deletions(-) diff --git a/webrender/src/composite.rs b/webrender/src/composite.rs index 15f6e2dd67..b51ab8b639 100644 --- a/webrender/src/composite.rs +++ b/webrender/src/composite.rs @@ -62,10 +62,13 @@ pub enum CompositeTileSurface { color: ColorF, }, Clear, + ExternalSurface { + external_surface_index: ResolvedExternalSurfaceIndex, + }, } /// The surface format for a tile being composited. -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, PartialEq)] pub enum CompositeSurfaceFormat { Rgba, Yuv, @@ -81,7 +84,6 @@ pub struct CompositeTile { pub dirty_rect: DeviceRect, pub valid_rect: DeviceRect, pub z_id: ZBufferId, - pub tile_id: Option, } /// Describes information about drawing a primitive as a compositor surface. @@ -119,6 +121,11 @@ impl YuvPlaneDescriptor { } } +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +#[derive(Debug, Copy, Clone)] +pub struct ResolvedExternalSurfaceIndex(pub usize); + /// An ExternalSurfaceDescriptor that has had image keys /// resolved to texture handles. This contains all the /// information that the compositor step in renderer @@ -126,11 +133,6 @@ impl YuvPlaneDescriptor { #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct ResolvedExternalSurface { - // Common information - pub device_rect: DeviceRect, - pub clip_rect: DeviceRect, - pub z_id: ZBufferId, - // YUV specific information pub image_dependencies: [ImageDependency; 3], pub yuv_planes: [YuvPlaneDescriptor; 3], @@ -391,23 +393,18 @@ impl CompositeState { let device_rect = (tile.world_tile_rect * global_device_pixel_scale).round(); let surface = tile.surface.as_ref().expect("no tile surface set!"); - let (surface, is_opaque, tile_id) = match surface { + let (surface, is_opaque) = match surface { TileSurface::Color { color } => { - (CompositeTileSurface::Color { color: *color }, true, None) + (CompositeTileSurface::Color { color: *color }, true) } TileSurface::Clear => { - (CompositeTileSurface::Clear, false, None) + (CompositeTileSurface::Clear, false) } TileSurface::Texture { descriptor, .. } => { let surface = descriptor.resolve(resource_cache, tile_cache.current_tile_size); - let tile_id = match surface { - ResolvedSurfaceTexture::Native { id, .. } => Some(id), - ResolvedSurfaceTexture::TextureCache { .. } => None, - }; ( CompositeTileSurface::Texture { surface }, tile.is_opaque || tile_cache.is_opaque(), - tile_id, ) } }; @@ -425,12 +422,23 @@ impl CompositeState { dirty_rect: tile.device_dirty_rect.translate(-device_rect.origin.to_vector()), clip_rect: device_clip_rect, z_id: tile.z_id, - tile_id, }; self.push_tile(tile, is_opaque); } + // Add opaque surface before any compositor surfaces + if visible_opaque_tile_count > 0 { + self.descriptor.surfaces.push( + CompositeSurfaceDescriptor { + surface_id: tile_cache.native_surface.as_ref().map(|s| s.opaque), + offset: tile_cache.device_position, + clip_rect: device_clip_rect, + image_dependencies: [ImageDependency::INVALID; 3], + } + ); + } + // For each compositor surface that was promoted, build the // information required for the compositor to draw it for external_surface in &tile_cache.external_surfaces { @@ -489,10 +497,11 @@ impl CompositeState { // Get a new z_id for each compositor surface, to ensure correct ordering // when drawing with the simple (Draw) compositor. + let surface = CompositeTileSurface::ExternalSurface { + external_surface_index: ResolvedExternalSurfaceIndex(self.external_surfaces.len()), + }; + self.external_surfaces.push(ResolvedExternalSurface { - device_rect: external_surface.device_rect, - clip_rect, - z_id: external_surface.z_id, yuv_color_space: external_surface.yuv_color_space, yuv_format: external_surface.yuv_format, yuv_rescale: external_surface.yuv_rescale, @@ -500,40 +509,35 @@ impl CompositeState { image_dependencies: external_surface.image_dependencies, yuv_planes, }); - } - if visible_opaque_tile_count > 0 { + let tile = CompositeTile { + surface, + rect: external_surface.device_rect, + valid_rect: external_surface.device_rect.translate(-external_surface.device_rect.origin.to_vector()), + dirty_rect: external_surface.device_rect.translate(-external_surface.device_rect.origin.to_vector()), + clip_rect, + z_id: external_surface.z_id, + }; + + // Add a surface descriptor for each compositor surface. For the Draw + // compositor, this is used to avoid composites being skipped by adding + // a dependency on the compositor surface external image keys / generations. self.descriptor.surfaces.push( CompositeSurfaceDescriptor { - surface_id: tile_cache.native_surface.as_ref().map(|s| s.opaque), - offset: tile_cache.device_position, - clip_rect: device_clip_rect, - image_dependencies: [ImageDependency::INVALID; 3], + // TODO(gw): When we add native compositor surfaces, this be + // need to be set, as that's how native compositor + // surfaces are added to the visual tree. + surface_id: None, + offset: tile.rect.origin, + clip_rect: tile.clip_rect, + image_dependencies: external_surface.image_dependencies, } ); - } - // TODO(gw): When we merge support for native compositor surfaces, surfaces - // need to be added here for both modes. - if let CompositorKind::Draw { .. } = self.compositor_kind { - // Add a surface descriptor for each compositor surface. For the Draw - // compositor, this is used to avoid composites being skipped by adding - // a dependency on the compositor surface external image keys / generations. - for external_surface in &self.external_surfaces { - self.descriptor.surfaces.push( - CompositeSurfaceDescriptor { - // TODO(gw): When we add native compositor surfaces, this be - // need to be set, as that's how native compositor - // surfaces are added to the visual tree. - surface_id: None, - offset: external_surface.device_rect.origin, - clip_rect: external_surface.clip_rect, - image_dependencies: external_surface.image_dependencies, - } - ); - } + self.push_tile(tile, true); } + // Add alpha / overlay tiles after compositor surfaces if visible_alpha_tile_count > 0 { self.descriptor.surfaces.push( CompositeSurfaceDescriptor { @@ -571,6 +575,9 @@ impl CompositeState { self.alpha_tiles.push(tile); } } + CompositeTileSurface::ExternalSurface { .. } => { + self.opaque_tiles.push(tile); + } } } } diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index 366ca8bb2f..edae98bf75 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -38,7 +38,7 @@ use api::{ApiMsg, BlobImageHandler, ColorF, ColorU, MixBlendMode}; use api::{DocumentId, Epoch, ExternalImageHandler, ExternalImageId}; use api::{ExternalImageSource, ExternalImageType, FontRenderMode, FrameMsg, ImageFormat}; use api::{PipelineId, ImageRendering, Checkpoint, NotificationRequest, OutputImageHandler}; -use api::{DebugCommand, MemoryReport, VoidPtrToSizeFn}; +use api::{DebugCommand, MemoryReport, VoidPtrToSizeFn, PremultipliedColorF}; use api::{RenderApiSender, RenderNotifier, TextureTarget}; #[cfg(feature = "replay")] use api::ExternalImage; @@ -49,7 +49,7 @@ use api::channel::MsgSender; use crate::batch::{AlphaBatchContainer, BatchKind, BatchFeatures, BatchTextures, BrushBatchKind, ClipBatchList}; #[cfg(any(feature = "capture", feature = "replay"))] use crate::capture::{CaptureConfig, ExternalCaptureImage, PlainExternalImage}; -use crate::composite::{CompositeState, CompositeTileSurface, CompositeTile}; +use crate::composite::{CompositeState, CompositeTileSurface, CompositeTile, ResolvedExternalSurface}; use crate::composite::{CompositorKind, Compositor, NativeTileId, CompositeSurfaceFormat}; use crate::composite::{CompositorConfig, NativeSurfaceOperationDetails, NativeSurfaceId, NativeSurfaceOperation}; use crate::debug_colors; @@ -4255,6 +4255,7 @@ impl Renderer { fn draw_tile_list<'a, I: Iterator>( &mut self, tiles_iter: I, + external_surfaces: &[ResolvedExternalSurface], projection: &default::Transform3D, partial_present_mode: Option, stats: &mut RendererStats, @@ -4270,27 +4271,11 @@ impl Renderer { &mut self.renderer_errors ); + let mut current_shader_params = (CompositeSurfaceFormat::Rgba, ImageBufferKind::Texture2DArray); let mut current_textures = BatchTextures::no_texture(); let mut instances = Vec::new(); for tile in tiles_iter { - // Work out the draw params based on the tile surface - let (texture, layer, color) = match tile.surface { - CompositeTileSurface::Color { color } => { - (TextureSource::Dummy, 0.0, color) - } - CompositeTileSurface::Clear => { - (TextureSource::Dummy, 0.0, ColorF::BLACK) - } - CompositeTileSurface::Texture { surface: ResolvedSurfaceTexture::TextureCache { texture, layer } } => { - (texture, layer as f32, ColorF::WHITE) - } - CompositeTileSurface::Texture { surface: ResolvedSurfaceTexture::Native { .. } } => { - unreachable!("bug: found native surface in simple composite path"); - } - }; - let textures = BatchTextures::color(texture); - // Determine a clip rect to apply to this tile, depending on what // the partial present mode is. let partial_clip_rect = match partial_present_mode { @@ -4314,113 +4299,125 @@ impl Renderer { None => continue, }; - // Flush this batch if the textures aren't compatible - if !current_textures.is_compatible_with(&textures) { - self.draw_instanced_batch( - &instances, - VertexArrayKind::Composite, - ¤t_textures, - stats, - ); - instances.clear(); - } - current_textures = textures; - - // Create the instance and add to current batch - let instance = CompositeInstance::new( - tile.rect, - clip_rect, - color.premultiplied(), - layer, - tile.z_id, - ); - instances.push(instance); - } - - // Flush the last batch - if !instances.is_empty() { - self.draw_instanced_batch( - &instances, - VertexArrayKind::Composite, - ¤t_textures, - stats, - ); - } - } + // Work out the draw params based on the tile surface + let (instance, textures, shader_params) = match tile.surface { + CompositeTileSurface::Color { color } => { + ( + CompositeInstance::new( + tile.rect, + clip_rect, + color.premultiplied(), + 0.0, + tile.z_id, + ), + BatchTextures::color(TextureSource::Dummy), + (CompositeSurfaceFormat::Rgba, ImageBufferKind::Texture2DArray), + ) + } + CompositeTileSurface::Clear => { + ( + CompositeInstance::new( + tile.rect, + clip_rect, + PremultipliedColorF::BLACK, + 0.0, + tile.z_id, + ), + BatchTextures::color(TextureSource::Dummy), + (CompositeSurfaceFormat::Rgba, ImageBufferKind::Texture2DArray), + ) + } + CompositeTileSurface::Texture { surface: ResolvedSurfaceTexture::TextureCache { texture, layer } } => { + ( + CompositeInstance::new( + tile.rect, + clip_rect, + PremultipliedColorF::WHITE, + layer as f32, + tile.z_id, + ), + BatchTextures::color(texture), + (CompositeSurfaceFormat::Rgba, ImageBufferKind::Texture2DArray), + ) + } + CompositeTileSurface::ExternalSurface { external_surface_index } => { + let surface = &external_surfaces[external_surface_index.0]; + + let textures = BatchTextures { + colors: [ + surface.yuv_planes[0].texture, + surface.yuv_planes[1].texture, + surface.yuv_planes[2].texture, + ], + }; - /// Draw a list of external compositor surfaces - fn draw_external_surface_list( - &mut self, - composite_state: &CompositeState, - projection: &default::Transform3D, - stats: &mut RendererStats, - ) { - // Only opaque compositor surfaces are supported - let opaque_sampler = self.gpu_profile.start_sampler(GPU_SAMPLER_TAG_OPAQUE); - self.set_blend(false, FramebufferKind::Main); + // When the texture is an external texture, the UV rect is not known when + // the external surface descriptor is created, because external textures + // are not resolved until the lock() callback is invoked at the start of + // the frame render. To handle this, query the texture resolver for the + // UV rect if it's an external texture, otherwise use the default UV rect. + let uv_rects = [ + self.texture_resolver.get_uv_rect(&textures.colors[0], surface.yuv_planes[0].uv_rect), + self.texture_resolver.get_uv_rect(&textures.colors[1], surface.yuv_planes[1].uv_rect), + self.texture_resolver.get_uv_rect(&textures.colors[2], surface.yuv_planes[2].uv_rect), + ]; + + ( + CompositeInstance::new_yuv( + tile.rect, + clip_rect, + tile.z_id, + surface.yuv_color_space, + surface.yuv_format, + surface.yuv_rescale, + [ + surface.yuv_planes[0].texture_layer as f32, + surface.yuv_planes[1].texture_layer as f32, + surface.yuv_planes[2].texture_layer as f32, + ], + uv_rects, + ), + textures, + (CompositeSurfaceFormat::Yuv, surface.image_buffer_kind), + ) + } + CompositeTileSurface::Texture { surface: ResolvedSurfaceTexture::Native { .. } } => { + unreachable!("bug: found native surface in simple composite path"); + } + }; - let mut current_textures = BatchTextures::no_texture(); - let mut instances = Vec::new(); + // Flush batch if shader params or textures changed + let flush_batch = !current_textures.is_compatible_with(&textures) || + shader_params != current_shader_params; - for surface in &composite_state.external_surfaces { - // Bind an appropriate YUV shader for the texture format kind - self.shaders - .borrow_mut() - .get_composite_shader( - CompositeSurfaceFormat::Yuv, - surface.image_buffer_kind, - ).bind( - &mut self.device, - projection, - &mut self.renderer_errors - ); + if flush_batch { + if !instances.is_empty() { + self.draw_instanced_batch( + &instances, + VertexArrayKind::Composite, + ¤t_textures, + stats, + ); + instances.clear(); + } + } - let textures = BatchTextures { - colors: [ - surface.yuv_planes[0].texture, - surface.yuv_planes[1].texture, - surface.yuv_planes[2].texture, - ], - }; + if shader_params != current_shader_params { + self.shaders + .borrow_mut() + .get_composite_shader(shader_params.0, shader_params.1) + .bind( + &mut self.device, + projection, + &mut self.renderer_errors + ); - // Flush this batch if the textures aren't compatible - if !current_textures.is_compatible_with(&textures) { - self.draw_instanced_batch( - &instances, - VertexArrayKind::Composite, - ¤t_textures, - stats, - ); - instances.clear(); + current_shader_params = shader_params; } - current_textures = textures; - // When the texture is an external texture, the UV rect is not known when - // the external surface descriptor is created, because external textures - // are not resolved until the lock() callback is invoked at the start of - // the frame render. To handle this, query the texture resolver for the - // UV rect if it's an external texture, otherwise use the default UV rect. - let uv_rects = [ - self.texture_resolver.get_uv_rect(&textures.colors[0], surface.yuv_planes[0].uv_rect), - self.texture_resolver.get_uv_rect(&textures.colors[1], surface.yuv_planes[1].uv_rect), - self.texture_resolver.get_uv_rect(&textures.colors[2], surface.yuv_planes[2].uv_rect), - ]; + current_textures = textures; - // Create the instance and add to current batch - let instance = CompositeInstance::new_yuv( - surface.device_rect, - surface.clip_rect, - surface.z_id, - surface.yuv_color_space, - surface.yuv_format, - surface.yuv_rescale, - [ - surface.yuv_planes[0].texture_layer as f32, - surface.yuv_planes[1].texture_layer as f32, - surface.yuv_planes[2].texture_layer as f32, - ], - uv_rects, - ); + // Add instance to current batch instances.push(instance); } @@ -4433,8 +4430,6 @@ impl Renderer { stats, ); } - - self.gpu_profile.finish_sampler(opaque_sampler); } /// Composite picture cache tiles into the framebuffer. This is currently @@ -4474,19 +4469,6 @@ impl Renderer { combined_dirty_rect = combined_dirty_rect.union(&dirty_rect); } - // Include any external surfaces in the partial present dirty rect. For now, - // we assume that the external surfaces are always dirty / updating and always - // need to be presented. - // TODO(gw): We could check if the epoch of the external image has changed, and - // skip including the external surface in the partial present dirty - // rect if it hasn't changed. - for surface in &composite_state.external_surfaces { - let dirty_rect = surface.device_rect - .intersection(&surface.clip_rect) - .unwrap_or_else(DeviceRect::zero); - combined_dirty_rect = combined_dirty_rect.union(&dirty_rect); - } - let combined_dirty_rect = combined_dirty_rect.round(); let combined_dirty_rect_i32 = combined_dirty_rect.to_i32(); // If nothing has changed, don't return any dirty rects at all (the client @@ -4537,15 +4519,6 @@ impl Renderer { + composite_state.alpha_tiles.len(); self.profile_counters.total_picture_cache_tiles.set(num_tiles); - // Draw external compositor surfaces - if !composite_state.external_surfaces.is_empty() { - self.draw_external_surface_list( - composite_state, - projection, - &mut results.stats, - ); - } - // Draw opaque tiles first, front-to-back to get maxmum // z-reject efficiency. if !composite_state.opaque_tiles.is_empty() { @@ -4554,6 +4527,7 @@ impl Renderer { self.set_blend(false, FramebufferKind::Main); self.draw_tile_list( composite_state.opaque_tiles.iter().rev(), + &composite_state.external_surfaces, projection, partial_present_mode, &mut results.stats, @@ -4568,6 +4542,7 @@ impl Renderer { self.device.set_blend_mode_premultiplied_dest_out(); self.draw_tile_list( composite_state.clear_tiles.iter(), + &composite_state.external_surfaces, projection, partial_present_mode, &mut results.stats, @@ -4583,6 +4558,7 @@ impl Renderer { self.set_blend_mode_premultiplied_alpha(FramebufferKind::Main); self.draw_tile_list( composite_state.alpha_tiles.iter(), + &composite_state.external_surfaces, projection, partial_present_mode, &mut results.stats, From 4236b415d1200d688fe7e9e56e05245270e5f7ca Mon Sep 17 00:00:00 2001 From: Glenn Watson Date: Fri, 6 Mar 2020 09:57:37 +0000 Subject: [PATCH 32/33] Bug 1579235 - Part 12 - Support native compositor surfaces. r=Bert,sotaro This patch adds support for external compositor surfaces when using native compositor mode. Differential Revision: https://phabricator.services.mozilla.com/D65436 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/c3a689bf10e1dd2b1dc04fe0494584561bd8421b --- webrender/src/composite.rs | 25 +++++-- webrender/src/frame_builder.rs | 4 ++ webrender/src/picture.rs | 121 +++++++++++++++++++++++++++++-- webrender/src/renderer.rs | 127 ++++++++++++++++++++++++++++++++- 4 files changed, 267 insertions(+), 10 deletions(-) diff --git a/webrender/src/composite.rs b/webrender/src/composite.rs index b51ab8b639..a65a38f457 100644 --- a/webrender/src/composite.rs +++ b/webrender/src/composite.rs @@ -100,6 +100,12 @@ pub struct ExternalSurfaceDescriptor { pub yuv_format: YuvFormat, pub yuv_rescale: f32, pub z_id: ZBufferId, + /// If native compositing is enabled, the native compositor surface handle. + /// Otherwise, this will be None + pub native_surface_id: Option, + /// If the native surface needs to be updated, this will contain the size + /// of the native surface as Some(size). If not dirty, this is None. + pub update_params: Option, } /// Information about a plane in a YUV surface. @@ -140,6 +146,9 @@ pub struct ResolvedExternalSurface { pub yuv_format: YuvFormat, pub yuv_rescale: f32, pub image_buffer_kind: ImageBufferKind, + + // Update information for a native surface if it's dirty + pub update_params: Option<(NativeSurfaceId, DeviceIntSize)>, } /// Public interface specified in `RendererOptions` that configures @@ -501,6 +510,16 @@ impl CompositeState { external_surface_index: ResolvedExternalSurfaceIndex(self.external_surfaces.len()), }; + // If the external surface descriptor reports that the native surface + // needs to be updated, create an update params tuple for the renderer + // to use. + let update_params = external_surface.update_params.map(|surface_size| { + ( + external_surface.native_surface_id.expect("bug: no native surface!"), + surface_size + ) + }); + self.external_surfaces.push(ResolvedExternalSurface { yuv_color_space: external_surface.yuv_color_space, yuv_format: external_surface.yuv_format, @@ -508,6 +527,7 @@ impl CompositeState { image_buffer_kind: get_buffer_kind(yuv_planes[0].texture), image_dependencies: external_surface.image_dependencies, yuv_planes, + update_params, }); let tile = CompositeTile { @@ -524,10 +544,7 @@ impl CompositeState { // a dependency on the compositor surface external image keys / generations. self.descriptor.surfaces.push( CompositeSurfaceDescriptor { - // TODO(gw): When we add native compositor surfaces, this be - // need to be set, as that's how native compositor - // surfaces are added to the visual tree. - surface_id: None, + surface_id: external_surface.native_surface_id, offset: tile.rect.origin, clip_rect: tile.clip_rect, image_dependencies: external_surface.image_dependencies, diff --git a/webrender/src/frame_builder.rs b/webrender/src/frame_builder.rs index 6f027572aa..deb2c9a5c3 100644 --- a/webrender/src/frame_builder.rs +++ b/webrender/src/frame_builder.rs @@ -406,6 +406,10 @@ impl FrameBuilder { visibility_state.resource_cache.destroy_compositor_surface(native_surface.opaque); visibility_state.resource_cache.destroy_compositor_surface(native_surface.alpha); } + + for (_, external_surface) in cache_state.external_native_surface_cache.drain() { + visibility_state.resource_cache.destroy_compositor_surface(external_surface.native_surface_id) + } } } diff --git a/webrender/src/picture.rs b/webrender/src/picture.rs index 18edf5d958..6331159735 100644 --- a/webrender/src/picture.rs +++ b/webrender/src/picture.rs @@ -273,6 +273,8 @@ pub struct PictureCacheState { allocations: PictureCacheRecycledAllocations, /// Currently allocated native compositor surface for this picture cache. pub native_surface: Option, + /// A cache of compositor surfaces that are retained between display lists + pub external_native_surface_cache: FastHashMap, } pub struct PictureCacheRecycledAllocations { @@ -2128,6 +2130,28 @@ pub struct NativeSurface { pub alpha: NativeSurfaceId, } +/// Hash key for an external native compositor surface +#[derive(PartialEq, Eq, Hash)] +pub struct ExternalNativeSurfaceKey { + /// The YUV image keys that are used to draw this surface. + pub image_keys: [ImageKey; 3], + /// The current device size of the surface. + pub size: DeviceIntSize, +} + +/// Information about a native compositor surface cached between frames. +pub struct ExternalNativeSurface { + /// If true, the surface was used this frame. Used for a simple form + /// of GC to remove old surfaces. + pub used_this_frame: bool, + /// The native compositor surface handle + pub native_surface_id: NativeSurfaceId, + /// List of image keys, and current image generations, that are drawn in this surface. + /// The image generations are used to check if the compositor surface is dirty and + /// needs to be updated. + pub image_dependencies: [ImageDependency; 3], +} + /// Represents a cache of tiles that make up a picture primitives. pub struct TileCacheInstance { /// Index of the tile cache / slice for this frame builder. It's determined @@ -2228,6 +2252,8 @@ pub struct TileCacheInstance { pub external_surfaces: Vec, /// z-buffer ID assigned to opaque tiles in this slice pub z_id_opaque: ZBufferId, + /// A cache of compositor surfaces that are retained between frames + pub external_native_surface_cache: FastHashMap, } impl TileCacheInstance { @@ -2282,6 +2308,7 @@ impl TileCacheInstance { tile_size_override: None, external_surfaces: Vec::new(), z_id_opaque: ZBufferId::invalid(), + external_native_surface_cache: FastHashMap::default(), } } @@ -2410,6 +2437,7 @@ impl TileCacheInstance { self.color_bindings = prev_state.color_bindings; self.current_tile_size = prev_state.current_tile_size; self.native_surface = prev_state.native_surface; + self.external_native_surface_cache = prev_state.external_native_surface_cache; fn recycle_map( ideal_len: usize, @@ -2442,6 +2470,15 @@ impl TileCacheInstance { ); } + // At the start of the frame, step through each current compositor surface + // and mark it as unused. Later, this is used to free old compositor surfaces. + // TODO(gw): In future, we might make this more sophisticated - for example, + // retaining them for >1 frame if unused, or retaining them in some + // kind of pool to reduce future allocations. + for external_native_surface in self.external_native_surface_cache.values_mut() { + external_native_surface.used_this_frame = false; + } + // Only evaluate what tile size to use fairly infrequently, so that we don't end // up constantly invalidating and reallocating tiles if the picture rect size is // changing near a threshold value. @@ -2684,6 +2721,10 @@ impl TileCacheInstance { frame_state.resource_cache.destroy_compositor_surface(native_surface.opaque); frame_state.resource_cache.destroy_compositor_surface(native_surface.alpha); } + + for (_, external_surface) in self.external_native_surface_cache.drain() { + frame_state.resource_cache.destroy_compositor_surface(external_surface.native_surface_id) + } } CompositorKind::Native { .. } => { // This could hit even when compositor mode is not changed, @@ -2712,7 +2753,7 @@ impl TileCacheInstance { data_stores: &DataStores, clip_store: &ClipStore, pictures: &[PicturePrimitive], - resource_cache: &ResourceCache, + resource_cache: &mut ResourceCache, opacity_binding_store: &OpacityBindingStorage, color_bindings: &ColorBindingStorage, image_instances: &ImageInstanceStorage, @@ -2909,10 +2950,9 @@ impl TileCacheInstance { // extract the logic below and support RGBA compositor surfaces too. let mut promote_to_surface = false; - // For initial implementation, only the simple (draw) compositor mode - // supports primitives as compositor surfaces. We can remove this restriction - // as a follow up, when we support this for native compositor modes. - if let CompositorKind::Draw { .. } = composite_state.compositor_kind { + + // If picture caching is disabled, we can't support any compositor surfaces. + if composite_state.picture_caching_is_enabled { // Check if this primitive _wants_ to be promoted to a compositor surface. if prim_data.common.flags.contains(PrimitiveFlags::PREFER_COMPOSITOR_SURFACE) { promote_to_surface = true; @@ -3001,6 +3041,64 @@ impl TileCacheInstance { } } + // When using native compositing, we need to find an existing native surface + // handle to use, or allocate a new one. For existing native surfaces, we can + // also determine whether this needs to be updated, depending on whether the + // image generation(s) of the YUV planes have changed since last composite. + let (native_surface_id, update_params) = match composite_state.compositor_kind { + CompositorKind::Draw { .. } => { + (None, None) + } + CompositorKind::Native { .. } => { + let native_surface_size = device_rect.size.round().to_i32(); + + let key = ExternalNativeSurfaceKey { + image_keys: prim_data.kind.yuv_key, + size: native_surface_size, + }; + + let native_surface = self.external_native_surface_cache + .entry(key) + .or_insert_with(|| { + // No existing surface, so allocate a new compositor surface and + // a single compositor tile that covers the entire compositor surface. + + let native_surface_id = resource_cache.create_compositor_surface( + native_surface_size, + true, + ); + + let tile_id = NativeTileId { + surface_id: native_surface_id, + x: 0, + y: 0, + }; + + resource_cache.create_compositor_tile(tile_id); + + ExternalNativeSurface { + used_this_frame: true, + native_surface_id, + image_dependencies: [ImageDependency::INVALID; 3], + } + }); + + // Mark that the surface is referenced this frame so that the + // backing native surface handle isn't freed. + native_surface.used_this_frame = true; + + // If the image dependencies match, there is no need to update + // the backing native surface. + let update_params = if image_dependencies == native_surface.image_dependencies { + None + } else { + Some(native_surface_size) + }; + + (Some(native_surface.native_surface_id), update_params) + } + }; + // Each compositor surface allocates a unique z-id self.external_surfaces.push(ExternalSurfaceDescriptor { local_rect: prim_info.prim_clip_rect, @@ -3013,6 +3111,8 @@ impl TileCacheInstance { yuv_format: prim_data.kind.format, yuv_rescale: prim_data.kind.color_depth.rescaling_factor(), z_id: composite_state.z_generator.next(), + native_surface_id, + update_params, }); } } else { @@ -3239,6 +3339,16 @@ impl TileCacheInstance { ); } + // A simple GC of the native external surface cache, to remove and free any + // surfaces that were not referenced during the update_prim_dependencies pass. + self.external_native_surface_cache.retain(|_, surface| { + if !surface.used_this_frame { + frame_state.resource_cache.destroy_compositor_surface(surface.native_surface_id); + } + + surface.used_this_frame + }); + // Detect if the picture cache was scrolled or scaled. In this case, // the device space dirty rects aren't applicable (until we properly // integrate with OS compositors that can handle scrolling slices). @@ -4174,6 +4284,7 @@ impl PicturePrimitive { root_transform: tile_cache.root_transform, current_tile_size: tile_cache.current_tile_size, native_surface: tile_cache.native_surface, + external_native_surface_cache: tile_cache.external_native_surface_cache, allocations: PictureCacheRecycledAllocations { old_opacity_bindings: tile_cache.old_opacity_bindings, old_color_bindings: tile_cache.old_color_bindings, diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index edae98bf75..a7e1da350c 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -68,7 +68,7 @@ use crate::glyph_rasterizer::{GlyphFormat, GlyphRasterizer}; use crate::gpu_cache::{GpuBlockData, GpuCacheUpdate, GpuCacheUpdateList}; use crate::gpu_cache::{GpuCacheDebugChunk, GpuCacheDebugCmd}; use crate::gpu_types::{PrimitiveHeaderI, PrimitiveHeaderF, ScalingInstance, SvgFilterInstance, TransformData}; -use crate::gpu_types::{CompositeInstance, ResolveInstanceData}; +use crate::gpu_types::{CompositeInstance, ResolveInstanceData, ZBufferId}; use crate::internal_types::{TextureSource, ORTHO_FAR_PLANE, ORTHO_NEAR_PLANE, ResourceCacheError}; use crate::internal_types::{CacheTextureId, DebugOutput, FastHashMap, FastHashSet, LayerIndex, RenderedDocument, ResultMsg}; use crate::internal_types::{TextureCacheAllocationKind, TextureCacheUpdate, TextureUpdateList, TextureUpdateSource}; @@ -4251,6 +4251,127 @@ impl Renderer { } } + /// Rasterize any external compositor surfaces that require updating + fn update_external_native_surfaces( + &mut self, + external_surfaces: &[ResolvedExternalSurface], + results: &mut RenderResults, + ) { + if external_surfaces.is_empty() { + return; + } + + let opaque_sampler = self.gpu_profile.start_sampler(GPU_SAMPLER_TAG_OPAQUE); + + self.device.disable_depth(); + self.set_blend(false, FramebufferKind::Main); + + for surface in external_surfaces { + // See if this surface needs to be updated + let (native_surface_id, surface_size) = match surface.update_params { + Some(params) => params, + None => continue, + }; + + // When updating an external surface, the entire surface rect is used + // for all of the draw, dirty, valid and clip rect parameters. + let surface_rect = surface_size.into(); + + // Bind the native compositor surface to update + let surface_info = self.compositor_config + .compositor() + .unwrap() + .bind( + NativeTileId { + surface_id: native_surface_id, + x: 0, + y: 0, + }, + surface_rect, + surface_rect, + ); + + // Bind the native surface to current FBO target + let draw_target = DrawTarget::NativeSurface { + offset: surface_info.origin, + external_fbo_id: surface_info.fbo_id, + dimensions: surface_size, + }; + self.device.bind_draw_target(draw_target); + + let projection = Transform3D::ortho( + 0.0, + surface_size.width as f32, + 0.0, + surface_size.height as f32, + ORTHO_NEAR_PLANE, + ORTHO_FAR_PLANE, + ); + + // Bind an appropriate YUV shader for the texture format kind + self.shaders + .borrow_mut() + .get_composite_shader( + CompositeSurfaceFormat::Yuv, + surface.image_buffer_kind, + ).bind( + &mut self.device, + &projection, + &mut self.renderer_errors + ); + + let textures = BatchTextures { + colors: [ + surface.yuv_planes[0].texture, + surface.yuv_planes[1].texture, + surface.yuv_planes[2].texture, + ], + }; + + // When the texture is an external texture, the UV rect is not known when + // the external surface descriptor is created, because external textures + // are not resolved until the lock() callback is invoked at the start of + // the frame render. To handle this, query the texture resolver for the + // UV rect if it's an external texture, otherwise use the default UV rect. + let uv_rects = [ + self.texture_resolver.get_uv_rect(&textures.colors[0], surface.yuv_planes[0].uv_rect), + self.texture_resolver.get_uv_rect(&textures.colors[1], surface.yuv_planes[1].uv_rect), + self.texture_resolver.get_uv_rect(&textures.colors[2], surface.yuv_planes[2].uv_rect), + ]; + + let instance = CompositeInstance::new_yuv( + surface_rect.to_f32(), + surface_rect.to_f32(), + // z-id is not relevant when updating a native compositor surface. + // TODO(gw): Support compositor surfaces without z-buffer, for memory / perf win here. + ZBufferId(0), + surface.yuv_color_space, + surface.yuv_format, + surface.yuv_rescale, + [ + surface.yuv_planes[0].texture_layer as f32, + surface.yuv_planes[1].texture_layer as f32, + surface.yuv_planes[2].texture_layer as f32, + ], + uv_rects, + ); + + self.draw_instanced_batch( + &[instance], + VertexArrayKind::Composite, + &textures, + &mut results.stats, + ); + + self.compositor_config + .compositor() + .unwrap() + .unbind(); + } + + self.gpu_profile.finish_sampler(opaque_sampler); + } + /// Draw a list of tiles to the framebuffer fn draw_tile_list<'a, I: Iterator>( &mut self, @@ -5448,6 +5569,10 @@ impl Renderer { // to specify how to composite each of the picture cache surfaces. match self.current_compositor_kind { CompositorKind::Native { .. } => { + self.update_external_native_surfaces( + &frame.composite_state.external_surfaces, + results, + ); let compositor = self.compositor_config.compositor().unwrap(); frame.composite_state.composite_native(&mut **compositor); } From 51b4045c9237cbba196def2dfec2dcbf168ad7fe Mon Sep 17 00:00:00 2001 From: Bert Peers Date: Fri, 6 Mar 2020 09:57:50 +0000 Subject: [PATCH 33/33] Bug 1618939 - Hit MOZ_CRASH(explicit panic) at gfx/wr/webrender/src/render_task.rs:37 r=gw Adding a repro-case reftest that asks for a 19996x5000 RenderTask (at -p1), then fix it in analogy with the clamping to reasonable values that happens for `NormalBorder`. Differential Revision: https://phabricator.services.mozilla.com/D65660 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/14caf2826f00fc609f7fe55d8df14e7e08fd1ced --- webrender/src/prim_store/line_dec.rs | 2 + webrender/src/prim_store/mod.rs | 11 ++++- .../reftests/text/large-line-decoration.yaml | 43 +++++++++++++++++++ wrench/reftests/text/reftest.list | 1 + 4 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 wrench/reftests/text/large-line-decoration.yaml diff --git a/webrender/src/prim_store/line_dec.rs b/webrender/src/prim_store/line_dec.rs index 1dbda9a5a5..4102409aef 100644 --- a/webrender/src/prim_store/line_dec.rs +++ b/webrender/src/prim_store/line_dec.rs @@ -18,6 +18,8 @@ use crate::prim_store::{ }; use crate::prim_store::PrimitiveInstanceKind; +/// Maximum resolution in device pixels at which line decorations are rasterized. +pub const MAX_LINE_DECORATION_RESOLUTION: u32 = 4096; #[derive(Clone, Debug, Hash, MallocSizeOf, PartialEq, Eq)] #[cfg_attr(feature = "capture", derive(Serialize))] diff --git a/webrender/src/prim_store/mod.rs b/webrender/src/prim_store/mod.rs index 3b53b710a3..ef50dfbf71 100644 --- a/webrender/src/prim_store/mod.rs +++ b/webrender/src/prim_store/mod.rs @@ -34,7 +34,7 @@ use crate::prim_store::borders::{ImageBorderDataHandle, NormalBorderDataHandle}; use crate::prim_store::gradient::{GRADIENT_FP_STOPS, GradientCacheKey, GradientStopKey}; use crate::prim_store::gradient::{LinearGradientPrimitive, LinearGradientDataHandle, RadialGradientDataHandle, ConicGradientDataHandle}; use crate::prim_store::image::{ImageDataHandle, ImageInstance, VisibleImageTile, YuvImageDataHandle}; -use crate::prim_store::line_dec::LineDecorationDataHandle; +use crate::prim_store::line_dec::{LineDecorationDataHandle,MAX_LINE_DECORATION_RESOLUTION}; use crate::prim_store::picture::PictureDataHandle; use crate::prim_store::text_run::{TextRunDataHandle, TextRunPrimitive}; #[cfg(debug_assertions)] @@ -2920,7 +2920,14 @@ impl PrimitiveStore { // TODO(gw): Do we ever need / want to support scales for text decorations // based on the current transform? let scale_factor = Scale::new(1.0) * device_pixel_scale; - let task_size = (LayoutSize::from_au(cache_key.size) * scale_factor).ceil().to_i32(); + let mut task_size = (LayoutSize::from_au(cache_key.size) * scale_factor).ceil().to_i32(); + if task_size.width > MAX_LINE_DECORATION_RESOLUTION as i32 || + task_size.height > MAX_LINE_DECORATION_RESOLUTION as i32 { + let max_extent = cmp::max(task_size.width, task_size.height); + let task_scale_factor = Scale::new(MAX_LINE_DECORATION_RESOLUTION as f32 / max_extent as f32); + task_size = (LayoutSize::from_au(cache_key.size) * scale_factor * task_scale_factor) + .ceil().to_i32(); + } // Request a pre-rendered image task. // TODO(gw): This match is a bit untidy, but it should disappear completely diff --git a/wrench/reftests/text/large-line-decoration.yaml b/wrench/reftests/text/large-line-decoration.yaml new file mode 100644 index 0000000000..ec12a5dc31 --- /dev/null +++ b/wrench/reftests/text/large-line-decoration.yaml @@ -0,0 +1,43 @@ +--- +root: + items: + - type: line + baseline: 0 + start: 0 + end: 50 + width: 5000 + thickness: 5000 + orientation: horizontal + color: red + style: solid + + - type: line + baseline: 0 + start: 100 + end: 150 + width: 5000 + thickness: 5000 + orientation: horizontal + color: green + style: dashed + + - type: line + baseline: 0 + start: 200 + end: 250 + width: 5000 + thickness: 5000 + orientation: horizontal + color: blue + style: dotted + + - type: line + baseline: 0 + start: 300 + end: 350 + width: 5000 + thickness: 5000 + orientation: horizontal + color: yellow + style: wavy + diff --git a/wrench/reftests/text/reftest.list b/wrench/reftests/text/reftest.list index 60dc7e3d3c..42f1180c79 100644 --- a/wrench/reftests/text/reftest.list +++ b/wrench/reftests/text/reftest.list @@ -66,6 +66,7 @@ fuzzy(1,113) platform(linux) == raster-space.yaml raster-space.png skip_on(android) skip_on(mac,>=10.14) != allow-subpixel.yaml allow-subpixel-ref.yaml # Android: we don't enable sub-px aa on this platform. skip_on(android,device) == bg-color.yaml bg-color-ref.yaml # Fails on Pixel2 != large-glyphs.yaml blank.yaml +!= large-line-decoration.yaml blank.yaml skip_on(android,device) == snap-text-offset.yaml snap-text-offset-ref.yaml fuzzy(5,4435) == shadow-border.yaml shadow-solid-ref.yaml fuzzy(5,4435) == shadow-image.yaml shadow-solid-ref.yaml