author | Hiroyuki Ikezoe <hikezoe.birchill@mozilla.com> |
Thu, 27 Feb 2020 08:43:06 +0000 | |
changeset 515862 | 9092c93a4ac6621e8fb255ae5b386046a0524c0a |
parent 515861 | 42d17390bcca6e518624bddb755433277dd64ba4 |
child 515863 | f6ff71a9517a9331e0b0253833f7ed94eddfaab2 |
push id | 108472 |
push user | hikezoe.birchill@mozilla.com |
push date | Thu, 27 Feb 2020 08:44:56 +0000 |
treeherder | autoland@0d3fcf6f3aaa [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | gw, boris |
bugs | 1510030 |
milestone | 75.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/gfx/layers/AnimationHelper.cpp +++ b/gfx/layers/AnimationHelper.cpp @@ -610,16 +610,22 @@ bool AnimationHelper::SampleAnimations(C continue; } const PropertyAnimationGroup& lastPropertyAnimationGroup = animationStorageData.mAnimation.LastElement(); // Store the AnimatedValue switch (lastPropertyAnimationGroup.mProperty) { + case eCSSProperty_background_color: { + aStorage->SetAnimatedValue( + iter.Key(), Servo_AnimationValue_GetColor(animationValues[0], + NS_RGBA(0, 0, 0, 0))); + break; + } case eCSSProperty_opacity: { MOZ_ASSERT(animationValues.Length() == 1); aStorage->SetAnimatedValue( iter.Key(), Servo_AnimationValue_GetOpacity(animationValues[0])); break; } case eCSSProperty_rotate: case eCSSProperty_scale:
--- a/gfx/layers/wr/WebRenderBridgeParent.cpp +++ b/gfx/layers/wr/WebRenderBridgeParent.cpp @@ -2274,33 +2274,38 @@ bool WebRenderBridgeParent::AdvanceAnima // started animations. mPreviousFrameTimeStamp = isAnimating ? lastComposeTime : TimeStamp(); return isAnimating; } bool WebRenderBridgeParent::SampleAnimations( wr::RenderRootArray<nsTArray<wr::WrOpacityProperty>>& aOpacityArrays, - wr::RenderRootArray<nsTArray<wr::WrTransformProperty>>& aTransformArrays) { + wr::RenderRootArray<nsTArray<wr::WrTransformProperty>>& aTransformArrays, + wr::RenderRootArray<nsTArray<wr::WrColorProperty>>& aColorArrays) { const bool isAnimating = AdvanceAnimations(); // return the animated data if has if (mAnimStorage->AnimatedValueCount()) { for (auto iter = mAnimStorage->ConstAnimatedValueTableIter(); !iter.Done(); iter.Next()) { AnimatedValue* value = iter.UserData(); wr::RenderRoot renderRoot = mAnimStorage->AnimationRenderRoot(iter.Key()); auto& transformArray = aTransformArrays[renderRoot]; auto& opacityArray = aOpacityArrays[renderRoot]; + auto& colorArray = aColorArrays[renderRoot]; if (value->Is<AnimationTransform>()) { transformArray.AppendElement(wr::ToWrTransformProperty( iter.Key(), value->Transform().mTransformInDevSpace)); } else if (value->Is<float>()) { opacityArray.AppendElement( wr::ToWrOpacityProperty(iter.Key(), value->Opacity())); + } else if (value->Is<nscolor>()) { + colorArray.AppendElement(wr::ToWrColorProperty( + iter.Key(), gfx::Color::FromABGR(value->Color()))); } } } return isAnimating; } void WebRenderBridgeParent::CompositeIfNeeded() { @@ -2420,31 +2425,33 @@ void WebRenderBridgeParent::MaybeGenerat if (framesGenerated == 0) { // Could skip generating frame now. mPreviousFrameTimeStamp = TimeStamp(); return; } wr::RenderRootArray<nsTArray<wr::WrOpacityProperty>> opacityArrays; wr::RenderRootArray<nsTArray<wr::WrTransformProperty>> transformArrays; - - if (SampleAnimations(opacityArrays, transformArrays)) { + wr::RenderRootArray<nsTArray<wr::WrColorProperty>> colorArrays; + + if (SampleAnimations(opacityArrays, transformArrays, colorArrays)) { // TODO we should have a better way of assessing whether we need a content // or a chrome frame generation. ScheduleGenerateFrameAllRenderRoots(); } // We do this even if the arrays are empty, because it will clear out any // previous properties store on the WR side, which is desirable. for (auto& api : mApis) { if (!api) { continue; } auto renderRoot = api->GetRenderRoot(); fastTxns[renderRoot]->UpdateDynamicProperties(opacityArrays[renderRoot], - transformArrays[renderRoot]); + transformArrays[renderRoot], + colorArrays[renderRoot]); } SetAPZSampleTime(); wr::RenderThread::Get()->IncPendingFrameCount( mApis[wr::RenderRoot::Default]->GetId(), aId, start, framesGenerated); #if defined(ENABLE_FRAME_LATENCY_LOG)
--- a/gfx/layers/wr/WebRenderBridgeParent.h +++ b/gfx/layers/wr/WebRenderBridgeParent.h @@ -422,17 +422,18 @@ class WebRenderBridgeParent final bool ShouldParentObserveEpoch(); mozilla::ipc::IPCResult HandleShutdown(); // Returns true if there is any animation (including animations in delay // phase). bool AdvanceAnimations(); bool SampleAnimations( wr::RenderRootArray<nsTArray<wr::WrOpacityProperty>>& aOpacityArrays, - wr::RenderRootArray<nsTArray<wr::WrTransformProperty>>& aTransformArrays); + wr::RenderRootArray<nsTArray<wr::WrTransformProperty>>& aTransformArrays, + wr::RenderRootArray<nsTArray<wr::WrColorProperty>>& aColorArrays); CompositorBridgeParent* GetRootCompositorBridgeParent() const; RefPtr<WebRenderBridgeParent> GetRootWebRenderBridgeParent() const; // Tell APZ what the subsequent sampling's timestamp should be. void SetAPZSampleTime();
--- a/gfx/webrender_bindings/WebRenderAPI.cpp +++ b/gfx/webrender_bindings/WebRenderAPI.cpp @@ -211,22 +211,25 @@ void TransactionBuilder::GenerateFrame() } void TransactionBuilder::InvalidateRenderedFrame() { wr_transaction_invalidate_rendered_frame(mTxn); } void TransactionBuilder::UpdateDynamicProperties( const nsTArray<wr::WrOpacityProperty>& aOpacityArray, - const nsTArray<wr::WrTransformProperty>& aTransformArray) { + const nsTArray<wr::WrTransformProperty>& aTransformArray, + const nsTArray<wr::WrColorProperty>& aColorArray) { wr_transaction_update_dynamic_properties( mTxn, aOpacityArray.IsEmpty() ? nullptr : aOpacityArray.Elements(), aOpacityArray.Length(), aTransformArray.IsEmpty() ? nullptr : aTransformArray.Elements(), - aTransformArray.Length()); + aTransformArray.Length(), + aColorArray.IsEmpty() ? nullptr : aColorArray.Elements(), + aColorArray.Length()); } bool TransactionBuilder::IsEmpty() const { return wr_transaction_is_empty(mTxn); } bool TransactionBuilder::IsResourceUpdatesEmpty() const { return wr_transaction_resource_updates_is_empty(mTxn); @@ -1134,16 +1137,30 @@ void DisplayListBuilder::PushHitTest(con bool aIsBackfaceVisible) { wr::LayoutRect clip = MergeClipLeaf(aClip); WRDL_LOG("PushHitTest b=%s cl=%s\n", mWrState, Stringify(aBounds).c_str(), Stringify(clip).c_str()); wr_dp_push_hit_test(mWrState, aBounds, clip, aIsBackfaceVisible, &mCurrentSpaceAndClipChain); } +void DisplayListBuilder::PushRectWithAnimation( + const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip, + bool aIsBackfaceVisible, const wr::ColorF& aColor, + const WrAnimationProperty* aAnimation) { + wr::LayoutRect clip = MergeClipLeaf(aClip); + WRDL_LOG("PushRectWithAnimation b=%s cl=%s c=%s\n", mWrState, + Stringify(aBounds).c_str(), Stringify(clip).c_str(), + Stringify(aColor).c_str()); + + wr_dp_push_rect_with_animation(mWrState, aBounds, clip, aIsBackfaceVisible, + &mCurrentSpaceAndClipChain, aColor, + aAnimation); +} + void DisplayListBuilder::PushClearRect(const wr::LayoutRect& aBounds) { wr::LayoutRect clip = MergeClipLeaf(aBounds); WRDL_LOG("PushClearRect b=%s c=%s\n", mWrState, Stringify(aBounds).c_str(), Stringify(clip).c_str()); wr_dp_push_clear_rect(mWrState, aBounds, clip, &mCurrentSpaceAndClipChain); } void DisplayListBuilder::PushClearRectWithComplexRegion(
--- a/gfx/webrender_bindings/WebRenderAPI.h +++ b/gfx/webrender_bindings/WebRenderAPI.h @@ -101,17 +101,18 @@ class TransactionBuilder final { void ClearDisplayList(Epoch aEpoch, wr::WrPipelineId aPipeline); void GenerateFrame(); void InvalidateRenderedFrame(); void UpdateDynamicProperties( const nsTArray<wr::WrOpacityProperty>& aOpacityArray, - const nsTArray<wr::WrTransformProperty>& aTransformArray); + const nsTArray<wr::WrTransformProperty>& aTransformArray, + const nsTArray<wr::WrColorProperty>& aColorArray); void SetDocumentView(const LayoutDeviceIntRect& aDocRect); void UpdateScrollPosition( const wr::WrPipelineId& aPipelineId, const layers::ScrollableLayerGuid::ViewID& aScrollId, const wr::LayoutPoint& aScrollPosition); @@ -457,16 +458,20 @@ class DisplayListBuilder final { wr::WrSpaceAndClip DefineScrollLayer( const layers::ScrollableLayerGuid::ViewID& aViewId, const Maybe<wr::WrSpaceAndClip>& aParent, const wr::LayoutRect& aContentRect, const wr::LayoutRect& aClipRect, const wr::LayoutPoint& aScrollOffset); void PushRect(const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip, bool aIsBackfaceVisible, const wr::ColorF& aColor); + void PushRectWithAnimation(const wr::LayoutRect& aBounds, + const wr::LayoutRect& aClip, + bool aIsBackfaceVisible, const wr::ColorF& aColor, + const WrAnimationProperty* aAnimation); void PushRoundedRect(const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip, bool aIsBackfaceVisible, const wr::ColorF& aColor); void PushHitTest(const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip, bool aIsBackfaceVisible); void PushClearRect(const wr::LayoutRect& aBounds); void PushClearRectWithComplexRegion(const wr::LayoutRect& aBounds, const wr::ComplexClipRegion& aRegion);
--- a/gfx/webrender_bindings/WebRenderTypes.h +++ b/gfx/webrender_bindings/WebRenderTypes.h @@ -639,16 +639,24 @@ static inline wr::WrTransformProperty To static inline wr::WrOpacityProperty ToWrOpacityProperty(uint64_t id, const float opacity) { wr::WrOpacityProperty prop; prop.id = id; prop.opacity = opacity; return prop; } +static inline wr::WrColorProperty ToWrColorProperty(uint64_t id, + const gfx::Color& color) { + wr::WrColorProperty prop; + prop.id = id; + prop.color = ToColorF(color); + return prop; +} + // Whenever possible, use wr::ExternalImageId instead of manipulating uint64_t. inline uint64_t AsUint64(const ExternalImageId& aId) { return static_cast<uint64_t>(aId._0); } static inline ExternalImageId ToExternalImageId(uint64_t aID) { ExternalImageId Id; Id._0 = aID;
--- a/gfx/webrender_bindings/src/bindings.rs +++ b/gfx/webrender_bindings/src/bindings.rs @@ -428,19 +428,21 @@ pub struct WrFilterData { B_values: *mut c_float, B_values_count: usize, funcA_type: ComponentTransferFuncType, A_values: *mut c_float, A_values_count: usize, } #[repr(u32)] +#[derive(Debug)] pub enum WrAnimationType { Transform = 0, Opacity = 1, + BackgroundColor = 2, } #[repr(C)] pub struct WrAnimationProperty { effect_type: WrAnimationType, id: u64, } @@ -454,16 +456,23 @@ pub struct WrTransformProperty { #[repr(C)] #[derive(Copy, Clone, Debug)] pub struct WrOpacityProperty { pub id: u64, pub opacity: f32, } +#[repr(C)] +#[derive(Copy, Clone, Debug)] +pub struct WrColorProperty { + pub id: u64, + pub color: ColorF, +} + /// cbindgen:field-names=[mHandle] /// cbindgen:derive-lt=true /// cbindgen:derive-lte=true #[repr(C)] #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct WrWindowId(u64); fn get_proc_address(glcontext_ptr: *mut c_void, @@ -1700,20 +1709,23 @@ pub extern "C" fn wr_transaction_invalid #[no_mangle] pub extern "C" fn wr_transaction_update_dynamic_properties( txn: &mut Transaction, opacity_array: *const WrOpacityProperty, opacity_count: usize, transform_array: *const WrTransformProperty, transform_count: usize, + color_array: *const WrColorProperty, + color_count: usize, ) { let mut properties = DynamicProperties { transforms: Vec::with_capacity(transform_count), floats: Vec::with_capacity(opacity_count), + colors: Vec::with_capacity(color_count), }; if transform_count > 0 { let transform_slice = unsafe { make_slice(transform_array, transform_count) }; properties.transforms.reserve(transform_slice.len()); for element in transform_slice.iter() { let prop = PropertyValue { @@ -1733,16 +1745,29 @@ pub extern "C" fn wr_transaction_update_ let prop = PropertyValue { key: PropertyBindingKey::new(element.id), value: element.opacity, }; properties.floats.push(prop); } } + if color_count > 0 { + let color_slice = unsafe { make_slice(color_array, color_count) }; + + properties.colors.reserve(color_slice.len()); + for element in color_slice.iter() { + let prop = PropertyValue { + key: PropertyBindingKey::new(element.id), + value: element.color, + }; + properties.colors.push(prop); + } + } + txn.update_dynamic_properties(properties); } #[no_mangle] pub extern "C" fn wr_transaction_append_transform_properties( txn: &mut Transaction, transform_array: *const WrTransformProperty, transform_count: usize, @@ -2376,16 +2401,17 @@ pub extern "C" fn wr_dp_push_stacking_co has_opacity_animation = true; }, WrAnimationType::Transform => { transform_binding = Some(PropertyBinding::Binding(PropertyBindingKey::new(anim.id), // Same as above opacity case. transform_ref.cloned().unwrap_or(LayoutTransform::identity()))); }, + _ => unreachable!("{:?} should not create a stacking context", anim.effect_type), } } if let Some(opacity) = opacity_ref { if !has_opacity_animation && *opacity < 1.0 { filters.push(FilterOp::Opacity(PropertyBinding::Value(*opacity), *opacity)); } } @@ -2608,47 +2634,97 @@ fn prim_flags( ) -> PrimitiveFlags { if is_backface_visible { PrimitiveFlags::IS_BACKFACE_VISIBLE } else { PrimitiveFlags::empty() } } -#[no_mangle] -pub extern "C" fn wr_dp_push_rect(state: &mut WrState, - rect: LayoutRect, - clip: LayoutRect, - is_backface_visible: bool, - parent: &WrSpaceAndClipChain, - color: ColorF) { - debug_assert!(unsafe { !is_in_render_thread() }); - +fn common_item_properties_for_rect( + state: &mut WrState, + rect: LayoutRect, + clip: LayoutRect, + is_backface_visible: bool, + parent: &WrSpaceAndClipChain +) -> CommonItemProperties { let space_and_clip = parent.to_webrender(state.pipeline_id); let clip_rect = clip.intersection(&rect); - let prim_info = CommonItemProperties { + CommonItemProperties { // NB: the damp-e10s talos-test will frequently crash on startup if we // early-return here for empty rects. I couldn't figure out why, but // it's pretty harmless to feed these through, so, uh, we do? clip_rect: clip_rect.unwrap_or(LayoutRect::zero()), clip_id: space_and_clip.clip_id, spatial_id: space_and_clip.spatial_id, flags: prim_flags(is_backface_visible), hit_info: state.current_tag, item_key: state.current_item_key, - }; + } +} + +#[no_mangle] +pub extern "C" fn wr_dp_push_rect(state: &mut WrState, + rect: LayoutRect, + clip: LayoutRect, + is_backface_visible: bool, + parent: &WrSpaceAndClipChain, + color: ColorF) { + debug_assert!(unsafe { !is_in_render_thread() }); + + let prim_info = common_item_properties_for_rect( + state, + rect, + clip, + is_backface_visible, + parent, + ); state.frame_builder.dl_builder.push_rect( &prim_info, color, ); } #[no_mangle] +pub extern "C" fn wr_dp_push_rect_with_animation( + state: &mut WrState, + rect: LayoutRect, + clip: LayoutRect, + is_backface_visible: bool, + parent: &WrSpaceAndClipChain, + color: ColorF, + animation: *const WrAnimationProperty) { + debug_assert!(unsafe { !is_in_render_thread() }); + + let prim_info = common_item_properties_for_rect( + state, + rect, + clip, + is_backface_visible, + parent, + ); + + let anim = unsafe { animation.as_ref() }; + if let Some(anim) = anim { + debug_assert!(anim.id > 0); + match anim.effect_type { + WrAnimationType::BackgroundColor => { + state.frame_builder.dl_builder.push_rect_with_animation( + &prim_info, + PropertyBinding::Binding(PropertyBindingKey::new(anim.id), color), + ) + }, + _ => unreachable!("Didn't expect {:?} animation", anim.effect_type), + } + } +} + +#[no_mangle] pub extern "C" fn wr_dp_push_rect_with_parent_clip( state: &mut WrState, rect: LayoutRect, clip: LayoutRect, is_backface_visible: bool, parent: &WrSpaceAndClip, color: ColorF, ) {
--- a/gfx/wr/examples/animation.rs +++ b/gfx/wr/examples/animation.rs @@ -182,16 +182,17 @@ impl Example for App { }, ], floats: vec![ PropertyValue { key: self.opacity_key, value: self.opacity, } ], + colors: vec![], }, ); txn.generate_frame(); api.send_transaction(document_id, txn); } _ => (), }
--- a/gfx/wr/webrender/src/box_shadow.rs +++ b/gfx/wr/webrender/src/box_shadow.rs @@ -1,13 +1,14 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * 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, BoxShadowClipMode, ClipMode, ColorF, PrimitiveKeyKind}; +use api::PropertyBinding; use api::MAX_BLUR_RADIUS; use api::units::*; use crate::clip::{ClipItemKey, ClipItemKeyKind}; use crate::scene_building::SceneBuilder; use crate::gpu_cache::GpuCacheHandle; use crate::gpu_types::BoxShadowStretchMode; use crate::prim_store::ScrollNodeAndClipChain; use crate::render_task_cache::RenderTaskCacheEntryHandle; @@ -158,17 +159,17 @@ impl<'a> SceneBuilder<'a> { spatial_node_index: clip_and_scroll.spatial_node_index, }); self.add_primitive( clip_and_scroll, &LayoutPrimitiveInfo::with_clip_rect(final_prim_rect, prim_info.clip_rect), clips, PrimitiveKeyKind::Rectangle { - color: color.into(), + color: PropertyBinding::Value(color.into()), }, ); } else { // Normal path for box-shadows with a valid blur radius. let blur_offset = (BLUR_SAMPLE_SCALE * blur_radius).ceil(); let mut extra_clips = vec![]; // Add a normal clip mask to clip out the contents @@ -184,17 +185,17 @@ impl<'a> SceneBuilder<'a> { // Get the local rect of where the shadow will be drawn, // expanded to include room for the blurred region. let dest_rect = shadow_rect.inflate(blur_offset, blur_offset); // 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. let shadow_clip_source = ClipItemKey { kind: ClipItemKeyKind::box_shadow( shadow_rect, shadow_radius, dest_rect,
--- a/gfx/wr/webrender/src/picture.rs +++ b/gfx/wr/webrender/src/picture.rs @@ -91,17 +91,17 @@ //! of the compositor surface. Otherwise, we can draw the tiles in the normal fast //! path before the compositor surface is drawn. Use of the per-tile valid and //! dirty rects ensure that we do a minimal amount of per-pixel work here to //! blend the overlay tile (this is not always optimal right now, but will be //! improved as a follow up). 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}; use crate::spatial_tree::{ROOT_SPATIAL_NODE_INDEX, SpatialTree, CoordinateSpaceMapping, SpatialNodeIndex, VisibleFace }; use crate::composite::{CompositorKind, CompositeState, NativeSurfaceId, NativeTileId}; use crate::composite::{ExternalSurfaceDescriptor}; @@ -115,16 +115,17 @@ use crate::internal_types::{FastHashMap, use crate::frame_builder::{FrameBuildingContext, FrameBuildingState, PictureState, PictureContext}; use crate::gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle}; use crate::gpu_types::UvRectKind; use plane_split::{Clipper, Polygon, Splitter}; use crate::prim_store::{SpaceMapper, PrimitiveVisibilityMask, PointKey, PrimitiveTemplateKind}; 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; use crate::render_target::RenderTargetKind; use crate::render_task::{RenderTask, RenderTaskLocation, BlurTaskCache, ClearMode}; use crate::resource_cache::{ResourceCache, ImageGeneration}; use crate::scene::SceneProperties; use crate::spatial_tree::CoordinateSystemId; @@ -259,30 +260,33 @@ struct PictureInfo { /// Picture-caching state to keep between scenes. pub struct PictureCacheState { /// The tiles retained by this picture cache. pub tiles: FastHashMap<TileOffset, Box<Tile>>, /// State of the spatial nodes from previous frame spatial_nodes: FastHashMap<SpatialNodeIndex, SpatialNodeDependency>, /// State of opacity bindings from previous frame opacity_bindings: FastHashMap<PropertyBindingId, OpacityBindingInfo>, + /// State of color bindings from previous frame + color_bindings: FastHashMap<PropertyBindingId, ColorBindingInfo>, /// The current transform of the picture cache root spatial node root_transform: TransformKey, /// The current tile size in device pixels current_tile_size: DeviceIntSize, /// Various allocations we want to avoid re-doing. allocations: PictureCacheRecycledAllocations, /// Currently allocated native compositor surface for this picture cache. pub native_surface_id: Option<NativeSurfaceId>, /// True if the entire picture cache is opaque. is_opaque: bool, } pub struct PictureCacheRecycledAllocations { old_opacity_bindings: FastHashMap<PropertyBindingId, OpacityBindingInfo>, + old_color_bindings: FastHashMap<PropertyBindingId, ColorBindingInfo>, compare_cache: FastHashMap<PrimitiveComparisonKey, PrimitiveCompareResult>, } /// Stores a list of cached picture tiles that are retained /// between new scenes. #[cfg_attr(feature = "capture", derive(Serialize))] pub struct RetainedTiles { /// The tiles retained between display lists. @@ -396,43 +400,49 @@ fn clampf(value: f32, low: f32, high: f3 } /// An index into the prims array in a TileDescriptor. #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[cfg_attr(feature = "capture", derive(Serialize))] #[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<T> { /// 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<T> { + Value(T), Binding(PropertyBindingId), } -impl From<PropertyBinding<f32>> for OpacityBinding { - fn from(binding: PropertyBinding<f32>) -> OpacityBinding { +impl<T> From<PropertyBinding<T>> for Binding<T> { + fn from(binding: PropertyBinding<T>) -> Binding<T> { 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<f32>; +pub type OpacityBindingInfo = BindingInfo<f32>; + +pub type ColorBinding = Binding<ColorU>; +pub type ColorBindingInfo = BindingInfo<ColorU>; + /// Information about the state of a spatial node value #[derive(Debug)] pub struct SpatialNodeDependency { /// The current value retrieved from the spatial tree. value: TransformKey, /// True if it was changed (or is new) since the last frame build. changed: bool, } @@ -471,16 +481,19 @@ struct TilePostUpdateContext<'a> { backdrop: BackdropInfo, /// Information about transform node differences from last frame. spatial_nodes: &'a FastHashMap<SpatialNodeIndex, SpatialNodeDependency>, /// Information about opacity bindings from the picture cache. opacity_bindings: &'a FastHashMap<PropertyBindingId, OpacityBindingInfo>, + /// Information about color bindings from the picture cache. + color_bindings: &'a FastHashMap<PropertyBindingId, ColorBindingInfo>, + /// Current size in device pixels of tiles for this cache current_tile_size: DeviceIntSize, /// The local rect of the overall picture cache local_rect: PictureRect, } // Mutable state passed to picture cache tiles during post_update @@ -510,16 +523,19 @@ struct PrimitiveDependencyInfo { prim_clip_rect: PictureRect, /// Image keys this primitive depends on. images: SmallVec<[ImageDependency; 8]>, /// Opacity bindings this primitive depends on. opacity_bindings: SmallVec<[OpacityBinding; 4]>, + /// Color binding this primitive depends on. + color_binding: Option<ColorBinding>, + /// Clips that this primitive depends on. clips: SmallVec<[ItemUid; 8]>, /// Spatial nodes references by the clip dependencies of this primitive. spatial_nodes: SmallVec<[SpatialNodeIndex; 4]>, /// If true, this primitive has been promoted to be a compositor surface. is_compositor_surface: bool, @@ -532,16 +548,17 @@ impl PrimitiveDependencyInfo { prim_origin: PicturePoint, prim_clip_rect: PictureRect, ) -> Self { PrimitiveDependencyInfo { prim_uid, prim_origin, images: SmallVec::new(), opacity_bindings: SmallVec::new(), + color_binding: None, clip_by_tile: false, prim_clip_rect, clips: SmallVec::new(), spatial_nodes: SmallVec::new(), is_compositor_surface: false, } } } @@ -683,16 +700,18 @@ pub enum PrimitiveCompareResult { /// The clip node content or spatial node changed Clip, /// The value of the transform changed Transform, /// An image dependency was dirty 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 /// debug logging is enabled. #[derive(Debug, Copy, Clone, PartialEq)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub enum PrimitiveCompareResultDetail { @@ -713,17 +732,21 @@ pub enum PrimitiveCompareResultDetail { }, /// An image dependency was dirty Image { detail: CompareHelperResult<ImageDependency>, }, /// The value of an opacity binding changed OpacityBinding { detail: CompareHelperResult<OpacityBinding>, - } + }, + /// The value of a color binding changed + ColorBinding { + detail: CompareHelperResult<ColorBinding>, + }, } /// Debugging information about why a tile was invalidated #[derive(Debug,Clone)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub enum InvalidationReason { /// The fractional offset changed @@ -881,16 +904,17 @@ impl Tile { frame_context: &FrameVisibilityContext, ) -> PictureRect { let mut prim_comparer = PrimitiveComparer::new( &self.prev_descriptor, &self.current_descriptor, state.resource_cache, ctx.spatial_nodes, ctx.opacity_bindings, + ctx.color_bindings, ); let mut dirty_rect = PictureRect::zero(); self.root.update_dirty_rects( &self.prev_descriptor.prims, &self.current_descriptor.prims, &mut prim_comparer, &mut dirty_rect, @@ -1049,16 +1073,22 @@ impl Tile { self.current_descriptor.opacity_bindings.extend_from_slice(&info.opacity_bindings); // Include any clip nodes that this primitive depends on. self.current_descriptor.clips.extend_from_slice(&info.clips); // 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. let (prim_origin, prim_clip_rect) = if info.clip_by_tile { let tile_p0 = self.local_tile_rect.origin; let tile_p1 = self.local_tile_rect.bottom_right(); let clip_p0 = PicturePoint::new( @@ -1101,16 +1131,17 @@ impl Tile { self.current_descriptor.prims.push(PrimitiveDescriptor { prim_uid: info.prim_uid, origin: prim_origin.into(), prim_clip_rect: prim_clip_rect.into(), transform_dep_count: info.spatial_nodes.len() as u8, 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. self.root.add_prim(prim_index, &info.prim_clip_rect); } /// Called during tile cache instance post_update. Allows invalidation and dirty /// rect calculation after primitive dependencies have been updated. @@ -1312,16 +1343,17 @@ pub struct PrimitiveDescriptor { /// dependencies since there is no entry in the clip chain /// dependencies for the local clip rect. pub prim_clip_rect: RectangleKey, /// The number of extra dependencies that this primitive has. transform_dep_count: u8, image_dep_count: u8, opacity_binding_dep_count: u8, clip_dep_count: u8, + color_binding_dep_count: u8, } impl PartialEq for PrimitiveDescriptor { fn eq(&self, other: &Self) -> bool { const EPSILON: f32 = 0.001; if self.prim_uid != other.prim_uid { return false; @@ -1462,27 +1494,32 @@ pub struct TileDescriptor { opacity_bindings: Vec<OpacityBinding>, /// List of the effects of transforms that we care about /// tracking for this tile. transforms: Vec<SpatialNodeIndex>, /// 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<ColorBinding>, } impl TileDescriptor { fn new() -> Self { TileDescriptor { prims: Vec::new(), clips: Vec::new(), opacity_bindings: Vec::new(), images: Vec::new(), transforms: Vec::new(), local_valid_rect: PictureRect::zero(), + color_bindings: Vec::new(), } } /// Print debug information about this tile descriptor to a tree printer. fn print(&self, pt: &mut dyn PrintTreePrinter) { pt.new_level("current_descriptor".to_string()); pt.new_level("prims".to_string()); @@ -1490,21 +1527,22 @@ impl TileDescriptor { pt.new_level(format!("prim uid={}", prim.prim_uid.get_uid())); pt.add_item(format!("origin: {},{}", prim.origin.x, prim.origin.y)); pt.add_item(format!("clip: origin={},{} size={}x{}", prim.prim_clip_rect.x, prim.prim_clip_rect.y, 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(); } pt.end_level(); if !self.clips.is_empty() { pt.new_level("clips".to_string()); for clip in &self.clips { @@ -1537,28 +1575,38 @@ impl TileDescriptor { pt.new_level("transforms".to_string()); for transform in &self.transforms { pt.new_level(format!("spatial_node={:?}", transform)); pt.end_level(); } 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(); } /// Clear the dependency information for a tile, when the dependencies /// are being rebuilt. fn clear(&mut self) { self.prims.clear(); self.clips.clear(); self.opacity_bindings.clear(); self.images.clear(); self.transforms.clear(); self.local_valid_rect = PictureRect::zero(); + self.color_bindings.clear(); } } /// Stores both the world and devices rects for a single dirty rect. #[derive(Debug, Clone)] pub struct DirtyRegionRect { /// World rect of this dirty region pub world_rect: WorldRect, @@ -2021,16 +2069,21 @@ pub struct TileCacheInstance { spatial_nodes: FastHashMap<SpatialNodeIndex, SpatialNodeDependency>, /// Switch back and forth between old and new spatial nodes hashmaps to avoid re-allocating. old_spatial_nodes: FastHashMap<SpatialNodeIndex, SpatialNodeDependency>, /// A set of spatial nodes that primitives / clips depend on found /// during dependency creation. This is used to avoid trying to /// calculate invalid relative transforms when building the spatial /// nodes hash above. used_spatial_nodes: FastHashSet<SpatialNodeIndex>, + /// List of color bindings, with some extra information + /// about whether they changed since last frame. + color_bindings: FastHashMap<PropertyBindingId, ColorBindingInfo>, + /// Switch back and forth between old and new bindings hashmaps to avoid re-allocating. + old_color_bindings: FastHashMap<PropertyBindingId, ColorBindingInfo>, /// The current dirty region tracker for this picture. pub dirty_region: DirtyRegion, /// Current size of tiles in picture units. tile_size: PictureSize, /// Tile coords of the currently allocated grid. tile_rect: TileRect, /// Pre-calculated versions of the tile_rect above, used to speed up the /// calculations in get_tile_coords_for_rect. @@ -2109,16 +2162,18 @@ impl TileCacheInstance { ROOT_SPATIAL_NODE_INDEX, PictureRect::zero(), ), opacity_bindings: FastHashMap::default(), old_opacity_bindings: FastHashMap::default(), 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(), tile_bounds_p0: TileOffset::zero(), tile_bounds_p1: TileOffset::zero(), local_rect: PictureRect::zero(), local_clip_rect: PictureRect::zero(), surface_index: SurfaceIndex(0), @@ -2170,17 +2225,17 @@ impl TileCacheInstance { p0.x = clamp(p0.x, self.tile_bounds_p0.x, self.tile_bounds_p1.x); p0.y = clamp(p0.y, self.tile_bounds_p0.y, self.tile_bounds_p1.y); p1.x = clamp(p1.x, self.tile_bounds_p0.x, self.tile_bounds_p1.x); p1.y = clamp(p1.y, self.tile_bounds_p0.y, self.tile_bounds_p1.y); (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, surface_index: SurfaceIndex, frame_context: &FrameVisibilityContext, frame_state: &mut FrameVisibilityState, ) -> WorldRect { self.external_surfaces.clear(); @@ -2252,16 +2307,17 @@ impl TileCacheInstance { } // If there are pending retained state, retrieve it. if let Some(prev_state) = frame_state.retained_tiles.caches.remove(&self.slice) { self.tiles.extend(prev_state.tiles); 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; fn recycle_map<K: std::cmp::Eq + std::hash::Hash, V>( ideal_len: usize, dest: &mut FastHashMap<K, V>, src: FastHashMap<K, V>, @@ -2276,16 +2332,21 @@ impl TileCacheInstance { } } recycle_map( self.opacity_bindings.len(), &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, prev_state.allocations.compare_cache, ); } // 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 @@ -2375,16 +2436,33 @@ impl TileCacheInstance { None => true, }; self.opacity_bindings.insert(*id, OpacityBindingInfo { value: *value, changed, }); } + // 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, ); // We know that this is an exact rectangle, since we (for now) only support tile // caches where the scroll root is in the root coordinate system. let local_tile_rect = pic_to_world_mapper @@ -2534,16 +2612,17 @@ impl TileCacheInstance { prim_clip_chain: Option<&ClipChainInstance>, local_prim_rect: LayoutRect, frame_context: &FrameVisibilityContext, data_stores: &DataStores, clip_store: &ClipStore, pictures: &[PicturePrimitive], resource_cache: &ResourceCache, opacity_binding_store: &OpacityBindingStorage, + color_bindings: &ColorBindingStorage, image_instances: &ImageInstanceStorage, surface_stack: &[SurfaceIndex], composite_state: &CompositeState, ) -> bool { // This primitive exists on the last element on the current surface stack. let prim_surface_index = *surface_stack.last().unwrap(); // If the primitive is completely clipped out by the clip chain, there @@ -2670,35 +2749,41 @@ impl TileCacheInstance { match prim_instance.kind { PrimitiveInstanceKind::Picture { pic_index,.. } => { // Pictures can depend on animated opacity bindings. let pic = &pictures[pic_index.0]; if let Some(PictureCompositeMode::Filter(Filter::Opacity(binding, _))) = pic.requested_composite_mode { 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 { backdrop_candidate = Some(BackdropKind::Color { color }); } } else { let opacity_binding = &opacity_binding_store[opacity_binding_index]; for binding in &opacity_binding.bindings { prim_info.opacity_bindings.push(OpacityBinding::from(*binding)); } } + 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, .. } => { let image_data = &data_stores.image[data_handle].kind; let image_instance = &image_instances[image_instance_index]; let opacity_binding_index = image_instance.opacity_binding_index; if opacity_binding_index == OpacityBindingIndex::INVALID { @@ -3090,16 +3175,17 @@ impl TileCacheInstance { let ctx = TilePostUpdateContext { pic_to_world_mapper, global_device_pixel_scale: frame_context.global_device_pixel_scale, local_clip_rect: self.local_clip_rect, 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, }; let mut state = TilePostUpdateState { resource_cache: frame_state.resource_cache, composite_state: frame_state.composite_state, compare_cache: &mut self.compare_cache, @@ -3973,22 +4059,24 @@ impl PicturePrimitive { if let Some(mut tile_cache) = self.tile_cache.take() { if !tile_cache.tiles.is_empty() { retained_tiles.caches.insert( tile_cache.slice, PictureCacheState { 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, }, }, ); } } } @@ -5533,28 +5621,31 @@ pub struct ImageDependency { } /// A helper struct to compare a primitive and all its sub-dependencies. struct PrimitiveComparer<'a> { clip_comparer: CompareHelper<'a, ItemUid>, 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<SpatialNodeIndex, SpatialNodeDependency>, opacity_bindings: &'a FastHashMap<PropertyBindingId, OpacityBindingInfo>, + color_bindings: &'a FastHashMap<PropertyBindingId, ColorBindingInfo>, } impl<'a> PrimitiveComparer<'a> { fn new( prev: &'a TileDescriptor, curr: &'a TileDescriptor, resource_cache: &'a ResourceCache, spatial_nodes: &'a FastHashMap<SpatialNodeIndex, SpatialNodeDependency>, opacity_bindings: &'a FastHashMap<PropertyBindingId, OpacityBindingInfo>, + color_bindings: &'a FastHashMap<PropertyBindingId, ColorBindingInfo>, ) -> Self { let clip_comparer = CompareHelper::new( &prev.clips, &curr.clips, ); let transform_comparer = CompareHelper::new( &prev.transforms, @@ -5566,58 +5657,69 @@ impl<'a> PrimitiveComparer<'a> { &curr.images, ); let opacity_comparer = CompareHelper::new( &prev.opacity_bindings, &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, } } fn reset(&mut self) { self.clip_comparer.reset(); self.transform_comparer.reset(); self.image_comparer.reset(); self.opacity_comparer.reset(); + self.color_comparer.reset(); } fn advance_prev(&mut self, prim: &PrimitiveDescriptor) { self.clip_comparer.advance_prev(prim.clip_dep_count); 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) { self.clip_comparer.advance_curr(prim.clip_dep_count); 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. fn compare_prim( &mut self, prev: &PrimitiveDescriptor, curr: &PrimitiveDescriptor, opt_detail: Option<&mut PrimitiveCompareResultDetail>, ) -> PrimitiveCompareResult { 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 { if let Some(detail) = opt_detail { *detail = PrimitiveCompareResultDetail::Descriptor{ old: *prev, new: *curr }; } return PrimitiveCompareResult::Descriptor; } @@ -5687,16 +5789,40 @@ impl<'a> PrimitiveComparer<'a> { if opt_detail.is_some() { Some(&mut bind_result) } else { None }, ) { if let Some(detail) = opt_detail { *detail = PrimitiveCompareResultDetail::OpacityBinding{ detail: bind_result }; } 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 } } /// Details for a node in a quadtree that tracks dirty rects for a tile. #[cfg_attr(any(feature="capture",feature="replay"), derive(Clone))] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))]
--- a/gfx/wr/webrender/src/prim_store/mod.rs +++ b/gfx/wr/webrender/src/prim_store/mod.rs @@ -1,13 +1,13 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * 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}; use api::{PrimitiveKeyKind}; use api::units::*; use crate::border::{get_max_scale_for_border, build_border_instances}; use crate::border::BorderSegmentCacheKey; use crate::clip::{ClipStore}; @@ -715,17 +715,17 @@ impl intern::InternDebug for PrimitiveKe /// The shared information for a given primitive. This is interned and retained /// both across frames and display lists, by comparing the matching PrimitiveKey. #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] #[derive(MallocSizeOf)] pub enum PrimitiveTemplateKind { Rectangle { - color: ColorF, + color: PropertyBinding<ColorF>, }, Clear, } /// Construct the primitive template data from a primitive key. This /// is invoked when a primitive key is created and the interner /// doesn't currently contain a primitive with this key. impl From<PrimitiveKeyKind> for PrimitiveTemplateKind { @@ -807,49 +807,51 @@ impl From<PrimitiveKey> for PrimitiveTem } } } 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 => { // Opaque black with operator dest out request.push(PremultipliedColorF::BLACK); } PrimitiveTemplateKind::Rectangle { ref color, .. } => { - request.push(color.premultiplied()); + request.push(scene_properties.resolve_color(color).premultiplied()) } } } } impl PrimitiveTemplate { /// Update the GPU cache for a given primitive template. This may be called multiple /// times per frame, by each primitive reference that refers to this interned /// template. The initial request call to the GPU cache ensures that work is only /// done if the cache entry is invalid (due to first use or eviction). 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 { PrimitiveTemplateKind::Clear => { PrimitiveOpacity::translucent() } PrimitiveTemplateKind::Rectangle { ref color, .. } => { - PrimitiveOpacity::from_alpha(color.a) + PrimitiveOpacity::from_alpha(scene_properties.resolve_color(color).a) } }; } } type PrimitiveDataHandle = intern::Handle<PrimitiveKeyKind>; impl intern::Internable for PrimitiveKeyKind { @@ -868,30 +870,37 @@ impl InternablePrimitive for PrimitiveKe info.rect.size, self, ) } fn make_instance_kind( key: PrimitiveKey, data_handle: PrimitiveDataHandle, - _: &mut PrimitiveStore, + prim_store: &mut PrimitiveStore, _reference_frame_relative_offset: LayoutVector2D, ) -> PrimitiveInstanceKind { match key.kind { PrimitiveKeyKind::Clear => { PrimitiveInstanceKind::Clear { 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, } } } } } // Maintains a list of opacity bindings that have been collapsed into // the color of a single primitive. This is an important optimization @@ -1321,34 +1330,37 @@ impl IsVisible for PrimitiveKeyKind { // we should move the logic for all other // primitive types to use this. fn is_visible(&self) -> bool { match *self { PrimitiveKeyKind::Clear => { true } PrimitiveKeyKind::Rectangle { ref color, .. } => { - color.a > 0 + match *color { + PropertyBinding::Value(value) => value.a > 0, + PropertyBinding::Binding(..) => true, + } } } } } impl CreateShadow for PrimitiveKeyKind { // Create a clone of this PrimitiveContainer, applying whatever // changes are necessary to the primitive to support rendering // it as part of the supplied shadow. fn create_shadow( &self, shadow: &Shadow, ) -> PrimitiveKeyKind { match *self { PrimitiveKeyKind::Rectangle { .. } => { PrimitiveKeyKind::Rectangle { - color: shadow.color.into(), + color: PropertyBinding::Value(shadow.color.into()), } } PrimitiveKeyKind::Clear => { panic!("bug: this prim is not supported in shadow contexts"); } } } } @@ -1399,16 +1411,17 @@ pub enum PrimitiveInstanceKind { /// Handle to the common interned data for this primitive. data_handle: ImageBorderDataHandle, }, Rectangle { /// Handle to the common interned data for this primitive. 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. data_handle: YuvImageDataHandle, segment_instance_index: SegmentInstanceIndex, is_compositor_surface: bool, }, Image { @@ -1655,16 +1668,18 @@ pub struct SegmentedInstance { pub segments_range: SegmentsRange, } pub type GlyphKeyStorage = storage::Storage<GlyphKey>; pub type TextRunIndex = storage::Index<TextRunPrimitive>; pub type TextRunStorage = storage::Storage<TextRunPrimitive>; pub type OpacityBindingIndex = storage::Index<OpacityBinding>; pub type OpacityBindingStorage = storage::Storage<OpacityBinding>; +pub type ColorBindingIndex = storage::Index<PropertyBinding<ColorU>>; +pub type ColorBindingStorage = storage::Storage<PropertyBinding<ColorU>>; pub type BorderHandleStorage = storage::Storage<RenderTaskCacheEntryHandle>; pub type SegmentStorage = storage::Storage<BrushSegment>; pub type SegmentsRange = storage::Range<BrushSegment>; pub type SegmentInstanceStorage = storage::Storage<SegmentedInstance>; pub type SegmentInstanceIndex = storage::Index<SegmentedInstance>; pub type ImageInstanceStorage = storage::Storage<ImageInstance>; pub type ImageInstanceIndex = storage::Index<ImageInstance>; pub type GradientTileStorage = storage::Storage<VisibleGradientTile>; @@ -1794,26 +1809,28 @@ impl PrimitiveScratchBuffer { #[cfg_attr(feature = "replay", derive(Deserialize))] #[derive(Clone, Debug)] pub struct PrimitiveStoreStats { picture_count: usize, text_run_count: usize, opacity_binding_count: usize, image_count: usize, linear_gradient_count: usize, + color_binding_count: usize, } impl PrimitiveStoreStats { pub fn empty() -> Self { PrimitiveStoreStats { picture_count: 0, text_run_count: 0, opacity_binding_count: 0, image_count: 0, linear_gradient_count: 0, + color_binding_count: 0, } } } #[cfg_attr(feature = "capture", derive(Serialize))] pub struct PrimitiveStore { pub pictures: Vec<PicturePrimitive>, pub text_runs: TextRunStorage, @@ -1821,36 +1838,40 @@ pub struct PrimitiveStore { /// A list of image instances. These are stored separately as /// storing them inline in the instance makes the structure bigger /// for other types. pub images: ImageInstanceStorage, /// List of animated opacity bindings for a primitive. pub opacity_bindings: OpacityBindingStorage, + /// animated color bindings for this primitive. + pub color_bindings: ColorBindingStorage, } impl PrimitiveStore { pub fn new(stats: &PrimitiveStoreStats) -> PrimitiveStore { PrimitiveStore { pictures: Vec::with_capacity(stats.picture_count), 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), } } pub fn get_stats(&self) -> PrimitiveStoreStats { PrimitiveStoreStats { picture_count: self.pictures.len(), text_run_count: self.text_runs.len(), 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(), } } #[allow(unused)] pub fn print_picture_tree(&self, root: PictureIndex) { use crate::print_tree::PrintTree; let mut pt = PrintTree::new("picture tree"); self.pictures[root.0].print(&self.pictures, root, &mut pt); @@ -2145,16 +2166,17 @@ impl PrimitiveStore { clip_chain.as_ref(), prim_local_rect, frame_context, frame_state.data_stores, frame_state.clip_store, &self.pictures, frame_state.resource_cache, &self.opacity_bindings, + &self.color_bindings, &self.images, &frame_state.surface_stack, &frame_state.composite_state, ) { prim_instance.visibility_info = PrimitiveVisibilityIndex::INVALID; // Ensure the primitive clip is popped - perhaps we can use // some kind of scope to do this automatically in future. frame_state.clip_chain_stack.pop_clip(); @@ -2970,17 +2992,17 @@ impl PrimitiveStore { } PrimitiveInstanceKind::Clear { data_handle, .. } => { let prim_data = &mut data_stores.prim[*data_handle]; prim_data.common.may_need_repetition = false; // 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]; let common_data = &mut prim_data.common; let border_data = &mut prim_data.kind; common_data.may_need_repetition = matches!(border_data.border.top.style, BorderStyle::Dotted | BorderStyle::Dashed) || @@ -3060,38 +3082,63 @@ impl PrimitiveStore { // TODO: get access to the ninepatch and to check whwther we need support // for repetitions in the shader. // Update the template this instane references, which may refresh the GPU // 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, *opacity_binding_index, frame_context.scene_properties, ); write_segment( *segment_instance_index, frame_state, &mut scratch.segments, &mut scratch.segment_instances, |request| { prim_data.kind.write_prim_gpu_blocks( request, + frame_context.scene_properties, ); } ); } PrimitiveInstanceKind::YuvImage { data_handle, segment_instance_index, .. } => { let prim_data = &mut data_stores.yuv_image[*data_handle]; let common_data = &mut prim_data.common; let yuv_image_data = &mut prim_data.kind; @@ -4337,13 +4384,13 @@ fn test_struct_sizes() { // The sizes of these structures are critical for performance on a number of // talos stress tests. If you get a failure here on CI, there's two possibilities: // (a) You made a structure smaller than it currently is. Great work! Update the // test expectations and move on. // (b) You made a structure larger. This is not necessarily a problem, but should only // be done with care, and after checking if talos performance regresses badly. assert_eq!(mem::size_of::<PrimitiveInstance>(), 88, "PrimitiveInstance size changed"); assert_eq!(mem::size_of::<PrimitiveInstanceKind>(), 40, "PrimitiveInstanceKind size changed"); - assert_eq!(mem::size_of::<PrimitiveTemplate>(), 40, "PrimitiveTemplate size changed"); - assert_eq!(mem::size_of::<PrimitiveTemplateKind>(), 20, "PrimitiveTemplateKind size changed"); - assert_eq!(mem::size_of::<PrimitiveKey>(), 20, "PrimitiveKey size changed"); - assert_eq!(mem::size_of::<PrimitiveKeyKind>(), 5, "PrimitiveKeyKind size changed"); + assert_eq!(mem::size_of::<PrimitiveTemplate>(), 48, "PrimitiveTemplate size changed"); + assert_eq!(mem::size_of::<PrimitiveTemplateKind>(), 28, "PrimitiveTemplateKind size changed"); + assert_eq!(mem::size_of::<PrimitiveKey>(), 28, "PrimitiveKey size changed"); + assert_eq!(mem::size_of::<PrimitiveKeyKind>(), 16, "PrimitiveKeyKind size changed"); }
--- a/gfx/wr/webrender/src/scene.rs +++ b/gfx/wr/webrender/src/scene.rs @@ -17,25 +17,27 @@ use std::sync::Arc; /// Stores a map of the animated property bindings for the current display list. These /// can be used to animate the transform and/or opacity of a display list without /// re-submitting the display list itself. #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct SceneProperties { transform_properties: FastHashMap<PropertyBindingId, LayoutTransform>, float_properties: FastHashMap<PropertyBindingId, f32>, + color_properties: FastHashMap<PropertyBindingId, ColorF>, current_properties: DynamicProperties, pending_properties: Option<DynamicProperties>, } impl SceneProperties { pub fn new() -> Self { SceneProperties { transform_properties: FastHashMap::default(), float_properties: FastHashMap::default(), + color_properties: FastHashMap::default(), current_properties: DynamicProperties::default(), pending_properties: None, } } /// Set the current property list for this display list. pub fn set_properties(&mut self, properties: DynamicProperties) { self.pending_properties = Some(properties); @@ -73,16 +75,21 @@ impl SceneProperties { .insert(property.key.id, property.value); } for property in &pending_properties.floats { self.float_properties .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; } } properties_changed } @@ -116,16 +123,37 @@ impl SceneProperties { .unwrap_or(v) } } } pub fn float_properties(&self) -> &FastHashMap<PropertyBindingId, f32> { &self.float_properties } + + /// Get the current value for a color property. + pub fn resolve_color( + &self, + property: &PropertyBinding<ColorF> + ) -> 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<PropertyBindingId, ColorF> { + &self.color_properties + } + } /// A representation of the layout within the display port for a given document or iframe. #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] #[derive(Clone)] pub struct ScenePipeline { pub pipeline_id: PipelineId,
--- a/gfx/wr/webrender/src/scene_building.rs +++ b/gfx/wr/webrender/src/scene_building.rs @@ -1209,17 +1209,17 @@ impl<'a> SceneBuilder<'a> { &info.common, None, apply_pipeline_clip, ); self.add_solid_rectangle( clip_and_scroll, &layout, - ColorF::TRANSPARENT, + PropertyBinding::Value(ColorF::TRANSPARENT), ); } DisplayItem::ClearRectangle(ref info) => { let (layout, _, clip_and_scroll) = self.process_common_properties( &info.common, None, apply_pipeline_clip, ); @@ -2744,23 +2744,29 @@ impl<'a> SceneBuilder<'a> { _prim_instance: &PrimitiveInstance, ) { } pub fn add_solid_rectangle( &mut self, clip_and_scroll: ScrollNodeAndClipChain, info: &LayoutPrimitiveInfo, - color: ColorF, + color: PropertyBinding<ColorF>, ) { - 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( clip_and_scroll, info, Vec::new(), PrimitiveKeyKind::Rectangle { color: color.into(),
--- a/gfx/wr/webrender_api/src/api.rs +++ b/gfx/wr/webrender_api/src/api.rs @@ -1149,17 +1149,17 @@ pub enum FilterDataIntern {} /// uniquely identifies a primitive template by key. #[derive(Debug, Clone, Eq, MallocSizeOf, PartialEq, Hash, Serialize, Deserialize)] pub enum PrimitiveKeyKind { /// Clear an existing rect, used for special effects on some platforms. Clear, /// Rectangle { /// - color: ColorU, + color: PropertyBinding<ColorU>, }, } /// Meta-macro to enumerate the various interner identifiers and types. /// /// IMPORTANT: Keep this synchronized with the list in mozilla-central located at /// gfx/webrender_bindings/webrender_ffi.h /// @@ -1816,17 +1816,17 @@ impl PropertyBindingId { uid: value as u32, } } } /// 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<T> { /// pub id: PropertyBindingId, _phantom: PhantomData<T>, } /// Construct a property value from a given key and value. impl<T: Copy> PropertyBindingKey<T> { @@ -1848,17 +1848,17 @@ impl<T> PropertyBindingKey<T> { /// A binding property can either be a specific value /// (the normal, non-animated case) or point to a binding location /// to fetch the current value from. /// Note that Binding has also a non-animated value, the value is /// 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<T> { /// Non-animated value. Value(T), /// Animated binding. Binding(PropertyBindingKey<T>, T), } impl<T: Default> Default for PropertyBinding<T> { @@ -1868,16 +1868,56 @@ impl<T: Default> Default for PropertyBin } impl<T> From<T> for PropertyBinding<T> { fn from(value: T) -> PropertyBinding<T> { PropertyBinding::Value(value) } } +impl From<PropertyBindingKey<ColorF>> for PropertyBindingKey<ColorU> { + fn from(key: PropertyBindingKey<ColorF>) -> PropertyBindingKey<ColorU> { + PropertyBindingKey { + id: key.id.clone(), + _phantom: PhantomData, + } + } +} + +impl From<PropertyBindingKey<ColorU>> for PropertyBindingKey<ColorF> { + fn from(key: PropertyBindingKey<ColorU>) -> PropertyBindingKey<ColorF> { + PropertyBindingKey { + id: key.id.clone(), + _phantom: PhantomData, + } + } +} + +impl From<PropertyBinding<ColorF>> for PropertyBinding<ColorU> { + fn from(value: PropertyBinding<ColorF>) -> PropertyBinding<ColorU> { + match value { + PropertyBinding::Value(value) => PropertyBinding::Value(value.into()), + PropertyBinding::Binding(k, v) => { + PropertyBinding::Binding(k.into(), v.into()) + } + } + } +} + +impl From<PropertyBinding<ColorU>> for PropertyBinding<ColorF> { + fn from(value: PropertyBinding<ColorU>) -> PropertyBinding<ColorF> { + 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)] pub struct PropertyValue<T> { /// pub key: PropertyBindingKey<T>, /// pub value: T, @@ -1885,18 +1925,20 @@ pub struct PropertyValue<T> { /// When using `generate_frame()`, a list of `PropertyValue` structures /// can optionally be supplied to provide the current value of any /// animated properties. #[derive(Clone, Deserialize, Serialize, Debug, PartialEq, Default)] pub struct DynamicProperties { /// pub transforms: Vec<PropertyValue<LayoutTransform>>, - /// + /// opacity pub floats: Vec<PropertyValue<f32>>, + /// background color + pub colors: Vec<PropertyValue<ColorF>>, } /// A handler to integrate WebRender with the thread that contains the `Renderer`. pub trait RenderNotifier: Send { /// fn clone(&self) -> Box<dyn RenderNotifier>; /// Wake the thread containing the `Renderer` up (after updates have been put /// in the renderer's queue).
--- a/gfx/wr/webrender_api/src/display_item.rs +++ b/gfx/wr/webrender_api/src/display_item.rs @@ -294,21 +294,21 @@ pub struct ScrollFrameDisplayItem { /// The amount this scrollframe has already been scrolled by, in the caller. /// This means that all the display items that are inside the scrollframe /// will have their coordinates shifted by this amount, and this offset /// should be added to those display item coordinates in order to get a /// normalized value that is consistent across display lists. 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<ColorF>, } /// Clears all colors from the area, making it possible to cut holes in the window. /// (useful for things like the macos frosted-glass effect). #[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] pub struct ClearRectangleDisplayItem { pub common: CommonItemProperties, }
--- a/gfx/wr/webrender_api/src/display_list.rs +++ b/gfx/wr/webrender_api/src/display_list.rs @@ -1111,17 +1111,29 @@ impl DisplayListBuilder { pub fn push_rect( &mut self, common: &di::CommonItemProperties, color: ColorF, ) { 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<ColorF>, + ) { + let item = di::DisplayItem::Rectangle(di::RectangleDisplayItem { + common: *common, + color, }); self.push_item(&item); } pub fn push_clear_rect( &mut self, common: &di::CommonItemProperties, ) {
--- a/gfx/wr/wrench/src/scene.rs +++ b/gfx/wr/wrench/src/scene.rs @@ -9,33 +9,40 @@ use webrender::api::units::{LayoutSize, /// Stores a map of the animated property bindings for the current display list. These /// can be used to animate the transform and/or opacity of a display list without /// re-submitting the display list itself. #[derive(Default)] pub struct SceneProperties { transform_properties: HashMap<PropertyBindingId, LayoutTransform>, float_properties: HashMap<PropertyBindingId, f32>, + color_properties: HashMap<PropertyBindingId, ColorF>, } impl SceneProperties { /// Set the current property list for this display list. 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 .insert(property.key.id, property.value); } for property in &properties.floats { 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. pub fn resolve_layout_transform( &self, property: &PropertyBinding<LayoutTransform>, ) -> LayoutTransform { match *property { @@ -52,16 +59,27 @@ impl SceneProperties { match *property { PropertyBinding::Value(value) => value, PropertyBinding::Binding(ref key, v) => self.float_properties .get(&key.id) .cloned() .unwrap_or(v), } } + + /// Get the current value for a color property. + pub fn resolve_color(&self, property: &PropertyBinding<ColorF>) -> 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. #[derive(Debug)] pub struct ScenePipeline { pub epoch: Epoch, pub viewport_size: LayoutSize, pub background_color: Option<ColorF>,
--- a/gfx/wr/wrench/src/yaml_frame_writer.rs +++ b/gfx/wr/wrench/src/yaml_frame_writer.rs @@ -997,17 +997,23 @@ impl YamlFrameWriter { None => break, }; let mut v = new_table(); match *base.item() { 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"); common_node(&mut v, clip_id_mapper, &item.common); } DisplayItem::ClearRectangle(item) => { str_node(&mut v, "type", "clear-rect"); common_node(&mut v, clip_id_mapper, &item.common);
--- a/layout/painting/nsDisplayList.cpp +++ b/layout/painting/nsDisplayList.cpp @@ -5136,22 +5136,38 @@ bool nsDisplayBackgroundColor::CreateWeb if (mColor == Color()) { return true; } if (HasBackgroundClipText()) { return false; } + uint64_t animationsId = 0; + // We don't support background-color animations on table elements yet. + if (GetType() == DisplayItemType::TYPE_BACKGROUND_COLOR) { + animationsId = AddAnimationsForWebRender( + this, aManager, aDisplayListBuilder, aBuilder.GetRenderRoot()); + } + LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits( mBackgroundRect, mFrame->PresContext()->AppUnitsPerDevPixel()); wr::LayoutRect r = wr::ToLayoutRect(bounds); - aBuilder.PushRect(r, r, !BackfaceIsHidden(), - wr::ToColorF(ToDeviceColor(mColor))); + if (animationsId) { + wr::WrAnimationProperty prop{ + wr::WrAnimationType::BackgroundColor, + animationsId, + }; + aBuilder.PushRectWithAnimation(r, r, !BackfaceIsHidden(), + wr::ToColorF(ToDeviceColor(mColor)), &prop); + } else { + aBuilder.PushRect(r, r, !BackfaceIsHidden(), + wr::ToColorF(ToDeviceColor(mColor))); + } return true; } void nsDisplayBackgroundColor::PaintWithClip(nsDisplayListBuilder* aBuilder, gfxContext* aCtx, const DisplayItemClip& aClip) { MOZ_ASSERT(!HasBackgroundClipText());