diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2021-05-20 09:47:09 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2021-06-07 11:15:42 +0000 |
commit | 189d4fd8fad9e3c776873be51938cd31a42b6177 (patch) | |
tree | 6497caeff5e383937996768766ab3bb2081a40b2 /chromium/cc | |
parent | 8bc75099d364490b22f43a7ce366b366c08f4164 (diff) |
BASELINE: Update Chromium to 90.0.4430.221
Change-Id: Iff4d9d18d2fcf1a576f3b1f453010f744a232920
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/cc')
291 files changed, 10331 insertions, 11194 deletions
diff --git a/chromium/cc/BUILD.gn b/chromium/cc/BUILD.gn index 3bb5e558302..3465bf70b6c 100644 --- a/chromium/cc/BUILD.gn +++ b/chromium/cc/BUILD.gn @@ -7,6 +7,10 @@ import("//skia/features.gni") import("//cc/cc.gni") +if (is_android) { + import("//build/config/android/rules.gni") +} + cc_component("cc") { sources = [ "benchmarks/benchmark_instrumentation.cc", @@ -29,10 +33,13 @@ cc_component("cc") { "benchmarks/unittest_only_benchmark.h", "benchmarks/unittest_only_benchmark_impl.cc", "benchmarks/unittest_only_benchmark_impl.h", + "document_transition/document_transition_request.cc", + "document_transition/document_transition_request.h", "input/actively_scrolling_type.h", "input/browser_controls_offset_manager.cc", "input/browser_controls_offset_manager.h", "input/browser_controls_offset_manager_client.h", + "input/browser_controls_state.h", "input/compositor_input_interfaces.h", "input/input_handler.h", "input/layer_selection_bound.cc", @@ -179,12 +186,15 @@ cc_component("cc") { "metrics/frame_sequence_tracker_collection.h", "metrics/frame_sorter.cc", "metrics/frame_sorter.h", + "metrics/jank_injector.cc", + "metrics/jank_injector.h", "metrics/jank_metrics.cc", "metrics/jank_metrics.h", "metrics/latency_ukm_reporter.cc", "metrics/latency_ukm_reporter.h", "metrics/lcd_text_metrics_reporter.cc", "metrics/lcd_text_metrics_reporter.h", + "metrics/shared_metrics_buffer.h", "metrics/throughput_ukm_reporter.cc", "metrics/throughput_ukm_reporter.h", "metrics/total_frame_counter.cc", @@ -192,6 +202,8 @@ cc_component("cc") { "metrics/ukm_smoothness_data.h", "metrics/video_playback_roughness_reporter.cc", "metrics/video_playback_roughness_reporter.h", + "metrics/web_vital_metrics.cc", + "metrics/web_vital_metrics.h", "raster/bitmap_raster_buffer_provider.cc", "raster/bitmap_raster_buffer_provider.h", "raster/gpu_raster_buffer_provider.cc", @@ -208,6 +220,8 @@ cc_component("cc") { "raster/raster_buffer.h", "raster/raster_buffer_provider.cc", "raster/raster_buffer_provider.h", + "raster/raster_query_queue.cc", + "raster/raster_query_queue.h", "raster/raster_source.cc", "raster/raster_source.h", "raster/scoped_gpu_raster.cc", @@ -398,6 +412,8 @@ cc_component("cc") { "trees/target_property.h", "trees/task_runner_provider.cc", "trees/task_runner_provider.h", + "trees/throttle_decider.cc", + "trees/throttle_decider.h", "trees/transform_node.cc", "trees/transform_node.h", "trees/tree_synchronizer.cc", @@ -432,6 +448,7 @@ cc_component("cc") { "//services/tracing/public/cpp:cpp", "//ui/events:events_base", "//ui/gfx", + "//ui/gfx/animation/keyframe", "//ui/gfx/geometry", "//ui/gl", "//ui/latency", @@ -482,6 +499,8 @@ cc_test_static_library("test_support") { "test/fake_proxy.h", "test/fake_raster_buffer_provider.cc", "test/fake_raster_buffer_provider.h", + "test/fake_raster_query_queue.cc", + "test/fake_raster_query_queue.h", "test/fake_raster_source.cc", "test/fake_raster_source.h", "test/fake_recording_source.cc", @@ -501,7 +520,6 @@ cc_test_static_library("test_support") { "test/fake_ui_resource_layer_tree_host_impl.h", "test/fake_video_frame_provider.cc", "test/fake_video_frame_provider.h", - "test/geometry_test_utils.cc", "test/geometry_test_utils.h", "test/layer_test_common.cc", "test/layer_test_common.h", @@ -612,6 +630,7 @@ cc_test_static_library("test_support") { "//ui/base:features", "//ui/gfx", "//ui/gfx:test_support", + "//ui/gfx/animation/keyframe", "//ui/gfx/geometry", "//ui/gl", "//ui/gl:test_support", @@ -644,6 +663,7 @@ cc_test("cc_unittests") { "base/unique_notifier_unittest.cc", "benchmarks/micro_benchmark_controller_unittest.cc", "debug/rendering_stats_unittest.cc", + "document_transition/document_transition_request_unittest.cc", "input/browser_controls_offset_manager_unittest.cc", "input/main_thread_scrolling_reason_unittest.cc", "input/scroll_snap_data_unittest.cc", @@ -693,6 +713,7 @@ cc_test("cc_unittests") { "metrics/frame_sequence_metrics_unittest.cc", "metrics/frame_sequence_tracker_unittest.cc", "metrics/frame_sorter_unittest.cc", + "metrics/jank_injector_unittest.cc", "metrics/jank_metrics_unittest.cc", "metrics/total_frame_counter_unittest.cc", "metrics/video_playback_roughness_reporter_unittest.cc", @@ -709,6 +730,7 @@ cc_test("cc_unittests") { "paint/paint_op_buffer_unittest.cc", "paint/paint_op_helper_unittest.cc", "paint/paint_shader_unittest.cc", + "paint/paint_worklet_input_unittest.cc", "paint/scoped_raster_flags_unittest.cc", "paint/skia_paint_canvas_unittest.cc", "paint/solid_color_analyzer_unittest.cc", @@ -752,6 +774,7 @@ cc_test("cc_unittests") { "trees/layer_tree_host_pixeltest_tiles.cc", "trees/layer_tree_host_unittest.cc", "trees/layer_tree_host_unittest_animation.cc", + "trees/layer_tree_host_unittest_capture.cc", "trees/layer_tree_host_unittest_capture_content.cc", "trees/layer_tree_host_unittest_checkerimaging.cc", "trees/layer_tree_host_unittest_context.cc", @@ -771,6 +794,7 @@ cc_test("cc_unittests") { "trees/property_tree_builder_unittest.cc", "trees/property_tree_unittest.cc", "trees/swap_promise_manager_unittest.cc", + "trees/throttle_decider_unittest.cc", "trees/tree_synchronizer_unittest.cc", "trees/ukm_manager_unittest.cc", @@ -779,11 +803,10 @@ cc_test("cc_unittests") { "animation/animation_timeline_unittest.cc", "animation/animation_unittest.cc", "animation/element_animations_unittest.cc", + "animation/filter_animation_curve_unittest.cc", "animation/keyframe_model_unittest.cc", - "animation/keyframed_animation_curve_unittest.cc", "animation/scroll_offset_animation_curve_unittest.cc", "animation/scroll_timeline_unittest.cc", - "animation/transform_operations_unittest.cc", "animation/worklet_animation_unittest.cc", # Setup. @@ -826,6 +849,7 @@ cc_test("cc_unittests") { "//ui/events:events_base", "//ui/gfx", "//ui/gfx:test_support", + "//ui/gfx/animation/keyframe", "//ui/gfx/geometry", "//ui/gl", "//ui/gl:test_support", @@ -906,3 +930,19 @@ cc_test("cc_perftests") { "//testing:run_perf_test", ] } + +if (is_android) { + java_cpp_enum("cc_android_java_enums_srcjar") { + sources = [ "//cc/input/browser_controls_state.h" ] + } + + android_library("cc_java") { + # Right now, this only includes the Java switches. But if we need more Java + # files, they should be added here as necessary. + srcjar_deps = [ ":cc_android_java_enums_srcjar" ] + deps = [ + "//base:base_java", + "//third_party/androidx:androidx_annotation_annotation_java", + ] + } +} diff --git a/chromium/cc/OWNERS b/chromium/cc/OWNERS index 2f29787e102..6b427ed86d7 100644 --- a/chromium/cc/OWNERS +++ b/chromium/cc/OWNERS @@ -5,6 +5,7 @@ # Folks listed as unofficial can't do OWNERS approvals but are good people to # ask for informal reviews. +set noparent # layers danakj@chromium.org pdr@chromium.org @@ -36,7 +37,6 @@ flackr@chromium.org smcgruer@chromium.org # images -khushalsagar@chromium.org vmpstr@chromium.org # surfaces @@ -52,6 +52,10 @@ majidvp@chromium.org # metrics sadrul@chromium.org +# paint +sunnyps@chromium.org +vasilyt@chromium.org + # general danakj@chromium.org vmpstr@chromium.org diff --git a/chromium/cc/animation/BUILD.gn b/chromium/cc/animation/BUILD.gn index 5bd47e62e5d..466d3f125da 100644 --- a/chromium/cc/animation/BUILD.gn +++ b/chromium/cc/animation/BUILD.gn @@ -9,8 +9,6 @@ cc_component("animation") { sources = [ "animation.cc", "animation.h", - "animation_curve.cc", - "animation_curve.h", "animation_delegate.h", "animation_events.cc", "animation_events.h", @@ -19,17 +17,16 @@ cc_component("animation") { "animation_host.h", "animation_id_provider.cc", "animation_id_provider.h", - "animation_target.h", "animation_timeline.cc", "animation_timeline.h", "element_animations.cc", "element_animations.h", + "filter_animation_curve.cc", + "filter_animation_curve.h", "keyframe_effect.cc", "keyframe_effect.h", "keyframe_model.cc", "keyframe_model.h", - "keyframed_animation_curve.cc", - "keyframed_animation_curve.h", "scroll_offset_animation_curve.cc", "scroll_offset_animation_curve.h", "scroll_offset_animation_curve_factory.cc", @@ -40,12 +37,6 @@ cc_component("animation") { "scroll_offset_animations_impl.h", "scroll_timeline.cc", "scroll_timeline.h", - "timing_function.cc", - "timing_function.h", - "transform_operation.cc", - "transform_operation.h", - "transform_operations.cc", - "transform_operations.h", "worklet_animation.cc", "worklet_animation.h", ] @@ -57,6 +48,7 @@ cc_component("animation") { "//cc", "//cc/paint", "//ui/gfx", + "//ui/gfx/animation/keyframe", "//ui/gfx/geometry", ] } diff --git a/chromium/cc/animation/animation.cc b/chromium/cc/animation/animation.cc index 718e1f7f670..0b8d96cd812 100644 --- a/chromium/cc/animation/animation.cc +++ b/chromium/cc/animation/animation.cc @@ -6,6 +6,8 @@ #include <inttypes.h> #include <algorithm> +#include <string> +#include <utility> #include "base/stl_util.h" #include "base/strings/stringprintf.h" @@ -16,7 +18,6 @@ #include "cc/animation/keyframe_effect.h" #include "cc/animation/scroll_offset_animation_curve.h" #include "cc/animation/scroll_timeline.h" -#include "cc/animation/transform_operations.h" #include "cc/trees/property_animation_state.h" namespace cc { diff --git a/chromium/cc/animation/animation.h b/chromium/cc/animation/animation.h index 8eba000f62e..90ade7a56a1 100644 --- a/chromium/cc/animation/animation.h +++ b/chromium/cc/animation/animation.h @@ -5,16 +5,17 @@ #ifndef CC_ANIMATION_ANIMATION_H_ #define CC_ANIMATION_ANIMATION_H_ +#include <memory> +#include <string> #include <vector> -#include <memory> #include "base/memory/ref_counted.h" #include "base/time/time.h" -#include "cc/animation/animation_curve.h" #include "cc/animation/animation_export.h" #include "cc/animation/element_animations.h" #include "cc/animation/keyframe_model.h" #include "cc/paint/element_id.h" +#include "ui/gfx/animation/keyframe/animation_curve.h" namespace cc { diff --git a/chromium/cc/animation/animation_curve.cc b/chromium/cc/animation/animation_curve.cc deleted file mode 100644 index 1a46580f534..00000000000 --- a/chromium/cc/animation/animation_curve.cc +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "cc/animation/animation_curve.h" - -#include "base/check.h" -#include "cc/animation/scroll_offset_animation_curve.h" - -namespace cc { - -const ColorAnimationCurve* AnimationCurve::ToColorAnimationCurve() const { - DCHECK(Type() == AnimationCurve::COLOR); - return static_cast<const ColorAnimationCurve*>(this); -} - -AnimationCurve::CurveType ColorAnimationCurve::Type() const { - return COLOR; -} - -const FloatAnimationCurve* AnimationCurve::ToFloatAnimationCurve() const { - DCHECK(Type() == AnimationCurve::FLOAT); - return static_cast<const FloatAnimationCurve*>(this); -} - -AnimationCurve::CurveType FloatAnimationCurve::Type() const { - return FLOAT; -} - -const TransformAnimationCurve* AnimationCurve::ToTransformAnimationCurve() - const { - DCHECK(Type() == AnimationCurve::TRANSFORM); - return static_cast<const TransformAnimationCurve*>(this); -} - -AnimationCurve::CurveType TransformAnimationCurve::Type() const { - return TRANSFORM; -} - -const FilterAnimationCurve* AnimationCurve::ToFilterAnimationCurve() const { - DCHECK(Type() == AnimationCurve::FILTER); - return static_cast<const FilterAnimationCurve*>(this); -} - -AnimationCurve::CurveType FilterAnimationCurve::Type() const { - return FILTER; -} - -const ScrollOffsetAnimationCurve* AnimationCurve::ToScrollOffsetAnimationCurve() - const { - DCHECK(Type() == AnimationCurve::SCROLL_OFFSET); - return static_cast<const ScrollOffsetAnimationCurve*>(this); -} - -ScrollOffsetAnimationCurve* AnimationCurve::ToScrollOffsetAnimationCurve() { - DCHECK(Type() == AnimationCurve::SCROLL_OFFSET); - return static_cast<ScrollOffsetAnimationCurve*>(this); -} - -const SizeAnimationCurve* AnimationCurve::ToSizeAnimationCurve() const { - DCHECK(Type() == AnimationCurve::SIZE); - return static_cast<const SizeAnimationCurve*>(this); -} - -AnimationCurve::CurveType SizeAnimationCurve::Type() const { - return SIZE; -} - -} // namespace cc diff --git a/chromium/cc/animation/animation_curve.h b/chromium/cc/animation/animation_curve.h deleted file mode 100644 index 34979f7b57c..00000000000 --- a/chromium/cc/animation/animation_curve.h +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CC_ANIMATION_ANIMATION_CURVE_H_ -#define CC_ANIMATION_ANIMATION_CURVE_H_ - -#include <memory> - -#include "base/time/time.h" -#include "cc/animation/animation_export.h" -#include "cc/paint/filter_operations.h" -#include "ui/gfx/geometry/size_f.h" -#include "ui/gfx/transform.h" - -namespace cc { - -class ColorAnimationCurve; -class FilterAnimationCurve; -class FloatAnimationCurve; -class ScrollOffsetAnimationCurve; -class SizeAnimationCurve; -class TransformAnimationCurve; -class TransformOperations; - -// An animation curve is a function that returns a value given a time. -class CC_ANIMATION_EXPORT AnimationCurve { - public: - enum CurveType { - COLOR = 0, - FLOAT, - TRANSFORM, - FILTER, - SCROLL_OFFSET, - SIZE, - // This must be last - LAST_CURVE_TYPE = SIZE, - }; - - virtual ~AnimationCurve() {} - - virtual base::TimeDelta Duration() const = 0; - virtual CurveType Type() const = 0; - virtual std::unique_ptr<AnimationCurve> Clone() const = 0; - - const ColorAnimationCurve* ToColorAnimationCurve() const; - const FloatAnimationCurve* ToFloatAnimationCurve() const; - const TransformAnimationCurve* ToTransformAnimationCurve() const; - const FilterAnimationCurve* ToFilterAnimationCurve() const; - const ScrollOffsetAnimationCurve* ToScrollOffsetAnimationCurve() const; - const SizeAnimationCurve* ToSizeAnimationCurve() const; - - ScrollOffsetAnimationCurve* ToScrollOffsetAnimationCurve(); -}; - -class CC_ANIMATION_EXPORT ColorAnimationCurve : public AnimationCurve { - public: - ~ColorAnimationCurve() override {} - - virtual SkColor GetValue(base::TimeDelta t) const = 0; - - CurveType Type() const override; -}; - -class CC_ANIMATION_EXPORT FloatAnimationCurve : public AnimationCurve { - public: - ~FloatAnimationCurve() override {} - - virtual float GetValue(base::TimeDelta t) const = 0; - - CurveType Type() const override; -}; - -class CC_ANIMATION_EXPORT TransformAnimationCurve : public AnimationCurve { - public: - ~TransformAnimationCurve() override {} - - virtual TransformOperations GetValue(base::TimeDelta t) const = 0; - - // Returns true if this animation is a translation. - virtual bool IsTranslation() const = 0; - - // Returns true if this animation preserves axis alignment. - virtual bool PreservesAxisAlignment() const = 0; - - // Animation start scale - virtual bool AnimationStartScale(bool forward_direction, - float* start_scale) const = 0; - - // Set |max_scale| to the maximum scale along any dimension at the end of - // intermediate animation target points (eg keyframe end points). When - // |forward_direction| is true, the animation curve assumes it plays from - // the first keyframe to the last, otherwise it assumes the opposite. Returns - // false if the maximum scale cannot be computed. - virtual bool MaximumTargetScale(bool forward_direction, - float* max_scale) const = 0; - - CurveType Type() const override; -}; - -class CC_ANIMATION_EXPORT FilterAnimationCurve : public AnimationCurve { - public: - ~FilterAnimationCurve() override {} - - virtual FilterOperations GetValue(base::TimeDelta t) const = 0; - virtual bool HasFilterThatMovesPixels() const = 0; - - CurveType Type() const override; -}; - -class CC_ANIMATION_EXPORT SizeAnimationCurve : public AnimationCurve { - public: - ~SizeAnimationCurve() override {} - - virtual gfx::SizeF GetValue(base::TimeDelta t) const = 0; - - CurveType Type() const override; -}; - -} // namespace cc - -#endif // CC_ANIMATION_ANIMATION_CURVE_H_ diff --git a/chromium/cc/animation/animation_delegate.h b/chromium/cc/animation/animation_delegate.h index 6931c8b0d18..0fea0dfeead 100644 --- a/chromium/cc/animation/animation_delegate.h +++ b/chromium/cc/animation/animation_delegate.h @@ -5,9 +5,11 @@ #ifndef CC_ANIMATION_ANIMATION_DELEGATE_H_ #define CC_ANIMATION_ANIMATION_DELEGATE_H_ +#include <memory> + #include "base/time/time.h" -#include "cc/animation/animation_curve.h" -#include "cc/animation/keyframe_model.h" +#include "cc/animation/animation_export.h" +#include "ui/gfx/animation/keyframe/animation_curve.h" namespace cc { @@ -28,7 +30,7 @@ class CC_ANIMATION_EXPORT AnimationDelegate { base::TimeTicks monotonic_time, int target_property, base::TimeTicks animation_start_time, - std::unique_ptr<AnimationCurve> curve) = 0; + std::unique_ptr<gfx::AnimationCurve> curve) = 0; virtual void NotifyLocalTimeUpdated( base::Optional<base::TimeDelta> local_time) = 0; diff --git a/chromium/cc/animation/animation_events.h b/chromium/cc/animation/animation_events.h index 3c060e275eb..7fc3703307b 100644 --- a/chromium/cc/animation/animation_events.h +++ b/chromium/cc/animation/animation_events.h @@ -8,9 +8,9 @@ #include <memory> #include <vector> -#include "cc/animation/animation_curve.h" #include "cc/animation/animation_export.h" #include "cc/trees/mutator_host.h" +#include "ui/gfx/animation/keyframe/animation_curve.h" namespace cc { @@ -51,7 +51,7 @@ struct CC_ANIMATION_EXPORT AnimationEvent { // For continuing a scroll offset animation on the main thread. base::TimeTicks animation_start_time; - std::unique_ptr<AnimationCurve> curve; + std::unique_ptr<gfx::AnimationCurve> curve; // Set for TIME_UPDATED events. base::Optional<base::TimeDelta> local_time; diff --git a/chromium/cc/animation/animation_host.cc b/chromium/cc/animation/animation_host.cc index 137ee986394..d1daa0ef2d2 100644 --- a/chromium/cc/animation/animation_host.cc +++ b/chromium/cc/animation/animation_host.cc @@ -10,8 +10,8 @@ #include "base/bind.h" #include "base/callback.h" +#include "base/containers/contains.h" #include "base/memory/ptr_util.h" -#include "base/stl_util.h" #include "base/trace_event/trace_event.h" #include "base/trace_event/traced_value.h" #include "cc/animation/animation.h" @@ -20,12 +20,13 @@ #include "cc/animation/animation_id_provider.h" #include "cc/animation/animation_timeline.h" #include "cc/animation/element_animations.h" +#include "cc/animation/keyframe_effect.h" #include "cc/animation/scroll_offset_animation_curve.h" #include "cc/animation/scroll_offset_animations.h" #include "cc/animation/scroll_offset_animations_impl.h" #include "cc/animation/scroll_timeline.h" -#include "cc/animation/timing_function.h" #include "cc/animation/worklet_animation.h" +#include "ui/gfx/animation/keyframe/timing_function.h" #include "ui/gfx/geometry/box_f.h" #include "ui/gfx/geometry/scroll_offset.h" @@ -57,16 +58,12 @@ std::unique_ptr<AnimationHost> AnimationHost::CreateForTesting( ThreadInstance thread_instance) { auto animation_host = base::WrapUnique(new AnimationHost(thread_instance)); - if (thread_instance == ThreadInstance::IMPL) - animation_host->SetSupportsScrollAnimations(true); - return animation_host; } AnimationHost::AnimationHost(ThreadInstance thread_instance) : mutator_host_client_(nullptr), thread_instance_(thread_instance), - supports_scroll_animations_(false), needs_push_properties_(false), mutator_(nullptr) { if (thread_instance_ == ThreadInstance::IMPL) { @@ -85,13 +82,10 @@ AnimationHost::~AnimationHost() { DCHECK(element_to_animations_map_.empty()); } -std::unique_ptr<MutatorHost> AnimationHost::CreateImplInstance( - bool supports_impl_scrolling) const { +std::unique_ptr<MutatorHost> AnimationHost::CreateImplInstance() const { DCHECK_EQ(thread_instance_, ThreadInstance::MAIN); - auto mutator_host_impl = base::WrapUnique<MutatorHost>(new AnimationHost(ThreadInstance::IMPL)); - mutator_host_impl->SetSupportsScrollAnimations(supports_impl_scrolling); return mutator_host_impl; } @@ -106,6 +100,20 @@ void AnimationHost::ClearMutators() { id_to_timeline_map_.clear(); } +base::TimeDelta AnimationHost::MinimumTickInterval() const { + base::TimeDelta min_interval = base::TimeDelta::Max(); + for (const auto& animation : ticking_animations_) { + DCHECK(animation->keyframe_effect()); + base::TimeDelta interval = + animation->keyframe_effect()->MinimumTickInterval(); + if (interval.is_zero()) + return interval; + if (interval < min_interval) + min_interval = interval; + } + return min_interval; +} + void AnimationHost::EraseTimeline(scoped_refptr<AnimationTimeline> timeline) { timeline->ClearAnimations(); timeline->SetAnimationHost(nullptr); @@ -146,7 +154,7 @@ void AnimationHost::SetHasInlineStyleMutation(bool has_inline_style_mutation) { void AnimationHost::UpdateRegisteredElementIds(ElementListType changed_list) { for (auto map_entry : element_to_animations_map_) { - // kReservedElementId is reserved for an paint worklet element that animates + // kReservedElementId is reserved for a paint worklet element that animates // a custom property. This element is assumed to always be present as no // element is needed to tick this animation. if (mutator_host_client()->IsElementInPropertyTrees(map_entry.first, @@ -339,20 +347,11 @@ AnimationHost::GetElementAnimationsForElementId(ElementId element_id) const { return iter == element_to_animations_map_.end() ? nullptr : iter->second; } -void AnimationHost::SetSupportsScrollAnimations( - bool supports_scroll_animations) { - supports_scroll_animations_ = supports_scroll_animations; -} - void AnimationHost::SetScrollAnimationDurationForTesting( base::TimeDelta duration) { ScrollOffsetAnimationCurve::SetAnimationDurationForTesting(duration); } -bool AnimationHost::SupportsScrollAnimations() const { - return supports_scroll_animations_; -} - bool AnimationHost::NeedsTickAnimations() const { return !ticking_animations_.empty(); } @@ -646,17 +645,11 @@ bool AnimationHost::AnimationsPreserveAxisAlignment( : true; } -void AnimationHost::GetAnimationScales(ElementId element_id, - ElementListType list_type, - float* maximum_scale, - float* starting_scale) const { - if (auto element_animations = GetElementAnimationsForElementId(element_id)) { - element_animations->GetAnimationScales(list_type, maximum_scale, - starting_scale); - return; - } - *maximum_scale = kNotScaled; - *starting_scale = kNotScaled; +float AnimationHost::MaximumScale(ElementId element_id, + ElementListType list_type) const { + if (auto element_animations = GetElementAnimationsForElementId(element_id)) + return element_animations->MaximumScale(list_type); + return kInvalidScale; } bool AnimationHost::IsElementAnimating(ElementId element_id) const { diff --git a/chromium/cc/animation/animation_host.h b/chromium/cc/animation/animation_host.h index feb5e947b0a..e9a7e098bd6 100644 --- a/chromium/cc/animation/animation_host.h +++ b/chromium/cc/animation/animation_host.h @@ -82,12 +82,10 @@ class CC_ANIMATION_EXPORT AnimationHost : public MutatorHost, void SetNeedsPushProperties(); bool needs_push_properties() const { return needs_push_properties_; } - bool SupportsScrollAnimations() const; - // MutatorHost implementation. - std::unique_ptr<MutatorHost> CreateImplInstance( - bool supports_impl_scrolling) const override; + std::unique_ptr<MutatorHost> CreateImplInstance() const override; void ClearMutators() override; + base::TimeDelta MinimumTickInterval() const override; // Processes the current |element_to_animations_map_|, registering animations // which can now be animated and unregistering those that can't based on the @@ -106,7 +104,6 @@ class CC_ANIMATION_EXPORT AnimationHost : public MutatorHost, void PushPropertiesTo(MutatorHost* host_impl) override; - void SetSupportsScrollAnimations(bool supports_scroll_animations) override; void SetScrollAnimationDurationForTesting(base::TimeDelta duration) override; bool NeedsTickAnimations() const override; @@ -158,10 +155,8 @@ class CC_ANIMATION_EXPORT AnimationHost : public MutatorHost, bool AnimationsPreserveAxisAlignment(ElementId element_id) const override; - void GetAnimationScales(ElementId element_id, - ElementListType list_type, - float* maximum_scale, - float* starting_scale) const override; + float MaximumScale(ElementId element_id, + ElementListType list_type) const override; bool IsElementAnimating(ElementId element_id) const override; bool HasTickingKeyframeModelForTesting(ElementId element_id) const override; @@ -269,7 +264,6 @@ class CC_ANIMATION_EXPORT AnimationHost : public MutatorHost, const ThreadInstance thread_instance_; - bool supports_scroll_animations_; bool needs_push_properties_; std::unique_ptr<LayerTreeMutator> mutator_; diff --git a/chromium/cc/animation/animation_target.h b/chromium/cc/animation/animation_target.h deleted file mode 100644 index 16e99e396b4..00000000000 --- a/chromium/cc/animation/animation_target.h +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CC_ANIMATION_ANIMATION_TARGET_H_ -#define CC_ANIMATION_ANIMATION_TARGET_H_ - -#include "cc/animation/animation_export.h" -#include "third_party/skia/include/core/SkColor.h" - -namespace gfx { -class ScrollOffset; -class SizeF; -} // namespace gfx - -namespace cc { - -class FilterOperations; -class KeyframeModel; -class TransformOperations; - -// An AnimationTarget is an entity that can be affected by a ticking -// cc:KeyframeModel. Any object that expects to have an opacity update, for -// example, should derive from this class. -class CC_ANIMATION_EXPORT AnimationTarget { - public: - virtual ~AnimationTarget() {} - virtual void NotifyClientFloatAnimated(float value, - int target_property_id, - KeyframeModel* keyframe_model) = 0; - virtual void NotifyClientFilterAnimated(const FilterOperations& filter, - int target_property_id, - KeyframeModel* keyframe_model) = 0; - virtual void NotifyClientSizeAnimated(const gfx::SizeF& size, - int target_property_id, - KeyframeModel* keyframe_model) = 0; - virtual void NotifyClientColorAnimated(SkColor color, - int target_property_id, - KeyframeModel* keyframe_model) = 0; - virtual void NotifyClientTransformOperationsAnimated( - const TransformOperations& operations, - int target_property_id, - KeyframeModel* keyframe_model) = 0; - virtual void NotifyClientScrollOffsetAnimated( - const gfx::ScrollOffset& scroll_offset, - int target_property_id, - KeyframeModel* keyframe_model) = 0; -}; - -} // namespace cc - -#endif // CC_ANIMATION_ANIMATION_TARGET_H_ diff --git a/chromium/cc/animation/animation_unittest.cc b/chromium/cc/animation/animation_unittest.cc index 360400e4bb4..15fe8962b49 100644 --- a/chromium/cc/animation/animation_unittest.cc +++ b/chromium/cc/animation/animation_unittest.cc @@ -456,30 +456,33 @@ TEST_F(AnimationTest, ToString) { animation_->id(), element_id_.ToString().c_str()), animation_->ToString()); - animation_->AddKeyframeModel( - KeyframeModel::Create(std::make_unique<FakeFloatAnimationCurve>(15), 42, - 73, TargetProperty::OPACITY)); + animation_->AddKeyframeModel(KeyframeModel::Create( + std::make_unique<FakeFloatAnimationCurve>(15), 42, 73, + KeyframeModel::TargetPropertyId(TargetProperty::OPACITY))); EXPECT_EQ( base::StringPrintf("Animation{id=%d, element_id=%s, " "keyframe_models=[KeyframeModel{id=42, " - "group=73, target_property_id=1, " - "run_state=WAITING_FOR_TARGET_AVAILABILITY}]}", + "group=73, target_property_type=1, " + "custom_property_name=, native_property_type=1, " + "run_state=WAITING_FOR_TARGET_AVAILABILITY, " + "element_id=(0)}]}", animation_->id(), element_id_.ToString().c_str()), animation_->ToString()); - animation_->AddKeyframeModel( - KeyframeModel::Create(std::make_unique<FakeFloatAnimationCurve>(18), 45, - 76, TargetProperty::BOUNDS)); - EXPECT_EQ( - base::StringPrintf( - "Animation{id=%d, element_id=%s, " - "keyframe_models=[KeyframeModel{id=42, " - "group=73, target_property_id=1, " - "run_state=WAITING_FOR_TARGET_AVAILABILITY}, KeyframeModel{id=45, " - "group=76, " - "target_property_id=5, run_state=WAITING_FOR_TARGET_AVAILABILITY}]}", - animation_->id(), element_id_.ToString().c_str()), - animation_->ToString()); + animation_->AddKeyframeModel(KeyframeModel::Create( + std::make_unique<FakeFloatAnimationCurve>(18), 45, 76, + KeyframeModel::TargetPropertyId(TargetProperty::BOUNDS))); + EXPECT_EQ(base::StringPrintf( + "Animation{id=%d, element_id=%s, " + "keyframe_models=[KeyframeModel{id=42, " + "group=73, target_property_type=1, custom_property_name=, " + "native_property_type=1, " + "run_state=WAITING_FOR_TARGET_AVAILABILITY, element_id=(0)}, " + "KeyframeModel{id=45, group=76, target_property_type=5, " + "custom_property_name=, native_property_type=1, " + "run_state=WAITING_FOR_TARGET_AVAILABILITY, element_id=(0)}]}", + animation_->id(), element_id_.ToString().c_str()), + animation_->ToString()); } } // namespace diff --git a/chromium/cc/animation/element_animations.cc b/chromium/cc/animation/element_animations.cc index 03996f2b245..c8e8637523c 100644 --- a/chromium/cc/animation/element_animations.cc +++ b/chromium/cc/animation/element_animations.cc @@ -7,17 +7,19 @@ #include <stddef.h> #include <algorithm> +#include <utility> #include "base/numerics/ranges.h" #include "cc/animation/animation_delegate.h" #include "cc/animation/animation_events.h" #include "cc/animation/animation_host.h" #include "cc/animation/keyframe_effect.h" -#include "cc/animation/keyframed_animation_curve.h" -#include "cc/animation/transform_operations.h" +#include "cc/animation/keyframe_model.h" #include "cc/paint/filter_operations.h" #include "cc/trees/mutator_host_client.h" +#include "ui/gfx/animation/keyframe/keyframed_animation_curve.h" #include "ui/gfx/geometry/box_f.h" +#include "ui/gfx/transform_operations.h" namespace cc { @@ -29,12 +31,18 @@ namespace { // TODO(flackr): Remove ElementId from ElementAnimations once all element // tracking is done on the KeyframeModel - https://crbug.com/900241 ElementId CalculateTargetElementId(const ElementAnimations* element_animations, - const KeyframeModel* keyframe_model) { - if (LIKELY(keyframe_model->element_id())) - return keyframe_model->element_id(); + const gfx::KeyframeModel* keyframe_model) { + if (LIKELY(KeyframeModel::ToCcKeyframeModel(keyframe_model)->element_id())) + return KeyframeModel::ToCcKeyframeModel(keyframe_model)->element_id(); return element_animations->element_id(); } +bool UsingPaintWorklet(int property_index) { + // The set of properties where its animation uses paint worklet infra. + return property_index == TargetProperty::CSS_CUSTOM_PROPERTY || + property_index == TargetProperty::NATIVE_PROPERTY; +} + } // namespace scoped_refptr<ElementAnimations> ElementAnimations::Create( @@ -51,10 +59,8 @@ ElementAnimations::ElementAnimations(AnimationHost* host, ElementId element_id) has_element_in_active_list_(false), has_element_in_pending_list_(false), needs_push_properties_(false), - active_maximum_scale_(kNotScaled), - active_starting_scale_(kNotScaled), - pending_maximum_scale_(kNotScaled), - pending_starting_scale_(kNotScaled) { + active_maximum_scale_(kInvalidScale), + pending_maximum_scale_(kInvalidScale) { InitAffectedElementTypes(); } @@ -75,8 +81,8 @@ void ElementAnimations::InitAffectedElementTypes() { } } -TargetProperties ElementAnimations::GetPropertiesMaskForAnimationState() { - TargetProperties properties; +gfx::TargetProperties ElementAnimations::GetPropertiesMaskForAnimationState() { + gfx::TargetProperties properties; properties[TargetProperty::TRANSFORM] = true; properties[TargetProperty::OPACITY] = true; properties[TargetProperty::FILTER] = true; @@ -88,7 +94,8 @@ void ElementAnimations::ClearAffectedElementTypes( const PropertyToElementIdMap& element_id_map) { DCHECK(animation_host_); - TargetProperties disable_properties = GetPropertiesMaskForAnimationState(); + gfx::TargetProperties disable_properties = + GetPropertiesMaskForAnimationState(); PropertyAnimationState disabled_state_mask, disabled_state; disabled_state_mask.currently_running = disable_properties; disabled_state_mask.potentially_animating = disable_properties; @@ -147,7 +154,7 @@ void ElementAnimations::RemoveKeyframeEffect(KeyframeEffect* keyframe_effect) { } bool ElementAnimations::IsEmpty() const { - return !keyframe_effects_list_.might_have_observers(); + return keyframe_effects_list_.empty(); } void ElementAnimations::SetNeedsPushProperties() { @@ -183,25 +190,13 @@ bool ElementAnimations::AnimationsPreserveAxisAlignment() const { return true; } -void ElementAnimations::GetAnimationScales(ElementListType list_type, - float* maximum_scale, - float* starting_scale) const { - *maximum_scale = kNotScaled; - *starting_scale = kNotScaled; +float ElementAnimations::MaximumScale(ElementListType list_type) const { + float maximum_scale = kInvalidScale; for (auto& keyframe_effect : keyframe_effects_list_) { - float keyframe_effect_maximum_scale = kNotScaled; - float keyframe_effect_starting_scale = kNotScaled; - bool success = keyframe_effect.GetAnimationScales( - list_type, &keyframe_effect_maximum_scale, - &keyframe_effect_starting_scale); - if (!success) { - *maximum_scale = kNotScaled; - *starting_scale = kNotScaled; - return; - } - *maximum_scale = std::max(*maximum_scale, keyframe_effect_maximum_scale); - *starting_scale = std::max(*starting_scale, keyframe_effect_starting_scale); + maximum_scale = + std::max(maximum_scale, keyframe_effect.MaximumScale(list_type)); } + return maximum_scale; } bool ElementAnimations::ScrollOffsetAnimationWasInterrupted() const { @@ -212,19 +207,21 @@ bool ElementAnimations::ScrollOffsetAnimationWasInterrupted() const { return false; } -void ElementAnimations::NotifyClientFloatAnimated( - float value, - int target_property_id, - KeyframeModel* keyframe_model) { - switch (keyframe_model->target_property_id()) { +void ElementAnimations::OnFloatAnimated(const float& value, + int target_property_id, + gfx::KeyframeModel* keyframe_model) { + switch (keyframe_model->TargetProperty()) { case TargetProperty::CSS_CUSTOM_PROPERTY: + case TargetProperty::NATIVE_PROPERTY: // Custom properties are only tracked on the pending tree, where they may // be used as inputs for PaintWorklets (which are only dispatched from the // pending tree). As such, we don't need to notify in the case where a // KeyframeModel only affects active elements. if (KeyframeModelAffectsPendingElements(keyframe_model)) - OnCustomPropertyAnimated(PaintWorkletInput::PropertyValue(value), - keyframe_model); + OnCustomPropertyAnimated( + PaintWorkletInput::PropertyValue(value), + KeyframeModel::ToCcKeyframeModel(keyframe_model), + target_property_id); break; case TargetProperty::OPACITY: { float opacity = base::ClampToRange(value, 0.0f, 1.0f); @@ -239,11 +236,10 @@ void ElementAnimations::NotifyClientFloatAnimated( } } -void ElementAnimations::NotifyClientFilterAnimated( - const FilterOperations& filters, - int target_property_id, - KeyframeModel* keyframe_model) { - switch (keyframe_model->target_property_id()) { +void ElementAnimations::OnFilterAnimated(const FilterOperations& filters, + int target_property_id, + gfx::KeyframeModel* keyframe_model) { + switch (keyframe_model->TargetProperty()) { case TargetProperty::BACKDROP_FILTER: if (KeyframeModelAffectsActiveElements(keyframe_model)) OnBackdropFilterAnimated(ElementListType::ACTIVE, filters, @@ -263,20 +259,20 @@ void ElementAnimations::NotifyClientFilterAnimated( } } -void ElementAnimations::NotifyClientColorAnimated( - SkColor value, - int target_property_id, - KeyframeModel* keyframe_model) { - DCHECK_EQ(keyframe_model->target_property_id(), +void ElementAnimations::OnColorAnimated(const SkColor& value, + int target_property_id, + gfx::KeyframeModel* keyframe_model) { + DCHECK_EQ(keyframe_model->TargetProperty(), TargetProperty::CSS_CUSTOM_PROPERTY); OnCustomPropertyAnimated(PaintWorkletInput::PropertyValue(value), - keyframe_model); + KeyframeModel::ToCcKeyframeModel(keyframe_model), + target_property_id); } -void ElementAnimations::NotifyClientTransformOperationsAnimated( - const TransformOperations& operations, +void ElementAnimations::OnTransformAnimated( + const gfx::TransformOperations& operations, int target_property_id, - KeyframeModel* keyframe_model) { + gfx::KeyframeModel* keyframe_model) { gfx::Transform transform = operations.Apply(); if (KeyframeModelAffectsActiveElements(keyframe_model)) OnTransformAnimated(ElementListType::ACTIVE, transform, keyframe_model); @@ -284,10 +280,10 @@ void ElementAnimations::NotifyClientTransformOperationsAnimated( OnTransformAnimated(ElementListType::PENDING, transform, keyframe_model); } -void ElementAnimations::NotifyClientScrollOffsetAnimated( +void ElementAnimations::OnScrollOffsetAnimated( const gfx::ScrollOffset& scroll_offset, int target_property_id, - KeyframeModel* keyframe_model) { + gfx::KeyframeModel* keyframe_model) { if (KeyframeModelAffectsActiveElements(keyframe_model)) OnScrollOffsetAnimated(ElementListType::ACTIVE, scroll_offset, keyframe_model); @@ -301,6 +297,8 @@ void ElementAnimations::InitClientAnimationState() { // (instead of only changed) recalculated current states to the client. pending_state_.Clear(); active_state_.Clear(); + active_maximum_scale_ = kInvalidScale; + pending_maximum_scale_ = kInvalidScale; UpdateClientAnimationState(); } @@ -326,7 +324,8 @@ void ElementAnimations::UpdateClientAnimationState() { active_state_ |= keyframe_effect_active_state; } - TargetProperties allowed_properties = GetPropertiesMaskForAnimationState(); + gfx::TargetProperties allowed_properties = + GetPropertiesMaskForAnimationState(); PropertyAnimationState allowed_state; allowed_state.currently_running = allowed_properties; allowed_state.potentially_animating = allowed_properties; @@ -347,19 +346,13 @@ void ElementAnimations::UpdateClientAnimationState() { element_id_map, ElementListType::ACTIVE, diff_active, active_state_); } - float maximum_scale = kNotScaled; - float starting_scale = kNotScaled; - if (transform_element_id) { - GetAnimationScales(ElementListType::ACTIVE, &maximum_scale, - &starting_scale); - } - if (maximum_scale != active_maximum_scale_ || - starting_scale != active_starting_scale_) { - animation_host_->mutator_host_client()->AnimationScalesChanged( - transform_element_id, ElementListType::ACTIVE, maximum_scale, - starting_scale); + float maximum_scale = transform_element_id + ? MaximumScale(ElementListType::ACTIVE) + : kInvalidScale; + if (maximum_scale != active_maximum_scale_) { + animation_host_->mutator_host_client()->MaximumScaleChanged( + transform_element_id, ElementListType::ACTIVE, maximum_scale); active_maximum_scale_ = maximum_scale; - active_starting_scale_ = starting_scale; } } @@ -371,23 +364,42 @@ void ElementAnimations::UpdateClientAnimationState() { pending_state_); } - float maximum_scale = kNotScaled; - float starting_scale = kNotScaled; - if (transform_element_id) { - GetAnimationScales(ElementListType::PENDING, &maximum_scale, - &starting_scale); - } - if (maximum_scale != pending_maximum_scale_ || - starting_scale != pending_starting_scale_) { - animation_host_->mutator_host_client()->AnimationScalesChanged( - transform_element_id, ElementListType::PENDING, maximum_scale, - starting_scale); + float maximum_scale = transform_element_id + ? MaximumScale(ElementListType::PENDING) + : kInvalidScale; + if (maximum_scale != pending_maximum_scale_) { + animation_host_->mutator_host_client()->MaximumScaleChanged( + transform_element_id, ElementListType::PENDING, maximum_scale); pending_maximum_scale_ = maximum_scale; - pending_starting_scale_ = starting_scale; } } } +void ElementAnimations::AttachToCurve(gfx::AnimationCurve* c) { + switch (c->Type()) { + case gfx::AnimationCurve::COLOR: + gfx::ColorAnimationCurve::ToColorAnimationCurve(c)->set_target(this); + break; + case gfx::AnimationCurve::FLOAT: + gfx::FloatAnimationCurve::ToFloatAnimationCurve(c)->set_target(this); + break; + case gfx::AnimationCurve::TRANSFORM: + gfx::TransformAnimationCurve::ToTransformAnimationCurve(c)->set_target( + this); + break; + case gfx::AnimationCurve::FILTER: + FilterAnimationCurve::ToFilterAnimationCurve(c)->set_target(this); + break; + case gfx::AnimationCurve::SCROLL_OFFSET: + ScrollOffsetAnimationCurve::ToScrollOffsetAnimationCurve(c)->set_target( + this); + break; + default: + NOTREACHED(); + break; + } +} + bool ElementAnimations::HasTickingKeyframeEffect() const { for (auto& keyframe_effect : keyframe_effects_list_) { if (keyframe_effect.HasTickingKeyframeModel()) @@ -441,7 +453,7 @@ bool ElementAnimations::IsCurrentlyAnimatingProperty( void ElementAnimations::OnFilterAnimated(ElementListType list_type, const FilterOperations& filters, - KeyframeModel* keyframe_model) { + gfx::KeyframeModel* keyframe_model) { ElementId target_element_id = CalculateTargetElementId(this, keyframe_model); DCHECK(target_element_id); DCHECK(animation_host_); @@ -453,7 +465,7 @@ void ElementAnimations::OnFilterAnimated(ElementListType list_type, void ElementAnimations::OnBackdropFilterAnimated( ElementListType list_type, const FilterOperations& backdrop_filters, - KeyframeModel* keyframe_model) { + gfx::KeyframeModel* keyframe_model) { ElementId target_element_id = CalculateTargetElementId(this, keyframe_model); DCHECK(target_element_id); DCHECK(animation_host_); @@ -464,7 +476,7 @@ void ElementAnimations::OnBackdropFilterAnimated( void ElementAnimations::OnOpacityAnimated(ElementListType list_type, float opacity, - KeyframeModel* keyframe_model) { + gfx::KeyframeModel* keyframe_model) { ElementId target_element_id = CalculateTargetElementId(this, keyframe_model); DCHECK(target_element_id); DCHECK(animation_host_); @@ -474,18 +486,26 @@ void ElementAnimations::OnOpacityAnimated(ElementListType list_type, } void ElementAnimations::OnCustomPropertyAnimated( - PaintWorkletInput::PropertyValue custom_prop_value, - KeyframeModel* keyframe_model) { + PaintWorkletInput::PropertyValue property_value, + KeyframeModel* keyframe_model, + int target_property_id) { DCHECK(animation_host_); DCHECK(animation_host_->mutator_host_client()); + ElementId id = CalculateTargetElementId(this, keyframe_model); + PaintWorkletInput::PropertyKey property_key = + target_property_id == TargetProperty::NATIVE_PROPERTY + ? PaintWorkletInput::PropertyKey( + keyframe_model->native_property_type(), id) + : PaintWorkletInput::PropertyKey( + keyframe_model->custom_property_name(), id); animation_host_->mutator_host_client()->OnCustomPropertyMutated( - CalculateTargetElementId(this, keyframe_model), - keyframe_model->custom_property_name(), std::move(custom_prop_value)); + std::move(property_key), std::move(property_value)); } -void ElementAnimations::OnTransformAnimated(ElementListType list_type, - const gfx::Transform& transform, - KeyframeModel* keyframe_model) { +void ElementAnimations::OnTransformAnimated( + ElementListType list_type, + const gfx::Transform& transform, + gfx::KeyframeModel* keyframe_model) { ElementId target_element_id = CalculateTargetElementId(this, keyframe_model); DCHECK(target_element_id); DCHECK(animation_host_); @@ -497,7 +517,7 @@ void ElementAnimations::OnTransformAnimated(ElementListType list_type, void ElementAnimations::OnScrollOffsetAnimated( ElementListType list_type, const gfx::ScrollOffset& scroll_offset, - KeyframeModel* keyframe_model) { + gfx::KeyframeModel* keyframe_model) { ElementId target_element_id = CalculateTargetElementId(this, keyframe_model); DCHECK(target_element_id); DCHECK(animation_host_); @@ -534,6 +554,18 @@ PropertyToElementIdMap ElementAnimations::GetPropertyToElementIdMap() const { for (int property_index = TargetProperty::FIRST_TARGET_PROPERTY; property_index <= TargetProperty::LAST_TARGET_PROPERTY; ++property_index) { + // We skip the set of properties that uses paint worklet, because the + // animation is not directly associated with the element its compositing + // layer targets and we use reserved element id when we attach a layer for + // the animation. In that case, the DCHECK here is no longer applicable. + // For example, when we have two paint worklet elements with two different + // custom property animations, then these two KeyframeModels would have + // different element_id and thus fail the first DCHECK here. + // It is not valid to include these properties in the PropertyToElementIdMap + // as they do not map to a single element id. Therefore, these properties + // should not be included in the map. + if (UsingPaintWorklet(property_index)) + continue; TargetProperty::Type property = static_cast<TargetProperty::Type>(property_index); ElementId element_id_for_property; @@ -579,7 +611,7 @@ unsigned int ElementAnimations::CountKeyframesForTesting() const { } KeyframeEffect* ElementAnimations::FirstKeyframeEffectForTesting() const { - DCHECK(keyframe_effects_list_.might_have_observers()); + DCHECK(!keyframe_effects_list_.empty()); return &*keyframe_effects_list_.begin(); } @@ -589,24 +621,26 @@ bool ElementAnimations::HasKeyframeEffectForTesting( } bool ElementAnimations::KeyframeModelAffectsActiveElements( - KeyframeModel* keyframe_model) const { + gfx::KeyframeModel* keyframe_model) const { // When we force a keyframe_model update due to a notification, we do not have // a KeyframeModel instance. In this case, we force an update of active // elements. if (!keyframe_model) return true; - return keyframe_model->affects_active_elements() && + return KeyframeModel::ToCcKeyframeModel(keyframe_model) + ->affects_active_elements() && has_element_in_active_list(); } bool ElementAnimations::KeyframeModelAffectsPendingElements( - KeyframeModel* keyframe_model) const { + gfx::KeyframeModel* keyframe_model) const { // When we force a keyframe_model update due to a notification, we do not have // a KeyframeModel instance. In this case, we force an update of pending // elements. if (!keyframe_model) return true; - return keyframe_model->affects_pending_elements() && + return KeyframeModel::ToCcKeyframeModel(keyframe_model) + ->affects_pending_elements() && has_element_in_pending_list(); } diff --git a/chromium/cc/animation/element_animations.h b/chromium/cc/animation/element_animations.h index 22a29defd10..e306a69e992 100644 --- a/chromium/cc/animation/element_animations.h +++ b/chromium/cc/animation/element_animations.h @@ -11,20 +11,27 @@ #include "base/memory/ref_counted.h" #include "base/observer_list.h" #include "cc/animation/animation_export.h" -#include "cc/animation/animation_target.h" +#include "cc/animation/filter_animation_curve.h" +#include "cc/animation/scroll_offset_animation_curve.h" #include "cc/paint/element_id.h" #include "cc/paint/paint_worklet_input.h" #include "cc/trees/property_animation_state.h" #include "cc/trees/target_property.h" +#include "ui/gfx/animation/keyframe/animation_curve.h" +#include "ui/gfx/animation/keyframe/target_property.h" #include "ui/gfx/geometry/scroll_offset.h" #include "ui/gfx/transform.h" +namespace gfx { +class TransformOperations; +} // namespace gfx + namespace cc { class AnimationHost; class FilterOperations; class KeyframeEffect; -class TransformOperations; +class KeyframeModel; enum class ElementListType; // An ElementAnimations owns a list of all KeyframeEffects attached to a single @@ -34,7 +41,11 @@ enum class ElementListType; // of the word; this naming is a legacy leftover. A target is just an amorphous // blob that has properties that can be animated. class CC_ANIMATION_EXPORT ElementAnimations - : public AnimationTarget, + : public gfx::FloatAnimationCurve::Target, + public gfx::ColorAnimationCurve::Target, + public gfx::TransformAnimationCurve::Target, + public ScrollOffsetAnimationCurve::Target, + public FilterAnimationCurve::Target, public base::RefCounted<ElementAnimations> { public: static scoped_refptr<ElementAnimations> Create(AnimationHost* host, @@ -107,15 +118,10 @@ class CC_ANIMATION_EXPORT ElementAnimations bool AnimationsPreserveAxisAlignment() const; - // Gets scales transform animations. On return, |maximum_scale| is the maximum - // scale along any dimension at any destination in active scale animations, - // and |starting_scale| is the maximum of starting animation scale along any - // dimension at any destination in active scale animations. They are set to - // kNotScaled if there is no active scale animation or the scales cannot be - // computed. - void GetAnimationScales(ElementListType list_type, - float* maximum_scale, - float* starting_scale) const; + // Returns the maximum scale along any dimension at any destination in active + // scale animations, or kInvalidScale if there is no active transform + // animation or the scale cannot be computed. + float MaximumScale(ElementListType list_type) const; bool ScrollOffsetAnimationWasInterrupted() const; @@ -129,25 +135,28 @@ class CC_ANIMATION_EXPORT ElementAnimations // that have changed since the last update. void UpdateClientAnimationState(); - void NotifyClientFloatAnimated(float value, - int target_property_id, - KeyframeModel* keyframe_model) override; - void NotifyClientFilterAnimated(const FilterOperations& filter, - int target_property_id, - KeyframeModel* keyframe_model) override; - void NotifyClientSizeAnimated(const gfx::SizeF& size, - int target_property_id, - KeyframeModel* keyframe_model) override {} - void NotifyClientColorAnimated(SkColor color, - int target_property_id, - KeyframeModel* keyframe_model) override; - void NotifyClientTransformOperationsAnimated( - const TransformOperations& operations, - int target_property_id, - KeyframeModel* keyframe_model) override; - void NotifyClientScrollOffsetAnimated(const gfx::ScrollOffset& scroll_offset, - int target_property_id, - KeyframeModel* keyframe_model) override; + // TODO(crbug.com/1176334): Animation targets should be attached to curves + // when they're created and the concrete subclass is known. This function + // exists as a stopgap: the animation machinery previously expected to + // announce a target and then pass curves that would implicitly animate the + // target (i.e., the machinery handled the attachment). + void AttachToCurve(gfx::AnimationCurve* c); + + void OnFloatAnimated(const float& value, + int target_property_id, + gfx::KeyframeModel* keyframe_model) override; + void OnFilterAnimated(const FilterOperations& filter, + int target_property_id, + gfx::KeyframeModel* keyframe_model) override; + void OnColorAnimated(const SkColor& color, + int target_property_id, + gfx::KeyframeModel* keyframe_model) override; + void OnTransformAnimated(const gfx::TransformOperations& operations, + int target_property_id, + gfx::KeyframeModel* keyframe_model) override; + void OnScrollOffsetAnimated(const gfx::ScrollOffset& scroll_offset, + int target_property_id, + gfx::KeyframeModel* keyframe_model) override; gfx::ScrollOffset ScrollOffsetForAnimation() const; @@ -174,30 +183,40 @@ class CC_ANIMATION_EXPORT ElementAnimations void OnFilterAnimated(ElementListType list_type, const FilterOperations& filters, - KeyframeModel* keyframe_model); + gfx::KeyframeModel* keyframe_model); void OnBackdropFilterAnimated(ElementListType list_type, const FilterOperations& backdrop_filters, - KeyframeModel* keyframe_model); + gfx::KeyframeModel* keyframe_model); void OnOpacityAnimated(ElementListType list_type, float opacity, - KeyframeModel* keyframe_model); - void OnCustomPropertyAnimated( - PaintWorkletInput::PropertyValue custom_prop_value, - KeyframeModel* keyframe_model); + gfx::KeyframeModel* keyframe_model); + // In addition to custom property animations, these also represent animations + // of native properties whose values are known to the Blink PaintWorklet + // responsible for painting them but not known to the compositor. The + // compositor animates a simple float progress which is then passed into blink + // code to interpolate. Unlike other native properties listed above, CC is not + // capable of drawing interpolations of these properties and defers to + // NativePaintWorklet subclasses to interpret the animation progress as it + // pertains to how to paint the native property. + void OnCustomPropertyAnimated(PaintWorkletInput::PropertyValue property_value, + KeyframeModel* keyframe_model, + int target_property_id); void OnTransformAnimated(ElementListType list_type, const gfx::Transform& transform, - KeyframeModel* keyframe_model); + gfx::KeyframeModel* keyframe_model); void OnScrollOffsetAnimated(ElementListType list_type, const gfx::ScrollOffset& scroll_offset, - KeyframeModel* keyframe_model); + gfx::KeyframeModel* keyframe_model); - static TargetProperties GetPropertiesMaskForAnimationState(); + static gfx::TargetProperties GetPropertiesMaskForAnimationState(); void UpdateKeyframeEffectsTickingState() const; void RemoveKeyframeEffectsFromTicking() const; - bool KeyframeModelAffectsActiveElements(KeyframeModel* keyframe_model) const; - bool KeyframeModelAffectsPendingElements(KeyframeModel* keyframe_model) const; + bool KeyframeModelAffectsActiveElements( + gfx::KeyframeModel* keyframe_model) const; + bool KeyframeModelAffectsPendingElements( + gfx::KeyframeModel* keyframe_model) const; base::ObserverList<KeyframeEffect>::Unchecked keyframe_effects_list_; AnimationHost* animation_host_; @@ -211,9 +230,7 @@ class CC_ANIMATION_EXPORT ElementAnimations PropertyAnimationState active_state_; PropertyAnimationState pending_state_; float active_maximum_scale_; - float active_starting_scale_; float pending_maximum_scale_; - float pending_starting_scale_; }; } // namespace cc diff --git a/chromium/cc/animation/element_animations_unittest.cc b/chromium/cc/animation/element_animations_unittest.cc index 9e1fa8337f3..e5fecd65354 100644 --- a/chromium/cc/animation/element_animations_unittest.cc +++ b/chromium/cc/animation/element_animations_unittest.cc @@ -4,6 +4,9 @@ #include "cc/animation/element_animations.h" +#include <limits> +#include <utility> + #include "base/memory/ptr_util.h" #include "cc/animation/animation.h" #include "cc/animation/animation_delegate.h" @@ -12,13 +15,13 @@ #include "cc/animation/animation_id_provider.h" #include "cc/animation/animation_timeline.h" #include "cc/animation/keyframe_effect.h" -#include "cc/animation/keyframed_animation_curve.h" #include "cc/animation/scroll_offset_animation_curve.h" #include "cc/animation/scroll_offset_animation_curve_factory.h" -#include "cc/animation/transform_operations.h" #include "cc/test/animation_test_common.h" #include "cc/test/animation_timelines_test_common.h" +#include "ui/gfx/animation/keyframe/keyframed_animation_curve.h" #include "ui/gfx/geometry/box_f.h" +#include "ui/gfx/transform_operations.h" namespace cc { namespace { @@ -40,9 +43,7 @@ class ElementAnimationsTest : public AnimationTimelinesTest { ElementAnimationsTest() = default; ~ElementAnimationsTest() override = default; - void SetUp() override { - AnimationTimelinesTest::SetUp(); - } + void SetUp() override { AnimationTimelinesTest::SetUp(); } void CreateImplTimelineAndAnimation() override { AnimationTimelinesTest::CreateImplTimelineAndAnimation(); @@ -269,14 +270,15 @@ TEST_F(ElementAnimationsTest, curve_fixed->SetInitialValue(initial_value); const int animation1_id = 1; std::unique_ptr<KeyframeModel> animation_fixed(KeyframeModel::Create( - std::move(curve_fixed), animation1_id, 0, TargetProperty::SCROLL_OFFSET)); + std::move(curve_fixed), animation1_id, 0, + KeyframeModel::TargetPropertyId(TargetProperty::SCROLL_OFFSET))); animation_->AddKeyframeModel(std::move(animation_fixed)); PushProperties(); - EXPECT_VECTOR2DF_EQ(initial_value, animation_impl_->keyframe_effect() - ->GetKeyframeModelById(animation1_id) - ->curve() - ->ToScrollOffsetAnimationCurve() - ->GetValue(base::TimeDelta())); + auto* scroll_curve = ScrollOffsetAnimationCurve::ToScrollOffsetAnimationCurve( + animation_impl_->keyframe_effect() + ->GetKeyframeModelById(animation1_id) + ->curve()); + EXPECT_VECTOR2DF_EQ(initial_value, scroll_curve->GetValue(base::TimeDelta())); animation_->RemoveKeyframeModel(animation1_id); // Animation without initial value set. @@ -285,15 +287,16 @@ TEST_F(ElementAnimationsTest, target_value)); const int animation2_id = 2; std::unique_ptr<KeyframeModel> keyframe_model(KeyframeModel::Create( - std::move(curve), animation2_id, 1, TargetProperty::SCROLL_OFFSET)); + std::move(curve), animation2_id, 1, + KeyframeModel::TargetPropertyId(TargetProperty::SCROLL_OFFSET))); animation_->AddKeyframeModel(std::move(keyframe_model)); PushProperties(); + scroll_curve = static_cast<ScrollOffsetAnimationCurve*>( + animation_impl_->keyframe_effect() + ->GetKeyframeModelById(animation2_id) + ->curve()); EXPECT_VECTOR2DF_EQ(provider_initial_value, - animation_impl_->keyframe_effect() - ->GetKeyframeModelById(animation2_id) - ->curve() - ->ToScrollOffsetAnimationCurve() - ->GetValue(base::TimeDelta())); + scroll_curve->GetValue(base::TimeDelta())); animation_->RemoveKeyframeModel(animation2_id); } @@ -748,10 +751,11 @@ TEST_F(ElementAnimationsTest, AnimationsAreDeleted) { // Tests that transitioning opacity from 0 to 1 works as expected. static std::unique_ptr<KeyframeModel> CreateKeyframeModel( - std::unique_ptr<AnimationCurve> curve, + std::unique_ptr<gfx::AnimationCurve> curve, int group_id, TargetProperty::Type property) { - return KeyframeModel::Create(std::move(curve), 0, group_id, property); + return KeyframeModel::Create(std::move(curve), 0, group_id, + KeyframeModel::TargetPropertyId(property)); } TEST_F(ElementAnimationsTest, TrivialTransition) { @@ -760,9 +764,10 @@ TEST_F(ElementAnimationsTest, TrivialTransition) { auto events = CreateEventsForTesting(); - std::unique_ptr<KeyframeModel> to_add(CreateKeyframeModel( - std::unique_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)), - 1, TargetProperty::OPACITY)); + std::unique_ptr<KeyframeModel> to_add( + CreateKeyframeModel(std::unique_ptr<gfx::AnimationCurve>( + new FakeFloatTransition(1.0, 0.f, 1.f)), + 1, TargetProperty::OPACITY)); int keyframe_model_id = to_add->id(); EXPECT_FALSE( @@ -807,8 +812,9 @@ TEST_F(ElementAnimationsTest, FilterTransition) { curve->AddKeyframe(FilterKeyframe::Create(base::TimeDelta::FromSecondsD(1.0), end_filters, nullptr)); - std::unique_ptr<KeyframeModel> keyframe_model( - KeyframeModel::Create(std::move(curve), 1, 0, TargetProperty::FILTER)); + std::unique_ptr<KeyframeModel> keyframe_model(KeyframeModel::Create( + std::move(curve), 1, 0, + KeyframeModel::TargetPropertyId(TargetProperty::FILTER))); animation_->AddKeyframeModel(std::move(keyframe_model)); animation_->Tick(kInitialTickTime); @@ -850,7 +856,8 @@ TEST_F(ElementAnimationsTest, BackdropFilterTransition) { end_filters, nullptr)); std::unique_ptr<KeyframeModel> keyframe_model(KeyframeModel::Create( - std::move(curve), 1, 0, TargetProperty::BACKDROP_FILTER)); + std::move(curve), 1, 0, + KeyframeModel::TargetPropertyId(TargetProperty::BACKDROP_FILTER))); animation_->AddKeyframeModel(std::move(keyframe_model)); animation_->Tick(kInitialTickTime); @@ -889,7 +896,8 @@ TEST_F(ElementAnimationsTest, ScrollOffsetTransition) { target_value)); std::unique_ptr<KeyframeModel> keyframe_model(KeyframeModel::Create( - std::move(curve), 1, 0, TargetProperty::SCROLL_OFFSET)); + std::move(curve), 1, 0, + KeyframeModel::TargetPropertyId(TargetProperty::SCROLL_OFFSET))); keyframe_model->set_needs_synchronized_start_time(true); animation_->AddKeyframeModel(std::move(keyframe_model)); @@ -961,7 +969,8 @@ TEST_F(ElementAnimationsTest, ScrollOffsetTransitionOnImplOnly) { double duration_in_seconds = curve->Duration().InSecondsF(); std::unique_ptr<KeyframeModel> keyframe_model(KeyframeModel::Create( - std::move(curve), 1, 0, TargetProperty::SCROLL_OFFSET)); + std::move(curve), 1, 0, + KeyframeModel::TargetPropertyId(TargetProperty::SCROLL_OFFSET))); keyframe_model->SetIsImplOnly(); animation_impl_->AddKeyframeModel(std::move(keyframe_model)); @@ -1060,7 +1069,8 @@ TEST_F(ElementAnimationsTest, ScrollOffsetTransitionNoImplProvider) { target_value)); std::unique_ptr<KeyframeModel> keyframe_model(KeyframeModel::Create( - std::move(curve), 1, 0, TargetProperty::SCROLL_OFFSET)); + std::move(curve), 1, 0, + KeyframeModel::TargetPropertyId(TargetProperty::SCROLL_OFFSET))); keyframe_model->set_needs_synchronized_start_time(true); animation_->AddKeyframeModel(std::move(keyframe_model)); @@ -1139,7 +1149,8 @@ TEST_F(ElementAnimationsTest, ScrollOffsetRemovalClearsScrollDelta) { int keyframe_model_id = 1; std::unique_ptr<KeyframeModel> keyframe_model(KeyframeModel::Create( - std::move(curve), keyframe_model_id, 0, TargetProperty::SCROLL_OFFSET)); + std::move(curve), keyframe_model_id, 0, + KeyframeModel::TargetPropertyId(TargetProperty::SCROLL_OFFSET))); keyframe_model->set_needs_synchronized_start_time(true); animation_->AddKeyframeModel(std::move(keyframe_model)); PushProperties(); @@ -1166,8 +1177,9 @@ TEST_F(ElementAnimationsTest, ScrollOffsetRemovalClearsScrollDelta) { // Now, test the 2-argument version of RemoveKeyframeModel. curve = ScrollOffsetAnimationCurveFactory::CreateEaseInOutAnimationForTesting( target_value); - keyframe_model = KeyframeModel::Create(std::move(curve), keyframe_model_id, 0, - TargetProperty::SCROLL_OFFSET); + keyframe_model = KeyframeModel::Create( + std::move(curve), keyframe_model_id, 0, + KeyframeModel::TargetPropertyId(TargetProperty::SCROLL_OFFSET)); keyframe_model->set_needs_synchronized_start_time(true); animation_->AddKeyframeModel(std::move(keyframe_model)); PushProperties(); @@ -1261,7 +1273,8 @@ TEST_F(ElementAnimationsTest, curve->SetInitialValue(initial_value); TimeDelta duration = curve->Duration(); std::unique_ptr<KeyframeModel> to_add(KeyframeModel::Create( - std::move(curve), 1, 0, TargetProperty::SCROLL_OFFSET)); + std::move(curve), 1, 0, + KeyframeModel::TargetPropertyId(TargetProperty::SCROLL_OFFSET))); to_add->SetIsImplOnly(); animation_impl_->AddKeyframeModel(std::move(to_add)); @@ -1333,9 +1346,10 @@ TEST_F(ElementAnimationsTest, auto events = CreateEventsForTesting(); - std::unique_ptr<KeyframeModel> to_add(CreateKeyframeModel( - std::unique_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)), - 1, TargetProperty::OPACITY)); + std::unique_ptr<KeyframeModel> to_add( + CreateKeyframeModel(std::unique_ptr<gfx::AnimationCurve>( + new FakeFloatTransition(1.0, 0.f, 1.f)), + 1, TargetProperty::OPACITY)); to_add->set_needs_synchronized_start_time(true); int keyframe_model_id = to_add->id(); @@ -1378,11 +1392,15 @@ TEST_F(ElementAnimationsTest, TrivialQueuing) { int animation1_id = 1; int animation2_id = 2; animation_->AddKeyframeModel(KeyframeModel::Create( - std::unique_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)), - animation1_id, 1, TargetProperty::OPACITY)); + std::unique_ptr<gfx::AnimationCurve>( + new FakeFloatTransition(1.0, 0.f, 1.f)), + animation1_id, 1, + KeyframeModel::TargetPropertyId(TargetProperty::OPACITY))); animation_->AddKeyframeModel(KeyframeModel::Create( - std::unique_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 1.f, 0.5f)), - animation2_id, 2, TargetProperty::OPACITY)); + std::unique_ptr<gfx::AnimationCurve>( + new FakeFloatTransition(1.0, 1.f, 0.5f)), + animation2_id, 2, + KeyframeModel::TargetPropertyId(TargetProperty::OPACITY))); animation_->Tick(kInitialTickTime); @@ -1435,17 +1453,19 @@ TEST_F(ElementAnimationsTest, Interrupt) { auto events = CreateEventsForTesting(); - animation_->AddKeyframeModel(CreateKeyframeModel( - std::unique_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)), - 1, TargetProperty::OPACITY)); + animation_->AddKeyframeModel( + CreateKeyframeModel(std::unique_ptr<gfx::AnimationCurve>( + new FakeFloatTransition(1.0, 0.f, 1.f)), + 1, TargetProperty::OPACITY)); animation_->Tick(kInitialTickTime); animation_->UpdateState(true, events.get()); EXPECT_TRUE(animation_->keyframe_effect()->HasTickingKeyframeModel()); EXPECT_EQ(0.f, client_.GetOpacity(element_id_, ElementListType::ACTIVE)); - std::unique_ptr<KeyframeModel> to_add(CreateKeyframeModel( - std::unique_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 1.f, 0.5f)), - 2, TargetProperty::OPACITY)); + std::unique_ptr<KeyframeModel> to_add( + CreateKeyframeModel(std::unique_ptr<gfx::AnimationCurve>( + new FakeFloatTransition(1.0, 1.f, 0.5f)), + 2, TargetProperty::OPACITY)); animation_->AbortKeyframeModelsWithProperty(TargetProperty::OPACITY, false); animation_->AddKeyframeModel(std::move(to_add)); @@ -1470,14 +1490,15 @@ TEST_F(ElementAnimationsTest, ScheduleTogetherWhenAPropertyIsBlocked) { auto events = CreateEventsForTesting(); animation_->AddKeyframeModel(CreateKeyframeModel( - std::unique_ptr<AnimationCurve>(new FakeTransformTransition(1)), 1, + std::unique_ptr<gfx::AnimationCurve>(new FakeTransformTransition(1)), 1, TargetProperty::TRANSFORM)); animation_->AddKeyframeModel(CreateKeyframeModel( - std::unique_ptr<AnimationCurve>(new FakeTransformTransition(1)), 2, + std::unique_ptr<gfx::AnimationCurve>(new FakeTransformTransition(1)), 2, TargetProperty::TRANSFORM)); - animation_->AddKeyframeModel(CreateKeyframeModel( - std::unique_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)), - 2, TargetProperty::OPACITY)); + animation_->AddKeyframeModel( + CreateKeyframeModel(std::unique_ptr<gfx::AnimationCurve>( + new FakeFloatTransition(1.0, 0.f, 1.f)), + 2, TargetProperty::OPACITY)); animation_->Tick(kInitialTickTime); animation_->UpdateState(true, events.get()); @@ -1505,14 +1526,16 @@ TEST_F(ElementAnimationsTest, ScheduleTogetherWithAnAnimWaiting) { auto events = CreateEventsForTesting(); animation_->AddKeyframeModel(CreateKeyframeModel( - std::unique_ptr<AnimationCurve>(new FakeTransformTransition(2)), 1, + std::unique_ptr<gfx::AnimationCurve>(new FakeTransformTransition(2)), 1, TargetProperty::TRANSFORM)); - animation_->AddKeyframeModel(CreateKeyframeModel( - std::unique_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)), - 1, TargetProperty::OPACITY)); - animation_->AddKeyframeModel(CreateKeyframeModel( - std::unique_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 1.f, 0.5f)), - 2, TargetProperty::OPACITY)); + animation_->AddKeyframeModel( + CreateKeyframeModel(std::unique_ptr<gfx::AnimationCurve>( + new FakeFloatTransition(1.0, 0.f, 1.f)), + 1, TargetProperty::OPACITY)); + animation_->AddKeyframeModel( + CreateKeyframeModel(std::unique_ptr<gfx::AnimationCurve>( + new FakeFloatTransition(1.0, 1.f, 0.5f)), + 2, TargetProperty::OPACITY)); // Animations with id 1 should both start now. animation_->Tick(kInitialTickTime); @@ -1543,9 +1566,10 @@ TEST_F(ElementAnimationsTest, TrivialLooping) { auto events = CreateEventsForTesting(); - std::unique_ptr<KeyframeModel> to_add(CreateKeyframeModel( - std::unique_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)), - 1, TargetProperty::OPACITY)); + std::unique_ptr<KeyframeModel> to_add( + CreateKeyframeModel(std::unique_ptr<gfx::AnimationCurve>( + new FakeFloatTransition(1.0, 0.f, 1.f)), + 1, TargetProperty::OPACITY)); to_add->set_iterations(3); animation_->AddKeyframeModel(std::move(to_add)); @@ -1587,9 +1611,10 @@ TEST_F(ElementAnimationsTest, InfiniteLooping) { auto events = CreateEventsForTesting(); - std::unique_ptr<KeyframeModel> to_add(CreateKeyframeModel( - std::unique_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)), - 1, TargetProperty::OPACITY)); + std::unique_ptr<KeyframeModel> to_add( + CreateKeyframeModel(std::unique_ptr<gfx::AnimationCurve>( + new FakeFloatTransition(1.0, 0.f, 1.f)), + 1, TargetProperty::OPACITY)); to_add->set_iterations(std::numeric_limits<double>::infinity()); animation_->AddKeyframeModel(std::move(to_add)); @@ -1632,9 +1657,10 @@ TEST_F(ElementAnimationsTest, PauseResume) { auto events = CreateEventsForTesting(); - animation_->AddKeyframeModel(CreateKeyframeModel( - std::unique_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)), - 1, TargetProperty::OPACITY)); + animation_->AddKeyframeModel( + CreateKeyframeModel(std::unique_ptr<gfx::AnimationCurve>( + new FakeFloatTransition(1.0, 0.f, 1.f)), + 1, TargetProperty::OPACITY)); animation_->Tick(kInitialTickTime); animation_->UpdateState(true, events.get()); @@ -1678,14 +1704,17 @@ TEST_F(ElementAnimationsTest, AbortAGroupedAnimation) { const int keyframe_model_id = 2; animation_->AddKeyframeModel(KeyframeModel::Create( - std::unique_ptr<AnimationCurve>(new FakeTransformTransition(1)), 1, 1, - TargetProperty::TRANSFORM)); + std::unique_ptr<gfx::AnimationCurve>(new FakeTransformTransition(1)), 1, + 1, KeyframeModel::TargetPropertyId(TargetProperty::TRANSFORM))); animation_->AddKeyframeModel(KeyframeModel::Create( - std::unique_ptr<AnimationCurve>(new FakeFloatTransition(2.0, 0.f, 1.f)), - keyframe_model_id, 1, TargetProperty::OPACITY)); + std::unique_ptr<gfx::AnimationCurve>( + new FakeFloatTransition(2.0, 0.f, 1.f)), + keyframe_model_id, 1, + KeyframeModel::TargetPropertyId(TargetProperty::OPACITY))); animation_->AddKeyframeModel(KeyframeModel::Create( - std::unique_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 1.f, 0.75f)), - 3, 2, TargetProperty::OPACITY)); + std::unique_ptr<gfx::AnimationCurve>( + new FakeFloatTransition(1.0, 1.f, 0.75f)), + 3, 2, KeyframeModel::TargetPropertyId(TargetProperty::OPACITY))); animation_->Tick(kInitialTickTime); animation_->UpdateState(true, events.get()); @@ -1719,9 +1748,10 @@ TEST_F(ElementAnimationsTest, PushUpdatesWhenSynchronizedStartTimeNeeded) { auto events = CreateEventsForTesting(); - std::unique_ptr<KeyframeModel> to_add(CreateKeyframeModel( - std::unique_ptr<AnimationCurve>(new FakeFloatTransition(2.0, 0.f, 1.f)), - 0, TargetProperty::OPACITY)); + std::unique_ptr<KeyframeModel> to_add( + CreateKeyframeModel(std::unique_ptr<gfx::AnimationCurve>( + new FakeFloatTransition(2.0, 0.f, 1.f)), + 0, TargetProperty::OPACITY)); to_add->set_needs_synchronized_start_time(true); animation_->AddKeyframeModel(std::move(to_add)); @@ -1752,7 +1782,7 @@ TEST_F(ElementAnimationsTest, SkipUpdateState) { auto events = CreateEventsForTesting(); std::unique_ptr<KeyframeModel> first_keyframe_model(CreateKeyframeModel( - std::unique_ptr<AnimationCurve>(new FakeTransformTransition(1)), 1, + std::unique_ptr<gfx::AnimationCurve>(new FakeTransformTransition(1)), 1, TargetProperty::TRANSFORM)); first_keyframe_model->set_is_controlling_instance_for_test(true); animation_->AddKeyframeModel(std::move(first_keyframe_model)); @@ -1760,9 +1790,10 @@ TEST_F(ElementAnimationsTest, SkipUpdateState) { animation_->Tick(kInitialTickTime); animation_->UpdateState(true, events.get()); - std::unique_ptr<KeyframeModel> second_keyframe_model(CreateKeyframeModel( - std::unique_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)), - 2, TargetProperty::OPACITY)); + std::unique_ptr<KeyframeModel> second_keyframe_model( + CreateKeyframeModel(std::unique_ptr<gfx::AnimationCurve>( + new FakeFloatTransition(1.0, 0.f, 1.f)), + 2, TargetProperty::OPACITY)); second_keyframe_model->set_is_controlling_instance_for_test(true); animation_->AddKeyframeModel(std::move(second_keyframe_model)); @@ -1798,9 +1829,10 @@ TEST_F(ElementAnimationsTest, InactiveObserverGetsTicked) { auto events = CreateEventsForTesting(); const int id = 1; - animation_impl_->AddKeyframeModel(CreateKeyframeModel( - std::unique_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.5f, 1.f)), - id, TargetProperty::OPACITY)); + animation_impl_->AddKeyframeModel( + CreateKeyframeModel(std::unique_ptr<gfx::AnimationCurve>( + new FakeFloatTransition(1.0, 0.5f, 1.f)), + id, TargetProperty::OPACITY)); animation_impl_->GetKeyframeModel(TargetProperty::OPACITY) ->set_affects_active_elements(false); @@ -1872,20 +1904,22 @@ TEST_F(ElementAnimationsTest, AbortKeyframeModelsWithProperty) { // Start with several animations, and allow some of them to reach the finished // state. animation_->AddKeyframeModel(KeyframeModel::Create( - std::unique_ptr<AnimationCurve>(new FakeTransformTransition(1.0)), 1, 1, - TargetProperty::TRANSFORM)); + std::unique_ptr<gfx::AnimationCurve>(new FakeTransformTransition(1.0)), 1, + 1, KeyframeModel::TargetPropertyId(TargetProperty::TRANSFORM))); animation_->AddKeyframeModel(KeyframeModel::Create( - std::unique_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)), - 2, 2, TargetProperty::OPACITY)); + std::unique_ptr<gfx::AnimationCurve>( + new FakeFloatTransition(1.0, 0.f, 1.f)), + 2, 2, KeyframeModel::TargetPropertyId(TargetProperty::OPACITY))); animation_->AddKeyframeModel(KeyframeModel::Create( - std::unique_ptr<AnimationCurve>(new FakeTransformTransition(1.0)), 3, 3, - TargetProperty::TRANSFORM)); + std::unique_ptr<gfx::AnimationCurve>(new FakeTransformTransition(1.0)), 3, + 3, KeyframeModel::TargetPropertyId(TargetProperty::TRANSFORM))); animation_->AddKeyframeModel(KeyframeModel::Create( - std::unique_ptr<AnimationCurve>(new FakeTransformTransition(2.0)), 4, 4, - TargetProperty::TRANSFORM)); + std::unique_ptr<gfx::AnimationCurve>(new FakeTransformTransition(2.0)), 4, + 4, KeyframeModel::TargetPropertyId(TargetProperty::TRANSFORM))); animation_->AddKeyframeModel(KeyframeModel::Create( - std::unique_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)), - 5, 5, TargetProperty::OPACITY)); + std::unique_ptr<gfx::AnimationCurve>( + new FakeFloatTransition(1.0, 0.f, 1.f)), + 5, 5, KeyframeModel::TargetPropertyId(TargetProperty::OPACITY))); animation_->Tick(kInitialTickTime); animation_->UpdateState(true, nullptr); @@ -2047,7 +2081,8 @@ TEST_F(ElementAnimationsTest, ImplThreadTakeoverAnimationGetsDeleted) { target_value)); curve->SetInitialValue(initial_value); std::unique_ptr<KeyframeModel> keyframe_model(KeyframeModel::Create( - std::move(curve), keyframe_model_id, 0, TargetProperty::SCROLL_OFFSET)); + std::move(curve), keyframe_model_id, 0, + KeyframeModel::TargetPropertyId(TargetProperty::SCROLL_OFFSET))); keyframe_model->set_start_time(TicksFromSecondsF(123)); keyframe_model->SetIsImplOnly(); animation_impl_->AddKeyframeModel(std::move(keyframe_model)); @@ -2074,9 +2109,9 @@ TEST_F(ElementAnimationsTest, ImplThreadTakeoverAnimationGetsDeleted) { EXPECT_EQ(1u, events->events_.size()); EXPECT_EQ(AnimationEvent::TAKEOVER, events->events_[0].type); EXPECT_EQ(TicksFromSecondsF(123), events->events_[0].animation_start_time); - EXPECT_EQ( - target_value, - events->events_[0].curve->ToScrollOffsetAnimationCurve()->target_value()); + EXPECT_EQ(target_value, static_cast<ScrollOffsetAnimationCurve*>( + events->events_[0].curve.get()) + ->target_value()); EXPECT_EQ(nullptr, animation_impl_->GetKeyframeModel(TargetProperty::SCROLL_OFFSET)); @@ -2112,14 +2147,15 @@ TEST_F(ElementAnimationsTest, FinishedEventsForGroup) { // Add two animations with the same group id but different durations. std::unique_ptr<KeyframeModel> first_keyframe_model(KeyframeModel::Create( - std::unique_ptr<AnimationCurve>(new FakeTransformTransition(2.0)), 1, - group_id, TargetProperty::TRANSFORM)); + std::unique_ptr<gfx::AnimationCurve>(new FakeTransformTransition(2.0)), 1, + group_id, KeyframeModel::TargetPropertyId(TargetProperty::TRANSFORM))); first_keyframe_model->set_is_controlling_instance_for_test(true); animation_impl_->AddKeyframeModel(std::move(first_keyframe_model)); std::unique_ptr<KeyframeModel> second_keyframe_model(KeyframeModel::Create( - std::unique_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)), - 2, group_id, TargetProperty::OPACITY)); + std::unique_ptr<gfx::AnimationCurve>( + new FakeFloatTransition(1.0, 0.f, 1.f)), + 2, group_id, KeyframeModel::TargetPropertyId(TargetProperty::OPACITY))); second_keyframe_model->set_is_controlling_instance_for_test(true); animation_impl_->AddKeyframeModel(std::move(second_keyframe_model)); @@ -2166,14 +2202,15 @@ TEST_F(ElementAnimationsTest, FinishedAndAbortedEventsForGroup) { // Add two animations with the same group id. std::unique_ptr<KeyframeModel> first_keyframe_model(CreateKeyframeModel( - std::unique_ptr<AnimationCurve>(new FakeTransformTransition(1.0)), 1, + std::unique_ptr<gfx::AnimationCurve>(new FakeTransformTransition(1.0)), 1, TargetProperty::TRANSFORM)); first_keyframe_model->set_is_controlling_instance_for_test(true); animation_impl_->AddKeyframeModel(std::move(first_keyframe_model)); - std::unique_ptr<KeyframeModel> second_keyframe_model(CreateKeyframeModel( - std::unique_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)), - 1, TargetProperty::OPACITY)); + std::unique_ptr<KeyframeModel> second_keyframe_model( + CreateKeyframeModel(std::unique_ptr<gfx::AnimationCurve>( + new FakeFloatTransition(1.0, 0.f, 1.f)), + 1, TargetProperty::OPACITY)); second_keyframe_model->set_is_controlling_instance_for_test(true); animation_impl_->AddKeyframeModel(std::move(second_keyframe_model)); @@ -2201,289 +2238,283 @@ TEST_F(ElementAnimationsTest, FinishedAndAbortedEventsForGroup) { EXPECT_EQ(TargetProperty::OPACITY, events->events_[1].target_property); } -TEST_F(ElementAnimationsTest, GetAnimationScalesNotScaled) { +TEST_F(ElementAnimationsTest, MaximumAnimationScaleNotScaled) { CreateTestLayer(true, false); AttachTimelineAnimationLayer(); CreateImplTimelineAndAnimation(); - float max_scale = 999; - float start_scale = 999; - EXPECT_TRUE(animation_impl_->keyframe_effect()->GetAnimationScales( - ElementListType::PENDING, &max_scale, &start_scale)); - EXPECT_EQ(kNotScaled, max_scale); - EXPECT_EQ(kNotScaled, start_scale); - EXPECT_TRUE(animation_impl_->keyframe_effect()->GetAnimationScales( - ElementListType::ACTIVE, &max_scale, &start_scale)); - EXPECT_EQ(kNotScaled, max_scale); - EXPECT_EQ(kNotScaled, start_scale); - - animation_impl_->AddKeyframeModel(CreateKeyframeModel( - std::unique_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)), - 1, TargetProperty::OPACITY)); + EXPECT_EQ(kInvalidScale, + element_animations_impl_->MaximumScale(ElementListType::PENDING)); + EXPECT_EQ(kInvalidScale, + element_animations_impl_->MaximumScale(ElementListType::ACTIVE)); + + animation_impl_->AddKeyframeModel( + CreateKeyframeModel(std::unique_ptr<gfx::AnimationCurve>( + new FakeFloatTransition(1.0, 0.f, 1.f)), + 1, TargetProperty::OPACITY)); // Opacity animations aren't non-translation transforms. - EXPECT_TRUE(animation_impl_->keyframe_effect()->GetAnimationScales( - ElementListType::PENDING, &max_scale, &start_scale)); - EXPECT_EQ(kNotScaled, max_scale); - EXPECT_EQ(kNotScaled, start_scale); - EXPECT_TRUE(animation_impl_->keyframe_effect()->GetAnimationScales( - ElementListType::ACTIVE, &max_scale, &start_scale)); - EXPECT_EQ(kNotScaled, max_scale); - EXPECT_EQ(kNotScaled, start_scale); - - std::unique_ptr<KeyframedTransformAnimationCurve> curve1( - KeyframedTransformAnimationCurve::Create()); - - TransformOperations operations1; + EXPECT_EQ(kInvalidScale, + element_animations_impl_->MaximumScale(ElementListType::PENDING)); + EXPECT_EQ(kInvalidScale, + element_animations_impl_->MaximumScale(ElementListType::ACTIVE)); + + std::unique_ptr<gfx::KeyframedTransformAnimationCurve> curve1( + gfx::KeyframedTransformAnimationCurve::Create()); + + gfx::TransformOperations operations1; curve1->AddKeyframe( - TransformKeyframe::Create(base::TimeDelta(), operations1, nullptr)); + gfx::TransformKeyframe::Create(base::TimeDelta(), operations1, nullptr)); operations1.AppendTranslate(10.0, 15.0, 0.0); - curve1->AddKeyframe(TransformKeyframe::Create( + curve1->AddKeyframe(gfx::TransformKeyframe::Create( base::TimeDelta::FromSecondsD(1.0), operations1, nullptr)); std::unique_ptr<KeyframeModel> keyframe_model(KeyframeModel::Create( - std::move(curve1), 2, 2, TargetProperty::TRANSFORM)); + std::move(curve1), 2, 2, + KeyframeModel::TargetPropertyId(TargetProperty::TRANSFORM))); animation_impl_->AddKeyframeModel(std::move(keyframe_model)); // The only transform animation we've added is a translation. - EXPECT_TRUE(animation_impl_->keyframe_effect()->GetAnimationScales( - ElementListType::PENDING, &max_scale, &start_scale)); - EXPECT_EQ(kNotScaled, max_scale); - EXPECT_EQ(kNotScaled, start_scale); - EXPECT_TRUE(animation_impl_->keyframe_effect()->GetAnimationScales( - ElementListType::ACTIVE, &max_scale, &start_scale)); - EXPECT_EQ(kNotScaled, max_scale); - EXPECT_EQ(kNotScaled, start_scale); + EXPECT_EQ(1.f, + element_animations_impl_->MaximumScale(ElementListType::PENDING)); + EXPECT_EQ(1.f, + element_animations_impl_->MaximumScale(ElementListType::ACTIVE)); } -TEST_F(ElementAnimationsTest, GetAnimationScales) { +TEST_F(ElementAnimationsTest, MaximumAnimationNonCalculatableScale) { CreateTestLayer(true, false); AttachTimelineAnimationLayer(); CreateImplTimelineAndAnimation(); - std::unique_ptr<KeyframedTransformAnimationCurve> curve1( - KeyframedTransformAnimationCurve::Create()); + std::unique_ptr<gfx::KeyframedTransformAnimationCurve> curve1( + gfx::KeyframedTransformAnimationCurve::Create()); + + gfx::TransformOperations operations1; + operations1.AppendScale(2.0, 2.0, 2.0); + operations1.AppendPerspective(100); + curve1->AddKeyframe( + gfx::TransformKeyframe::Create(base::TimeDelta(), operations1, nullptr)); + operations1.AppendTranslate(10.0, 15.0, 0.0); + curve1->AddKeyframe(gfx::TransformKeyframe::Create( + base::TimeDelta::FromSecondsD(1.0), operations1, nullptr)); + + std::unique_ptr<KeyframeModel> keyframe_model(KeyframeModel::Create( + std::move(curve1), 2, 2, + KeyframeModel::TargetPropertyId(TargetProperty::TRANSFORM))); + animation_impl_->AddKeyframeModel(std::move(keyframe_model)); + + // All keyframes have perspective, so the ElementAnimations' scale is not + // calculatable. + EXPECT_EQ(kInvalidScale, + element_animations_impl_->MaximumScale(ElementListType::PENDING)); + EXPECT_EQ(kInvalidScale, + element_animations_impl_->MaximumScale(ElementListType::ACTIVE)); +} - TransformOperations operations1a; +TEST_F(ElementAnimationsTest, MaximumAnimationPartialNonCalculatableScale) { + CreateTestLayer(true, false); + AttachTimelineAnimationLayer(); + CreateImplTimelineAndAnimation(); + + std::unique_ptr<gfx::KeyframedTransformAnimationCurve> curve1( + gfx::KeyframedTransformAnimationCurve::Create()); + + gfx::TransformOperations operations1; + operations1.AppendScale(2.0, 2.0, 2.0); + curve1->AddKeyframe( + gfx::TransformKeyframe::Create(base::TimeDelta(), operations1, nullptr)); + operations1.AppendPerspective(100); + curve1->AddKeyframe(gfx::TransformKeyframe::Create( + base::TimeDelta::FromSecondsD(1.0), operations1, nullptr)); + + std::unique_ptr<KeyframeModel> keyframe_model(KeyframeModel::Create( + std::move(curve1), 2, 2, + KeyframeModel::TargetPropertyId(TargetProperty::TRANSFORM))); + animation_impl_->AddKeyframeModel(std::move(keyframe_model)); + + // Though some keyframes have perspective and the scale is not calculatable, + // we use the other keyframes to calculate the ElementAnimations' scale. + EXPECT_EQ(2.f, + element_animations_impl_->MaximumScale(ElementListType::PENDING)); + EXPECT_EQ(2.f, + element_animations_impl_->MaximumScale(ElementListType::ACTIVE)); +} + +TEST_F(ElementAnimationsTest, MaximumScale) { + CreateTestLayer(true, false); + AttachTimelineAnimationLayer(); + CreateImplTimelineAndAnimation(); + + std::unique_ptr<gfx::KeyframedTransformAnimationCurve> curve1( + gfx::KeyframedTransformAnimationCurve::Create()); + + gfx::TransformOperations operations1a; operations1a.AppendScale(2.0, 3.0, 4.0); curve1->AddKeyframe( - TransformKeyframe::Create(base::TimeDelta(), operations1a, nullptr)); - TransformOperations operations1b; + gfx::TransformKeyframe::Create(base::TimeDelta(), operations1a, nullptr)); + gfx::TransformOperations operations1b; operations1b.AppendScale(5.0, 4.0, 3.0); - curve1->AddKeyframe(TransformKeyframe::Create( + curve1->AddKeyframe(gfx::TransformKeyframe::Create( base::TimeDelta::FromSecondsD(1.0), operations1b, nullptr)); std::unique_ptr<KeyframeModel> keyframe_model(KeyframeModel::Create( - std::move(curve1), 1, 1, TargetProperty::TRANSFORM)); + std::move(curve1), 1, 1, + KeyframeModel::TargetPropertyId(TargetProperty::TRANSFORM))); keyframe_model->set_affects_active_elements(false); animation_impl_->AddKeyframeModel(std::move(keyframe_model)); - float max_scale = kNotScaled; - float start_scale = kNotScaled; - EXPECT_TRUE(animation_impl_->keyframe_effect()->GetAnimationScales( - ElementListType::PENDING, &max_scale, &start_scale)); - EXPECT_EQ(5.f, max_scale); - EXPECT_EQ(4.f, start_scale); - EXPECT_TRUE(animation_impl_->keyframe_effect()->GetAnimationScales( - ElementListType::ACTIVE, &max_scale, &start_scale)); - EXPECT_EQ(kNotScaled, max_scale); - EXPECT_EQ(kNotScaled, start_scale); + EXPECT_EQ(5.f, + element_animations_impl_->MaximumScale(ElementListType::PENDING)); + EXPECT_EQ(kInvalidScale, + element_animations_impl_->MaximumScale(ElementListType::ACTIVE)); animation_impl_->ActivateKeyframeModels(); - EXPECT_TRUE(animation_impl_->keyframe_effect()->GetAnimationScales( - ElementListType::PENDING, &max_scale, &start_scale)); - EXPECT_EQ(5.f, max_scale); - EXPECT_EQ(4.f, start_scale); - EXPECT_TRUE(animation_impl_->keyframe_effect()->GetAnimationScales( - ElementListType::ACTIVE, &max_scale, &start_scale)); - EXPECT_EQ(5.f, max_scale); - EXPECT_EQ(4.f, start_scale); - - std::unique_ptr<KeyframedTransformAnimationCurve> curve2( - KeyframedTransformAnimationCurve::Create()); - - TransformOperations operations2a; + EXPECT_EQ(5.f, + element_animations_impl_->MaximumScale(ElementListType::PENDING)); + EXPECT_EQ(5.f, + element_animations_impl_->MaximumScale(ElementListType::ACTIVE)); + + std::unique_ptr<gfx::KeyframedTransformAnimationCurve> curve2( + gfx::KeyframedTransformAnimationCurve::Create()); + + gfx::TransformOperations operations2a; operations2a.AppendScale(1.0, 2.0, 3.0); curve2->AddKeyframe( - TransformKeyframe::Create(base::TimeDelta(), operations2a, nullptr)); - TransformOperations operations2b; + gfx::TransformKeyframe::Create(base::TimeDelta(), operations2a, nullptr)); + gfx::TransformOperations operations2b; operations2b.AppendScale(6.0, 5.0, 4.0); - curve2->AddKeyframe(TransformKeyframe::Create( + curve2->AddKeyframe(gfx::TransformKeyframe::Create( base::TimeDelta::FromSecondsD(1.0), operations2b, nullptr)); animation_impl_->RemoveKeyframeModel(1); - keyframe_model = - KeyframeModel::Create(std::move(curve2), 2, 2, TargetProperty::TRANSFORM); + keyframe_model = KeyframeModel::Create( + std::move(curve2), 2, 2, + KeyframeModel::TargetPropertyId(TargetProperty::TRANSFORM)); // Reverse Direction keyframe_model->set_direction(KeyframeModel::Direction::REVERSE); keyframe_model->set_affects_active_elements(false); animation_impl_->AddKeyframeModel(std::move(keyframe_model)); - std::unique_ptr<KeyframedTransformAnimationCurve> curve3( - KeyframedTransformAnimationCurve::Create()); + std::unique_ptr<gfx::KeyframedTransformAnimationCurve> curve3( + gfx::KeyframedTransformAnimationCurve::Create()); - TransformOperations operations3a; + gfx::TransformOperations operations3a; operations3a.AppendScale(5.0, 3.0, 1.0); curve3->AddKeyframe( - TransformKeyframe::Create(base::TimeDelta(), operations3a, nullptr)); - TransformOperations operations3b; + gfx::TransformKeyframe::Create(base::TimeDelta(), operations3a, nullptr)); + gfx::TransformOperations operations3b; operations3b.AppendScale(1.5, 2.5, 3.5); - curve3->AddKeyframe(TransformKeyframe::Create( + curve3->AddKeyframe(gfx::TransformKeyframe::Create( base::TimeDelta::FromSecondsD(1.0), operations3b, nullptr)); - keyframe_model = - KeyframeModel::Create(std::move(curve3), 3, 3, TargetProperty::TRANSFORM); + keyframe_model = KeyframeModel::Create( + std::move(curve3), 3, 3, + KeyframeModel::TargetPropertyId(TargetProperty::TRANSFORM)); keyframe_model->set_affects_active_elements(false); animation_impl_->AddKeyframeModel(std::move(keyframe_model)); - EXPECT_TRUE(animation_impl_->keyframe_effect()->GetAnimationScales( - ElementListType::PENDING, &max_scale, &start_scale)); - EXPECT_EQ(3.5f, max_scale); - EXPECT_EQ(6.f, start_scale); - EXPECT_TRUE(animation_impl_->keyframe_effect()->GetAnimationScales( - ElementListType::ACTIVE, &max_scale, &start_scale)); - EXPECT_EQ(kNotScaled, max_scale); - EXPECT_EQ(kNotScaled, start_scale); + EXPECT_EQ(6.f, + element_animations_impl_->MaximumScale(ElementListType::PENDING)); + EXPECT_EQ(kInvalidScale, + element_animations_impl_->MaximumScale(ElementListType::ACTIVE)); animation_impl_->ActivateKeyframeModels(); - EXPECT_TRUE(animation_impl_->keyframe_effect()->GetAnimationScales( - ElementListType::PENDING, &max_scale, &start_scale)); - EXPECT_EQ(3.5f, max_scale); - EXPECT_EQ(6.f, start_scale); - EXPECT_TRUE(animation_impl_->keyframe_effect()->GetAnimationScales( - ElementListType::ACTIVE, &max_scale, &start_scale)); - EXPECT_EQ(3.5f, max_scale); - EXPECT_EQ(6.f, start_scale); + EXPECT_EQ(6.f, + element_animations_impl_->MaximumScale(ElementListType::PENDING)); + EXPECT_EQ(6.f, + element_animations_impl_->MaximumScale(ElementListType::ACTIVE)); animation_impl_->keyframe_effect()->GetKeyframeModelById(2)->SetRunState( KeyframeModel::FINISHED, TicksFromSecondsF(0.0)); - // Only unfinished animations should be considered by GetAnimationScales. - EXPECT_TRUE(animation_impl_->keyframe_effect()->GetAnimationScales( - ElementListType::PENDING, &max_scale, &start_scale)); - EXPECT_EQ(3.5f, max_scale); - EXPECT_EQ(5.f, start_scale); - EXPECT_TRUE(animation_impl_->keyframe_effect()->GetAnimationScales( - ElementListType::ACTIVE, &max_scale, &start_scale)); - EXPECT_EQ(3.5f, max_scale); - EXPECT_EQ(5.f, start_scale); + // Only unfinished animations should be considered by MaximumAnimationScale. + EXPECT_EQ(5.f, + element_animations_impl_->MaximumScale(ElementListType::PENDING)); + EXPECT_EQ(5.f, + element_animations_impl_->MaximumScale(ElementListType::ACTIVE)); } -TEST_F(ElementAnimationsTest, GetAnimationScalesWithDirection) { +TEST_F(ElementAnimationsTest, MaximumAnimationScaleWithDirection) { CreateTestLayer(true, false); AttachTimelineAnimationLayer(); CreateImplTimelineAndAnimation(); - std::unique_ptr<KeyframedTransformAnimationCurve> curve1( - KeyframedTransformAnimationCurve::Create()); - TransformOperations operations1; + std::unique_ptr<gfx::KeyframedTransformAnimationCurve> curve1( + gfx::KeyframedTransformAnimationCurve::Create()); + gfx::TransformOperations operations1; operations1.AppendScale(1.0, 2.0, 3.0); curve1->AddKeyframe( - TransformKeyframe::Create(base::TimeDelta(), operations1, nullptr)); - TransformOperations operations2; + gfx::TransformKeyframe::Create(base::TimeDelta(), operations1, nullptr)); + gfx::TransformOperations operations2; operations2.AppendScale(4.0, 5.0, 6.0); - curve1->AddKeyframe(TransformKeyframe::Create( + curve1->AddKeyframe(gfx::TransformKeyframe::Create( base::TimeDelta::FromSecondsD(1.0), operations2, nullptr)); std::unique_ptr<KeyframeModel> keyframe_model_owned(KeyframeModel::Create( - std::move(curve1), 1, 1, TargetProperty::TRANSFORM)); + std::move(curve1), 1, 1, + KeyframeModel::TargetPropertyId(TargetProperty::TRANSFORM))); KeyframeModel* keyframe_model = keyframe_model_owned.get(); animation_impl_->AddKeyframeModel(std::move(keyframe_model_owned)); - float max_scale = 999; - float start_scale = 999; - EXPECT_GT(keyframe_model->playback_rate(), 0.0); // NORMAL direction with positive playback rate. keyframe_model->set_direction(KeyframeModel::Direction::NORMAL); - EXPECT_TRUE(animation_impl_->keyframe_effect()->GetAnimationScales( - ElementListType::PENDING, &max_scale, &start_scale)); - EXPECT_EQ(6.f, max_scale); - EXPECT_EQ(3.f, start_scale); - EXPECT_TRUE(animation_impl_->keyframe_effect()->GetAnimationScales( - ElementListType::ACTIVE, &max_scale, &start_scale)); - EXPECT_EQ(6.f, max_scale); - EXPECT_EQ(3.f, start_scale); + EXPECT_EQ(6.f, + element_animations_impl_->MaximumScale(ElementListType::PENDING)); + EXPECT_EQ(6.f, + element_animations_impl_->MaximumScale(ElementListType::ACTIVE)); // ALTERNATE direction with positive playback rate. keyframe_model->set_direction(KeyframeModel::Direction::ALTERNATE_NORMAL); - EXPECT_TRUE(animation_impl_->keyframe_effect()->GetAnimationScales( - ElementListType::PENDING, &max_scale, &start_scale)); - EXPECT_EQ(6.f, max_scale); - EXPECT_EQ(3.f, start_scale); - EXPECT_TRUE(animation_impl_->keyframe_effect()->GetAnimationScales( - ElementListType::ACTIVE, &max_scale, &start_scale)); - EXPECT_EQ(6.f, max_scale); - EXPECT_EQ(3.f, start_scale); + EXPECT_EQ(6.f, + element_animations_impl_->MaximumScale(ElementListType::PENDING)); + EXPECT_EQ(6.f, + element_animations_impl_->MaximumScale(ElementListType::ACTIVE)); // REVERSE direction with positive playback rate. keyframe_model->set_direction(KeyframeModel::Direction::REVERSE); - EXPECT_TRUE(animation_impl_->keyframe_effect()->GetAnimationScales( - ElementListType::PENDING, &max_scale, &start_scale)); - EXPECT_EQ(3.f, max_scale); - EXPECT_EQ(6.f, start_scale); - EXPECT_TRUE(animation_impl_->keyframe_effect()->GetAnimationScales( - ElementListType::ACTIVE, &max_scale, &start_scale)); - EXPECT_EQ(3.f, max_scale); - EXPECT_EQ(6.f, start_scale); + EXPECT_EQ(6.f, + element_animations_impl_->MaximumScale(ElementListType::PENDING)); + EXPECT_EQ(6.f, + element_animations_impl_->MaximumScale(ElementListType::ACTIVE)); // ALTERNATE reverse direction. keyframe_model->set_direction(KeyframeModel::Direction::REVERSE); - EXPECT_TRUE(animation_impl_->keyframe_effect()->GetAnimationScales( - ElementListType::PENDING, &max_scale, &start_scale)); - EXPECT_EQ(3.f, max_scale); - EXPECT_EQ(6.f, start_scale); - EXPECT_TRUE(animation_impl_->keyframe_effect()->GetAnimationScales( - ElementListType::ACTIVE, &max_scale, &start_scale)); - EXPECT_EQ(3.f, max_scale); - EXPECT_EQ(6.f, start_scale); + EXPECT_EQ(6.f, + element_animations_impl_->MaximumScale(ElementListType::PENDING)); + EXPECT_EQ(6.f, + element_animations_impl_->MaximumScale(ElementListType::ACTIVE)); keyframe_model->set_playback_rate(-1.0); // NORMAL direction with negative playback rate. keyframe_model->set_direction(KeyframeModel::Direction::NORMAL); - EXPECT_TRUE(animation_impl_->keyframe_effect()->GetAnimationScales( - ElementListType::PENDING, &max_scale, &start_scale)); - EXPECT_EQ(3.f, max_scale); - EXPECT_EQ(6.f, start_scale); - EXPECT_TRUE(animation_impl_->keyframe_effect()->GetAnimationScales( - ElementListType::ACTIVE, &max_scale, &start_scale)); - EXPECT_EQ(3.f, max_scale); - EXPECT_EQ(6.f, start_scale); + EXPECT_EQ(6.f, + element_animations_impl_->MaximumScale(ElementListType::PENDING)); + EXPECT_EQ(6.f, + element_animations_impl_->MaximumScale(ElementListType::ACTIVE)); // ALTERNATE direction with negative playback rate. keyframe_model->set_direction(KeyframeModel::Direction::ALTERNATE_NORMAL); - EXPECT_TRUE(animation_impl_->keyframe_effect()->GetAnimationScales( - ElementListType::PENDING, &max_scale, &start_scale)); - EXPECT_EQ(3.f, max_scale); - EXPECT_EQ(6.f, start_scale); - EXPECT_TRUE(animation_impl_->keyframe_effect()->GetAnimationScales( - ElementListType::ACTIVE, &max_scale, &start_scale)); - EXPECT_EQ(3.f, max_scale); - EXPECT_EQ(6.f, start_scale); + EXPECT_EQ(6.f, + element_animations_impl_->MaximumScale(ElementListType::PENDING)); + EXPECT_EQ(6.f, + element_animations_impl_->MaximumScale(ElementListType::ACTIVE)); // REVERSE direction with negative playback rate. keyframe_model->set_direction(KeyframeModel::Direction::REVERSE); - EXPECT_TRUE(animation_impl_->keyframe_effect()->GetAnimationScales( - ElementListType::PENDING, &max_scale, &start_scale)); - EXPECT_EQ(6.f, max_scale); - EXPECT_EQ(3.f, start_scale); - EXPECT_TRUE(animation_impl_->keyframe_effect()->GetAnimationScales( - ElementListType::ACTIVE, &max_scale, &start_scale)); - EXPECT_EQ(6.f, max_scale); - EXPECT_EQ(3.f, start_scale); + EXPECT_EQ(6.f, + element_animations_impl_->MaximumScale(ElementListType::PENDING)); + EXPECT_EQ(6.f, + element_animations_impl_->MaximumScale(ElementListType::ACTIVE)); // ALTERNATE reverse direction with negative playback rate. keyframe_model->set_direction(KeyframeModel::Direction::REVERSE); - EXPECT_TRUE(animation_impl_->keyframe_effect()->GetAnimationScales( - ElementListType::PENDING, &max_scale, &start_scale)); - EXPECT_EQ(6.f, max_scale); - EXPECT_EQ(3.f, start_scale); - EXPECT_TRUE(animation_impl_->keyframe_effect()->GetAnimationScales( - ElementListType::ACTIVE, &max_scale, &start_scale)); - EXPECT_EQ(6.f, max_scale); - EXPECT_EQ(3.f, start_scale); + EXPECT_EQ(6.f, + element_animations_impl_->MaximumScale(ElementListType::PENDING)); + EXPECT_EQ(6.f, + element_animations_impl_->MaximumScale(ElementListType::ACTIVE)); } TEST_F(ElementAnimationsTest, NewlyPushedAnimationWaitsForActivation) { @@ -3689,9 +3720,10 @@ TEST_F(ElementAnimationsTest, TestIsCurrentlyAnimatingProperty) { AttachTimelineAnimationLayer(); // Create an animation that initially affects only pending elements. - std::unique_ptr<KeyframeModel> keyframe_model(CreateKeyframeModel( - std::unique_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)), - 1, TargetProperty::OPACITY)); + std::unique_ptr<KeyframeModel> keyframe_model( + CreateKeyframeModel(std::unique_ptr<gfx::AnimationCurve>( + new FakeFloatTransition(1.0, 0.f, 1.f)), + 1, TargetProperty::OPACITY)); keyframe_model->set_affects_active_elements(false); animation_->AddKeyframeModel(std::move(keyframe_model)); @@ -3759,9 +3791,10 @@ TEST_F(ElementAnimationsTest, TestIsAnimatingPropertyTimeOffsetFillMode) { // Create an animation that initially affects only pending elements, and has // a start delay of 2 seconds. - std::unique_ptr<KeyframeModel> keyframe_model(CreateKeyframeModel( - std::unique_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)), - 1, TargetProperty::OPACITY)); + std::unique_ptr<KeyframeModel> keyframe_model( + CreateKeyframeModel(std::unique_ptr<gfx::AnimationCurve>( + new FakeFloatTransition(1.0, 0.f, 1.f)), + 1, TargetProperty::OPACITY)); keyframe_model->set_fill_mode(KeyframeModel::FillMode::NONE); keyframe_model->set_time_offset(TimeDelta::FromMilliseconds(-2000)); keyframe_model->set_affects_active_elements(false); @@ -3837,9 +3870,10 @@ TEST_F(ElementAnimationsTest, DestroyTestMainLayerBeforePushProperties) { AttachTimelineAnimationLayer(); EXPECT_EQ(0u, host_->ticking_animations_for_testing().size()); - animation_->AddKeyframeModel(CreateKeyframeModel( - std::unique_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 1.f, 0.5f)), - 2, TargetProperty::OPACITY)); + animation_->AddKeyframeModel( + CreateKeyframeModel(std::unique_ptr<gfx::AnimationCurve>( + new FakeFloatTransition(1.0, 1.f, 0.5f)), + 2, TargetProperty::OPACITY)); EXPECT_EQ(1u, host_->ticking_animations_for_testing().size()); DestroyTestMainLayer(); @@ -3859,18 +3893,20 @@ TEST_F(ElementAnimationsTest, RemoveAndReAddAnimationToTicking) { // Add an animation and ensure the animation is in the host's ticking // animations. Remove the animation using RemoveFromTicking(). - animation_->AddKeyframeModel(CreateKeyframeModel( - std::unique_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 1.f, 0.5f)), - 1, TargetProperty::OPACITY)); + animation_->AddKeyframeModel( + CreateKeyframeModel(std::unique_ptr<gfx::AnimationCurve>( + new FakeFloatTransition(1.0, 1.f, 0.5f)), + 1, TargetProperty::OPACITY)); ASSERT_EQ(1u, host_->ticking_animations_for_testing().size()); animation_->keyframe_effect()->RemoveFromTicking(); ASSERT_EQ(0u, host_->ticking_animations_for_testing().size()); // Ensure that adding a new animation will correctly update the ticking // animations list. - animation_->AddKeyframeModel(CreateKeyframeModel( - std::unique_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 1.f, 0.5f)), - 2, TargetProperty::OPACITY)); + animation_->AddKeyframeModel( + CreateKeyframeModel(std::unique_ptr<gfx::AnimationCurve>( + new FakeFloatTransition(1.0, 1.f, 0.5f)), + 2, TargetProperty::OPACITY)); EXPECT_EQ(1u, host_->ticking_animations_for_testing().size()); } @@ -3882,11 +3918,12 @@ TEST_F(ElementAnimationsTest, FinishedKeyframeModelsNotCopiedToImpl) { CreateImplTimelineAndAnimation(); animation_->AddKeyframeModel(KeyframeModel::Create( - std::unique_ptr<AnimationCurve>(new FakeTransformTransition(1.0)), 1, 1, - TargetProperty::TRANSFORM)); + std::unique_ptr<gfx::AnimationCurve>(new FakeTransformTransition(1.0)), 1, + 1, KeyframeModel::TargetPropertyId(TargetProperty::TRANSFORM))); animation_->AddKeyframeModel(KeyframeModel::Create( - std::unique_ptr<AnimationCurve>(new FakeFloatTransition(2.0, 0.f, 1.f)), - 2, 2, TargetProperty::OPACITY)); + std::unique_ptr<gfx::AnimationCurve>( + new FakeFloatTransition(2.0, 0.f, 1.f)), + 2, 2, KeyframeModel::TargetPropertyId(TargetProperty::OPACITY))); // Finish the first keyframe model. animation_->Tick(kInitialTickTime); @@ -3908,5 +3945,30 @@ TEST_F(ElementAnimationsTest, FinishedKeyframeModelsNotCopiedToImpl) { EXPECT_TRUE(animation_impl_->keyframe_effect()->GetKeyframeModelById(2)); } +TEST_F(ElementAnimationsTest, ClientAnimationState) { + client_.RegisterElementId(element_id_, ElementListType::ACTIVE); + AttachTimelineAnimationLayer(); + CreateImplTimelineAndAnimation(); + + animation_->AddKeyframeModel(KeyframeModel::Create( + std::make_unique<FakeTransformTransition>(1.0), 1, 1, + KeyframeModel::TargetPropertyId(TargetProperty::TRANSFORM))); + + auto* layer = client_.FindTestLayer(element_id_, ElementListType::ACTIVE); + EXPECT_TRUE(layer->is_currently_animating(TargetProperty::TRANSFORM)); + EXPECT_EQ(1.f, layer->maximum_animation_scale()); + + // The client resets the cached data, simulating a property rebuild or a + // property push with different values. + layer->set_is_currently_animating(TargetProperty::TRANSFORM, false); + layer->set_maximum_animation_scale(kInvalidScale); + + // The client should call this function which should refresh all data of the + // client. + element_animations_->InitClientAnimationState(); + EXPECT_TRUE(layer->is_currently_animating(TargetProperty::TRANSFORM)); + EXPECT_EQ(1.f, layer->maximum_animation_scale()); +} + } // namespace } // namespace cc diff --git a/chromium/cc/animation/filter_animation_curve.cc b/chromium/cc/animation/filter_animation_curve.cc new file mode 100644 index 00000000000..83d991c0662 --- /dev/null +++ b/chromium/cc/animation/filter_animation_curve.cc @@ -0,0 +1,206 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/animation/filter_animation_curve.h" + +#include "base/memory/ptr_util.h" + +namespace cc { + +// TODO(crbug.com/747185): All code in this namespace duplicates code from +// ui/gfx/keyframe/animation/ unnecessarily. +namespace { + +template <class KeyframeType> +void InsertKeyframe(std::unique_ptr<KeyframeType> keyframe, + std::vector<std::unique_ptr<KeyframeType>>* keyframes) { + // Usually, the keyframes will be added in order, so this loop would be + // unnecessary and we should skip it if possible. + if (!keyframes->empty() && keyframe->Time() < keyframes->back()->Time()) { + for (size_t i = 0; i < keyframes->size(); ++i) { + if (keyframe->Time() < keyframes->at(i)->Time()) { + keyframes->insert(keyframes->begin() + i, std::move(keyframe)); + return; + } + } + } + + keyframes->push_back(std::move(keyframe)); +} + +struct TimeValues { + base::TimeDelta start_time; + base::TimeDelta duration; + double progress; +}; + +template <typename KeyframeType> +TimeValues GetTimeValues(const KeyframeType& start_frame, + const KeyframeType& end_frame, + double scaled_duration, + base::TimeDelta time) { + TimeValues values; + values.start_time = start_frame.Time() * scaled_duration; + values.duration = (end_frame.Time() * scaled_duration) - values.start_time; + const base::TimeDelta elapsed = time - values.start_time; + values.progress = (elapsed.is_inf() || values.duration.is_zero()) + ? 1.0 + : (elapsed / values.duration); + return values; +} + +template <typename KeyframeType> +base::TimeDelta TransformedAnimationTime( + const std::vector<std::unique_ptr<KeyframeType>>& keyframes, + const std::unique_ptr<gfx::TimingFunction>& timing_function, + double scaled_duration, + base::TimeDelta time) { + if (timing_function) { + const auto values = GetTimeValues(*keyframes.front(), *keyframes.back(), + scaled_duration, time); + time = (values.duration * timing_function->GetValue(values.progress)) + + values.start_time; + } + + return time; +} + +template <typename KeyframeType> +size_t GetActiveKeyframe( + const std::vector<std::unique_ptr<KeyframeType>>& keyframes, + double scaled_duration, + base::TimeDelta time) { + DCHECK_GE(keyframes.size(), 2ul); + size_t i = 0; + while ((i < keyframes.size() - 2) && // Last keyframe is never active. + (time >= (keyframes[i + 1]->Time() * scaled_duration))) + ++i; + + return i; +} + +template <typename KeyframeType> +double TransformedKeyframeProgress( + const std::vector<std::unique_ptr<KeyframeType>>& keyframes, + double scaled_duration, + base::TimeDelta time, + size_t i) { + const double progress = + GetTimeValues(*keyframes[i], *keyframes[i + 1], scaled_duration, time) + .progress; + return keyframes[i]->timing_function() + ? keyframes[i]->timing_function()->GetValue(progress) + : progress; +} + +} // namespace + +void FilterAnimationCurve::Tick(base::TimeDelta t, + int property_id, + gfx::KeyframeModel* keyframe_model) const { + if (target_) { + target_->OnFilterAnimated(GetValue(t), property_id, keyframe_model); + } +} + +int FilterAnimationCurve::Type() const { + return gfx::AnimationCurve::FILTER; +} + +const char* FilterAnimationCurve::TypeName() const { + return "Filter"; +} + +const FilterAnimationCurve* FilterAnimationCurve::ToFilterAnimationCurve( + const gfx::AnimationCurve* c) { + DCHECK_EQ(gfx::AnimationCurve::FILTER, c->Type()); + return static_cast<const FilterAnimationCurve*>(c); +} + +FilterAnimationCurve* FilterAnimationCurve::ToFilterAnimationCurve( + gfx::AnimationCurve* c) { + DCHECK_EQ(AnimationCurve::FILTER, c->Type()); + return static_cast<FilterAnimationCurve*>(c); +} + +std::unique_ptr<FilterKeyframe> FilterKeyframe::Create( + base::TimeDelta time, + const FilterOperations& value, + std::unique_ptr<gfx::TimingFunction> timing_function) { + return base::WrapUnique( + new FilterKeyframe(time, value, std::move(timing_function))); +} + +FilterKeyframe::FilterKeyframe( + base::TimeDelta time, + const FilterOperations& value, + std::unique_ptr<gfx::TimingFunction> timing_function) + : Keyframe(time, std::move(timing_function)), value_(value) {} + +FilterKeyframe::~FilterKeyframe() = default; + +const FilterOperations& FilterKeyframe::Value() const { + return value_; +} + +std::unique_ptr<FilterKeyframe> FilterKeyframe::Clone() const { + std::unique_ptr<gfx::TimingFunction> func; + if (timing_function()) + func = timing_function()->Clone(); + return FilterKeyframe::Create(Time(), Value(), std::move(func)); +} + +void KeyframedFilterAnimationCurve::AddKeyframe( + std::unique_ptr<FilterKeyframe> keyframe) { + InsertKeyframe(std::move(keyframe), &keyframes_); +} + +base::TimeDelta KeyframedFilterAnimationCurve::Duration() const { + return (keyframes_.back()->Time() - keyframes_.front()->Time()) * + scaled_duration(); +} + +std::unique_ptr<gfx::AnimationCurve> KeyframedFilterAnimationCurve::Clone() + const { + std::unique_ptr<KeyframedFilterAnimationCurve> to_return = + KeyframedFilterAnimationCurve::Create(); + for (const auto& keyframe : keyframes_) + to_return->AddKeyframe(keyframe->Clone()); + + if (timing_function_) + to_return->SetTimingFunction(timing_function_->Clone()); + + to_return->set_scaled_duration(scaled_duration()); + + return std::move(to_return); +} + +FilterOperations KeyframedFilterAnimationCurve::GetValue( + base::TimeDelta t) const { + if (t <= (keyframes_.front()->Time() * scaled_duration())) + return keyframes_.front()->Value(); + + if (t >= (keyframes_.back()->Time() * scaled_duration())) + return keyframes_.back()->Value(); + + t = TransformedAnimationTime(keyframes_, timing_function_, scaled_duration(), + t); + size_t i = GetActiveKeyframe(keyframes_, scaled_duration(), t); + double progress = + TransformedKeyframeProgress(keyframes_, scaled_duration(), t, i); + + return keyframes_[i + 1]->Value().Blend(keyframes_[i]->Value(), progress); +} + +std::unique_ptr<KeyframedFilterAnimationCurve> +KeyframedFilterAnimationCurve::Create() { + return base::WrapUnique(new KeyframedFilterAnimationCurve); +} + +KeyframedFilterAnimationCurve::KeyframedFilterAnimationCurve() + : scaled_duration_(1.0) {} + +KeyframedFilterAnimationCurve::~KeyframedFilterAnimationCurve() = default; + +} // namespace cc diff --git a/chromium/cc/animation/filter_animation_curve.h b/chromium/cc/animation/filter_animation_curve.h new file mode 100644 index 00000000000..0455d67df19 --- /dev/null +++ b/chromium/cc/animation/filter_animation_curve.h @@ -0,0 +1,83 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CC_ANIMATION_FILTER_ANIMATION_CURVE_H_ +#define CC_ANIMATION_FILTER_ANIMATION_CURVE_H_ + +#include <memory> +#include <utility> +#include <vector> + +#include "ui/gfx/animation/keyframe/keyframed_animation_curve.h" + +#include "cc/animation/animation_export.h" +#include "cc/paint/filter_operations.h" + +namespace cc { + +class CC_ANIMATION_EXPORT FilterAnimationCurve : public gfx::AnimationCurve { + DECLARE_ANIMATION_CURVE_BODY(FilterOperations, Filter) +}; + +class CC_ANIMATION_EXPORT FilterKeyframe : public gfx::Keyframe { + public: + static std::unique_ptr<FilterKeyframe> Create( + base::TimeDelta time, + const FilterOperations& value, + std::unique_ptr<gfx::TimingFunction> timing_function); + ~FilterKeyframe() override; + + const FilterOperations& Value() const; + + std::unique_ptr<FilterKeyframe> Clone() const; + + private: + FilterKeyframe(base::TimeDelta time, + const FilterOperations& value, + std::unique_ptr<gfx::TimingFunction> timing_function); + + FilterOperations value_; +}; + +class CC_ANIMATION_EXPORT KeyframedFilterAnimationCurve + : public FilterAnimationCurve { + public: + // It is required that the keyframes be sorted by time. + static std::unique_ptr<KeyframedFilterAnimationCurve> Create(); + + KeyframedFilterAnimationCurve(const KeyframedFilterAnimationCurve&) = delete; + ~KeyframedFilterAnimationCurve() override; + + KeyframedFilterAnimationCurve& operator=( + const KeyframedFilterAnimationCurve&) = delete; + + void AddKeyframe(std::unique_ptr<FilterKeyframe> keyframe); + void SetTimingFunction(std::unique_ptr<gfx::TimingFunction> timing_function) { + timing_function_ = std::move(timing_function); + } + double scaled_duration() const { return scaled_duration_; } + void set_scaled_duration(double scaled_duration) { + scaled_duration_ = scaled_duration; + } + + // AnimationCurve implementation + base::TimeDelta Duration() const override; + std::unique_ptr<gfx::AnimationCurve> Clone() const override; + + // FilterAnimationCurve implementation + FilterOperations GetValue(base::TimeDelta t) const override; + + private: + KeyframedFilterAnimationCurve(); + + // Always sorted in order of increasing time. No two keyframes have the + // same time. + std::vector<std::unique_ptr<FilterKeyframe>> keyframes_; + std::unique_ptr<gfx::TimingFunction> timing_function_; + double scaled_duration_; +}; + +} // namespace cc + +#endif // CC_ANIMATION_FILTER_ANIMATION_CURVE_H_ diff --git a/chromium/cc/animation/filter_animation_curve_unittest.cc b/chromium/cc/animation/filter_animation_curve_unittest.cc new file mode 100644 index 00000000000..e7401a78a11 --- /dev/null +++ b/chromium/cc/animation/filter_animation_curve_unittest.cc @@ -0,0 +1,127 @@ +// Copyright 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/animation/filter_animation_curve.h" + +#include <memory> + +#include "cc/test/geometry_test_utils.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/gfx/animation/tween.h" +#include "ui/gfx/geometry/box_f.h" +#include "ui/gfx/test/gfx_util.h" +#include "ui/gfx/transform_operations.h" + +namespace cc { +namespace { + +void ExpectBrightness(double brightness, const FilterOperations& filter) { + EXPECT_EQ(1u, filter.size()); + EXPECT_EQ(FilterOperation::BRIGHTNESS, filter.at(0).type()); + EXPECT_FLOAT_EQ(brightness, filter.at(0).amount()); +} + +// Tests that a filter animation with one keyframe works as expected. +TEST(KeyframedAnimationCurveTest, OneFilterKeyframe) { + std::unique_ptr<KeyframedFilterAnimationCurve> curve( + KeyframedFilterAnimationCurve::Create()); + FilterOperations operations; + operations.Append(FilterOperation::CreateBrightnessFilter(2.f)); + curve->AddKeyframe( + FilterKeyframe::Create(base::TimeDelta(), operations, nullptr)); + + ExpectBrightness(2.f, curve->GetValue(base::TimeDelta::FromSecondsD(-1.f))); + ExpectBrightness(2.f, curve->GetValue(base::TimeDelta::FromSecondsD(0.f))); + ExpectBrightness(2.f, curve->GetValue(base::TimeDelta::FromSecondsD(0.5f))); + ExpectBrightness(2.f, curve->GetValue(base::TimeDelta::FromSecondsD(1.f))); + ExpectBrightness(2.f, curve->GetValue(base::TimeDelta::FromSecondsD(2.f))); +} + +// Tests that a filter animation with two keyframes works as expected. +TEST(KeyframedAnimationCurveTest, TwoFilterKeyframe) { + std::unique_ptr<KeyframedFilterAnimationCurve> curve( + KeyframedFilterAnimationCurve::Create()); + FilterOperations operations1; + operations1.Append(FilterOperation::CreateBrightnessFilter(2.f)); + FilterOperations operations2; + operations2.Append(FilterOperation::CreateBrightnessFilter(4.f)); + + curve->AddKeyframe( + FilterKeyframe::Create(base::TimeDelta(), operations1, nullptr)); + curve->AddKeyframe(FilterKeyframe::Create(base::TimeDelta::FromSecondsD(1.f), + operations2, nullptr)); + ExpectBrightness(2.f, curve->GetValue(base::TimeDelta::FromSecondsD(-1.f))); + ExpectBrightness(2.f, curve->GetValue(base::TimeDelta::FromSecondsD(0.f))); + ExpectBrightness(3.f, curve->GetValue(base::TimeDelta::FromSecondsD(0.5f))); + ExpectBrightness(4.f, curve->GetValue(base::TimeDelta::FromSecondsD(1.f))); + ExpectBrightness(4.f, curve->GetValue(base::TimeDelta::FromSecondsD(2.f))); +} + +// Tests that a filter animation with three keyframes works as expected. +TEST(KeyframedAnimationCurveTest, ThreeFilterKeyframe) { + std::unique_ptr<KeyframedFilterAnimationCurve> curve( + KeyframedFilterAnimationCurve::Create()); + FilterOperations operations1; + operations1.Append(FilterOperation::CreateBrightnessFilter(2.f)); + FilterOperations operations2; + operations2.Append(FilterOperation::CreateBrightnessFilter(4.f)); + FilterOperations operations3; + operations3.Append(FilterOperation::CreateBrightnessFilter(8.f)); + curve->AddKeyframe( + FilterKeyframe::Create(base::TimeDelta(), operations1, nullptr)); + curve->AddKeyframe(FilterKeyframe::Create(base::TimeDelta::FromSecondsD(1.f), + operations2, nullptr)); + curve->AddKeyframe(FilterKeyframe::Create(base::TimeDelta::FromSecondsD(2.f), + operations3, nullptr)); + ExpectBrightness(2.f, curve->GetValue(base::TimeDelta::FromSecondsD(-1.f))); + ExpectBrightness(2.f, curve->GetValue(base::TimeDelta::FromSecondsD(0.f))); + ExpectBrightness(3.f, curve->GetValue(base::TimeDelta::FromSecondsD(0.5f))); + ExpectBrightness(4.f, curve->GetValue(base::TimeDelta::FromSecondsD(1.f))); + ExpectBrightness(6.f, curve->GetValue(base::TimeDelta::FromSecondsD(1.5f))); + ExpectBrightness(8.f, curve->GetValue(base::TimeDelta::FromSecondsD(2.f))); + ExpectBrightness(8.f, curve->GetValue(base::TimeDelta::FromSecondsD(3.f))); +} + +// Tests that a filter animation with multiple keys at a given time works +// sanely. +TEST(KeyframedAnimationCurveTest, RepeatedFilterKeyTimes) { + std::unique_ptr<KeyframedFilterAnimationCurve> curve( + KeyframedFilterAnimationCurve::Create()); + // A step function. + FilterOperations operations1; + operations1.Append(FilterOperation::CreateBrightnessFilter(4.f)); + FilterOperations operations2; + operations2.Append(FilterOperation::CreateBrightnessFilter(4.f)); + FilterOperations operations3; + operations3.Append(FilterOperation::CreateBrightnessFilter(6.f)); + FilterOperations operations4; + operations4.Append(FilterOperation::CreateBrightnessFilter(6.f)); + curve->AddKeyframe( + FilterKeyframe::Create(base::TimeDelta(), operations1, nullptr)); + curve->AddKeyframe(FilterKeyframe::Create(base::TimeDelta::FromSecondsD(1.f), + operations2, nullptr)); + curve->AddKeyframe(FilterKeyframe::Create(base::TimeDelta::FromSecondsD(1.f), + operations3, nullptr)); + curve->AddKeyframe(FilterKeyframe::Create(base::TimeDelta::FromSecondsD(2.f), + operations4, nullptr)); + + ExpectBrightness(4.f, curve->GetValue(base::TimeDelta::FromSecondsD(-1.f))); + ExpectBrightness(4.f, curve->GetValue(base::TimeDelta::FromSecondsD(0.f))); + ExpectBrightness(4.f, curve->GetValue(base::TimeDelta::FromSecondsD(0.5f))); + + // There is a discontinuity at 1. Any value between 4 and 6 is valid. + FilterOperations value = curve->GetValue(base::TimeDelta::FromSecondsD(1.f)); + EXPECT_EQ(1u, value.size()); + EXPECT_EQ(FilterOperation::BRIGHTNESS, value.at(0).type()); + EXPECT_GE(value.at(0).amount(), 4); + EXPECT_LE(value.at(0).amount(), 6); + + ExpectBrightness(6.f, curve->GetValue(base::TimeDelta::FromSecondsD(1.5f))); + ExpectBrightness(6.f, curve->GetValue(base::TimeDelta::FromSecondsD(2.f))); + ExpectBrightness(6.f, curve->GetValue(base::TimeDelta::FromSecondsD(3.f))); +} + +} // namespace +} // namespace cc diff --git a/chromium/cc/animation/keyframe_effect.cc b/chromium/cc/animation/keyframe_effect.cc index a6d70065b15..ef4ec75cb45 100644 --- a/chromium/cc/animation/keyframe_effect.cc +++ b/chromium/cc/animation/keyframe_effect.cc @@ -4,18 +4,21 @@ #include "cc/animation/keyframe_effect.h" +#include <algorithm> #include <memory> +#include <string> +#include <utility> #include "base/stl_util.h" #include "base/time/time.h" #include "cc/animation/animation.h" -#include "cc/animation/animation_curve.h" #include "cc/animation/animation_host.h" #include "cc/animation/animation_timeline.h" -#include "cc/animation/keyframe_model.h" #include "cc/animation/scroll_offset_animation_curve.h" -#include "cc/animation/transform_operations.h" #include "cc/trees/property_animation_state.h" +#include "ui/gfx//transform_operations.h" +#include "ui/gfx/animation/keyframe/animation_curve.h" +#include "ui/gfx/animation/keyframe/target_property.h" namespace cc { @@ -108,8 +111,7 @@ void KeyframeEffect::Tick(base::TimeTicks monotonic_time) { StartKeyframeModels(monotonic_time); for (auto& keyframe_model : keyframe_models_) { - TickKeyframeModel(monotonic_time, keyframe_model.get(), - element_animations_.get()); + TickKeyframeModel(monotonic_time, keyframe_model.get()); } last_tick_time_ = monotonic_time; @@ -117,51 +119,18 @@ void KeyframeEffect::Tick(base::TimeTicks monotonic_time) { } void KeyframeEffect::TickKeyframeModel(base::TimeTicks monotonic_time, - KeyframeModel* keyframe_model, - AnimationTarget* target) { - if ((keyframe_model->run_state() != KeyframeModel::STARTING && - keyframe_model->run_state() != KeyframeModel::RUNNING && - keyframe_model->run_state() != KeyframeModel::PAUSED) || + KeyframeModel* keyframe_model) { + if ((keyframe_model->run_state() != gfx::KeyframeModel::STARTING && + keyframe_model->run_state() != gfx::KeyframeModel::RUNNING && + keyframe_model->run_state() != gfx::KeyframeModel::PAUSED) || !keyframe_model->InEffect(monotonic_time)) { return; } - AnimationCurve* curve = keyframe_model->curve(); + gfx::AnimationCurve* curve = keyframe_model->curve(); base::TimeDelta trimmed = keyframe_model->TrimTimeToCurrentIteration(monotonic_time); - - switch (curve->Type()) { - case AnimationCurve::TRANSFORM: - target->NotifyClientTransformOperationsAnimated( - curve->ToTransformAnimationCurve()->GetValue(trimmed), - keyframe_model->target_property_id(), keyframe_model); - break; - case AnimationCurve::FLOAT: - target->NotifyClientFloatAnimated( - curve->ToFloatAnimationCurve()->GetValue(trimmed), - keyframe_model->target_property_id(), keyframe_model); - break; - case AnimationCurve::FILTER: - target->NotifyClientFilterAnimated( - curve->ToFilterAnimationCurve()->GetValue(trimmed), - keyframe_model->target_property_id(), keyframe_model); - break; - case AnimationCurve::COLOR: - target->NotifyClientColorAnimated( - curve->ToColorAnimationCurve()->GetValue(trimmed), - keyframe_model->target_property_id(), keyframe_model); - break; - case AnimationCurve::SCROLL_OFFSET: - target->NotifyClientScrollOffsetAnimated( - curve->ToScrollOffsetAnimationCurve()->GetValue(trimmed), - keyframe_model->target_property_id(), keyframe_model); - break; - case AnimationCurve::SIZE: - target->NotifyClientSizeAnimated( - curve->ToSizeAnimationCurve()->GetValue(trimmed), - keyframe_model->target_property_id(), keyframe_model); - break; - } + curve->Tick(trimmed, keyframe_model->TargetProperty(), keyframe_model); } void KeyframeEffect::RemoveFromTicking() { @@ -225,8 +194,8 @@ void KeyframeEffect::Pause(base::TimeDelta pause_offset, // to tick scroll-linked keyframe models directly. if (pause_condition == PauseCondition::kAfterStart && (keyframe_model->run_state() == - KeyframeModel::WAITING_FOR_TARGET_AVAILABILITY || - keyframe_model->run_state() == KeyframeModel::STARTING)) + gfx::KeyframeModel::WAITING_FOR_TARGET_AVAILABILITY || + keyframe_model->run_state() == gfx::KeyframeModel::STARTING)) continue; keyframe_model->Pause(pause_offset); did_pause = true; @@ -240,29 +209,26 @@ void KeyframeEffect::Pause(base::TimeDelta pause_offset, void KeyframeEffect::AddKeyframeModel( std::unique_ptr<KeyframeModel> keyframe_model) { - DCHECK(keyframe_model->target_property_id() != - TargetProperty::SCROLL_OFFSET || - (animation_->animation_host()->SupportsScrollAnimations())); DCHECK(!keyframe_model->is_impl_only() || - keyframe_model->target_property_id() == TargetProperty::SCROLL_OFFSET); + keyframe_model->TargetProperty() == TargetProperty::SCROLL_OFFSET); // This is to make sure that keyframe models in the same group, i.e., start // together, don't animate the same property. - DCHECK(std::none_of( - keyframe_models_.begin(), keyframe_models_.end(), - [&](const auto& existing_keyframe_model) { - return keyframe_model->target_property_id() == - existing_keyframe_model->target_property_id() && - keyframe_model->group() == existing_keyframe_model->group(); - })); - - if (keyframe_model->target_property_id() == TargetProperty::SCROLL_OFFSET) { + DCHECK(std::none_of(keyframe_models_.begin(), keyframe_models_.end(), + [&](const auto& existing_keyframe_model) { + return keyframe_model->TargetProperty() == + existing_keyframe_model->TargetProperty() && + keyframe_model->group() == + existing_keyframe_model->group(); + })); + + if (keyframe_model->TargetProperty() == TargetProperty::SCROLL_OFFSET) { // We should never have more than one scroll offset animation queued on the // same scrolling element as this would result in multiple automated // scrolls. DCHECK(std::none_of( keyframe_models_.begin(), keyframe_models_.end(), [&](const auto& existing_keyframe_model) { - return existing_keyframe_model->target_property_id() == + return existing_keyframe_model->TargetProperty() == TargetProperty::SCROLL_OFFSET && !existing_keyframe_model->is_finished() && (!existing_keyframe_model->is_controlling_instance() || @@ -306,7 +272,7 @@ void KeyframeEffect::RemoveKeyframeModel(int keyframe_model_id) { }); for (auto it = keyframe_models_to_remove; it != keyframe_models_.end(); ++it) { - if ((*it)->target_property_id() == TargetProperty::SCROLL_OFFSET) { + if ((*it)->TargetProperty() == TargetProperty::SCROLL_OFFSET) { if (has_bound_element_animations()) scroll_offset_animation_was_interrupted_ = true; } else if (!(*it)->is_finished()) { @@ -328,7 +294,7 @@ void KeyframeEffect::RemoveKeyframeModel(int keyframe_model_id) { void KeyframeEffect::AbortKeyframeModel(int keyframe_model_id) { if (KeyframeModel* keyframe_model = GetKeyframeModelById(keyframe_model_id)) { if (!keyframe_model->is_finished()) { - keyframe_model->SetRunState(KeyframeModel::ABORTED, + keyframe_model->SetRunState(gfx::KeyframeModel::ABORTED, last_tick_time_.value_or(base::TimeTicks())); if (has_bound_element_animations()) element_animations_->UpdateClientAnimationState(); @@ -349,17 +315,17 @@ void KeyframeEffect::AbortKeyframeModelsWithProperty( bool aborted_keyframe_model = false; for (auto& keyframe_model : keyframe_models_) { - if (keyframe_model->target_property_id() == target_property && + if (keyframe_model->TargetProperty() == target_property && !keyframe_model->is_finished()) { // Currently only impl-only scroll offset KeyframeModels can be completed // on the main thread. if (needs_completion && keyframe_model->is_impl_only()) { keyframe_model->SetRunState( - KeyframeModel::ABORTED_BUT_NEEDS_COMPLETION, + gfx::KeyframeModel::ABORTED_BUT_NEEDS_COMPLETION, last_tick_time_.value_or(base::TimeTicks())); } else { keyframe_model->SetRunState( - KeyframeModel::ABORTED, + gfx::KeyframeModel::ABORTED, last_tick_time_.value_or(base::TimeTicks())); } aborted_keyframe_model = true; @@ -400,6 +366,9 @@ void KeyframeEffect::KeyframeModelAdded() { needs_to_start_keyframe_models_ = true; UpdateTickingState(); + for (auto& keyframe_model : keyframe_models_) { + element_animations_->AttachToCurve(keyframe_model->curve()); + } element_animations_->UpdateClientAnimationState(); } @@ -433,7 +402,7 @@ bool KeyframeEffect::DispatchAnimationEventToKeyframeModel( case AnimationEvent::ABORTED: if (keyframe_model) { - keyframe_model->SetRunState(KeyframeModel::ABORTED, + keyframe_model->SetRunState(gfx::KeyframeModel::ABORTED, event.monotonic_time); keyframe_model->set_received_finished_event(true); dispatched = true; @@ -474,14 +443,14 @@ bool KeyframeEffect::HasTickingKeyframeModel() const { bool KeyframeEffect::AffectsCustomProperty() const { for (const auto& it : keyframe_models_) - if (it->target_property_id() == TargetProperty::CSS_CUSTOM_PROPERTY) + if (it->TargetProperty() == TargetProperty::CSS_CUSTOM_PROPERTY) return true; return false; } bool KeyframeEffect::HasNonDeletedKeyframeModel() const { for (const auto& keyframe_model : keyframe_models_) { - if (keyframe_model->run_state() != KeyframeModel::WAITING_FOR_DELETION) + if (keyframe_model->run_state() != gfx::KeyframeModel::WAITING_FOR_DELETION) return true; } return false; @@ -489,28 +458,19 @@ bool KeyframeEffect::HasNonDeletedKeyframeModel() const { bool KeyframeEffect::AnimationsPreserveAxisAlignment() const { for (const auto& keyframe_model : keyframe_models_) { - if (keyframe_model->is_finished() || - keyframe_model->target_property_id() != TargetProperty::TRANSFORM) + if (keyframe_model->is_finished()) continue; - const TransformAnimationCurve* transform_animation_curve = - keyframe_model->curve()->ToTransformAnimationCurve(); - if (!transform_animation_curve->PreservesAxisAlignment()) + if (!keyframe_model->curve()->PreservesAxisAlignment()) return false; } return true; } -bool KeyframeEffect::GetAnimationScales(ElementListType list_type, - float* maximum_scale, - float* starting_scale) const { - *maximum_scale = kNotScaled; - *starting_scale = kNotScaled; - bool maximum_scale_valid = true; - bool starting_scale_valid = true; +float KeyframeEffect::MaximumScale(ElementListType list_type) const { + float maximum_scale = kInvalidScale; for (const auto& keyframe_model : keyframe_models_) { - if (keyframe_model->is_finished() || - keyframe_model->target_property_id() != TargetProperty::TRANSFORM) + if (keyframe_model->is_finished()) continue; if ((list_type == ElementListType::ACTIVE && @@ -519,50 +479,11 @@ bool KeyframeEffect::GetAnimationScales(ElementListType list_type, !keyframe_model->affects_pending_elements())) continue; - const TransformAnimationCurve* transform_animation_curve = - keyframe_model->curve()->ToTransformAnimationCurve(); - if (transform_animation_curve->IsTranslation()) - continue; - - bool forward_direction = true; - switch (keyframe_model->direction()) { - case KeyframeModel::Direction::NORMAL: - case KeyframeModel::Direction::ALTERNATE_NORMAL: - forward_direction = keyframe_model->playback_rate() >= 0.0; - break; - case KeyframeModel::Direction::REVERSE: - case KeyframeModel::Direction::ALTERNATE_REVERSE: - forward_direction = keyframe_model->playback_rate() < 0.0; - break; - } - - if (maximum_scale_valid) { - float keyframe_model_maximum_scale = kNotScaled; - if (transform_animation_curve->MaximumTargetScale( - forward_direction, &keyframe_model_maximum_scale)) { - *maximum_scale = std::max(*maximum_scale, keyframe_model_maximum_scale); - } else { - maximum_scale_valid = false; - *maximum_scale = kNotScaled; - } - } - - if (starting_scale_valid) { - float keyframe_model_starting_scale = kNotScaled; - if (transform_animation_curve->AnimationStartScale( - forward_direction, &keyframe_model_starting_scale)) { - *starting_scale = - std::max(*starting_scale, keyframe_model_starting_scale); - } else { - starting_scale_valid = false; - *starting_scale = kNotScaled; - } - } - - if (!maximum_scale_valid && !starting_scale_valid) - return false; + float curve_maximum_scale = kInvalidScale; + if (keyframe_model->curve()->MaximumScale(&curve_maximum_scale)) + maximum_scale = std::max(maximum_scale, curve_maximum_scale); } - return true; + return maximum_scale; } bool KeyframeEffect::IsPotentiallyAnimatingProperty( @@ -570,7 +491,7 @@ bool KeyframeEffect::IsPotentiallyAnimatingProperty( ElementListType list_type) const { for (const auto& keyframe_model : keyframe_models_) { if (!keyframe_model->is_finished() && - keyframe_model->target_property_id() == target_property) { + keyframe_model->TargetProperty() == target_property) { if ((list_type == ElementListType::ACTIVE && keyframe_model->affects_active_elements()) || (list_type == ElementListType::PENDING && @@ -587,7 +508,7 @@ bool KeyframeEffect::IsCurrentlyAnimatingProperty( for (const auto& keyframe_model : keyframe_models_) { if (!keyframe_model->is_finished() && keyframe_model->InEffect(last_tick_time_.value_or(base::TimeTicks())) && - keyframe_model->target_property_id() == target_property) { + keyframe_model->TargetProperty() == target_property) { if ((list_type == ElementListType::ACTIVE && keyframe_model->affects_active_elements()) || (list_type == ElementListType::PENDING && @@ -602,7 +523,7 @@ KeyframeModel* KeyframeEffect::GetKeyframeModel( TargetProperty::Type target_property) const { for (size_t i = 0; i < keyframe_models_.size(); ++i) { size_t index = keyframe_models_.size() - i - 1; - if (keyframe_models_[index]->target_property_id() == target_property) + if (keyframe_models_[index]->TargetProperty() == target_property) return keyframe_models_[index].get(); } return nullptr; @@ -628,7 +549,7 @@ void KeyframeEffect::GetPropertyAnimationState( keyframe_model->InEffect(last_tick_time_.value_or(base::TimeTicks())); bool active = keyframe_model->affects_active_elements(); bool pending = keyframe_model->affects_pending_elements(); - int property = keyframe_model->target_property_id(); + int property = keyframe_model->TargetProperty(); if (pending) pending_state->potentially_animating[property] = true; @@ -653,12 +574,12 @@ void KeyframeEffect::MarkAbortedKeyframeModelsForDeletion( // deletion. if (KeyframeModel* keyframe_model = GetKeyframeModelById(keyframe_model_impl->id())) { - if (keyframe_model->run_state() == KeyframeModel::ABORTED) { + if (keyframe_model->run_state() == gfx::KeyframeModel::ABORTED) { keyframe_model_impl->SetRunState( - KeyframeModel::WAITING_FOR_DELETION, + gfx::KeyframeModel::WAITING_FOR_DELETION, keyframe_effect_impl->last_tick_time_.value_or(base::TimeTicks())); keyframe_model->SetRunState( - KeyframeModel::WAITING_FOR_DELETION, + gfx::KeyframeModel::WAITING_FOR_DELETION, last_tick_time_.value_or(base::TimeTicks())); keyframe_model_aborted = true; } @@ -674,7 +595,7 @@ void KeyframeEffect::PurgeKeyframeModelsMarkedForDeletion(bool impl_only) { keyframe_models_, [impl_only](const std::unique_ptr<KeyframeModel>& keyframe_model) { return keyframe_model->run_state() == - KeyframeModel::WAITING_FOR_DELETION && + gfx::KeyframeModel::WAITING_FOR_DELETION && (!impl_only || keyframe_model->is_impl_only()); }); } @@ -683,7 +604,7 @@ void KeyframeEffect::PurgeDeletedKeyframeModels() { base::EraseIf(keyframe_models_, [](const std::unique_ptr<KeyframeModel>& keyframe_model) { return keyframe_model->run_state() == - KeyframeModel::WAITING_FOR_DELETION && + gfx::KeyframeModel::WAITING_FOR_DELETION && !keyframe_model->affects_pending_elements(); }); } @@ -703,9 +624,9 @@ void KeyframeEffect::PushNewKeyframeModelsToImplThread( if (keyframe_effect_impl->GetKeyframeModelById(keyframe_model->id())) continue; - if (keyframe_model->target_property_id() == TargetProperty::SCROLL_OFFSET && - !keyframe_model->curve() - ->ToScrollOffsetAnimationCurve() + if (keyframe_model->TargetProperty() == TargetProperty::SCROLL_OFFSET && + !ScrollOffsetAnimationCurve::ToScrollOffsetAnimationCurve( + keyframe_model->curve()) ->HasSetInitialValue()) { gfx::ScrollOffset current_scroll_offset; if (keyframe_effect_impl->HasElementInActiveList()) { @@ -716,13 +637,15 @@ void KeyframeEffect::PushNewKeyframeModelsToImplThread( // scroll offset will be up to date. current_scroll_offset = ScrollOffsetForAnimation(); } - keyframe_model->curve()->ToScrollOffsetAnimationCurve()->SetInitialValue( - current_scroll_offset); + ScrollOffsetAnimationCurve* curve = + ScrollOffsetAnimationCurve::ToScrollOffsetAnimationCurve( + keyframe_model->curve()); + curve->SetInitialValue(current_scroll_offset); } // The new keyframe_model should be set to run as soon as possible. - KeyframeModel::RunState initial_run_state = - KeyframeModel::WAITING_FOR_TARGET_AVAILABILITY; + gfx::KeyframeModel::RunState initial_run_state = + gfx::KeyframeModel::WAITING_FOR_TARGET_AVAILABILITY; std::unique_ptr<KeyframeModel> to_add( keyframe_model->CreateImplInstance(initial_run_state)); DCHECK(!to_add->needs_synchronized_start_time()); @@ -735,7 +658,8 @@ namespace { bool IsCompleted(KeyframeModel* keyframe_model, const KeyframeEffect* main_thread_keyframe_effect) { if (keyframe_model->is_impl_only()) { - return (keyframe_model->run_state() == KeyframeModel::WAITING_FOR_DELETION); + return (keyframe_model->run_state() == + gfx::KeyframeModel::WAITING_FOR_DELETION); } else { KeyframeModel* main_thread_keyframe_model = main_thread_keyframe_effect->GetKeyframeModelById(keyframe_model->id()); @@ -824,21 +748,33 @@ std::string KeyframeEffect::KeyframeModelsToString() const { return str; } +base::TimeDelta KeyframeEffect::MinimumTickInterval() const { + base::TimeDelta min_interval = base::TimeDelta::Max(); + for (const auto& model : keyframe_models_) { + base::TimeDelta interval = model->curve()->TickInterval(); + if (interval.is_zero()) + return interval; + if (interval < min_interval) + min_interval = interval; + } + return min_interval; +} + void KeyframeEffect::StartKeyframeModels(base::TimeTicks monotonic_time) { DCHECK(needs_to_start_keyframe_models_); needs_to_start_keyframe_models_ = false; // First collect running properties affecting each type of element. - TargetProperties blocked_properties_for_active_elements; - TargetProperties blocked_properties_for_pending_elements; + gfx::TargetProperties blocked_properties_for_active_elements; + gfx::TargetProperties blocked_properties_for_pending_elements; std::vector<size_t> keyframe_models_waiting_for_target; keyframe_models_waiting_for_target.reserve(keyframe_models_.size()); for (size_t i = 0; i < keyframe_models_.size(); ++i) { auto& keyframe_model = keyframe_models_[i]; - if (keyframe_model->run_state() == KeyframeModel::STARTING || - keyframe_model->run_state() == KeyframeModel::RUNNING) { - int property = keyframe_model->target_property_id(); + if (keyframe_model->run_state() == gfx::KeyframeModel::STARTING || + keyframe_model->run_state() == gfx::KeyframeModel::RUNNING) { + int property = keyframe_model->TargetProperty(); if (keyframe_model->affects_active_elements()) { blocked_properties_for_active_elements[property] = true; } @@ -846,7 +782,7 @@ void KeyframeEffect::StartKeyframeModels(base::TimeTicks monotonic_time) { blocked_properties_for_pending_elements[property] = true; } } else if (keyframe_model->run_state() == - KeyframeModel::WAITING_FOR_TARGET_AVAILABILITY) { + gfx::KeyframeModel::WAITING_FOR_TARGET_AVAILABILITY) { keyframe_models_waiting_for_target.push_back(i); } } @@ -861,19 +797,19 @@ void KeyframeEffect::StartKeyframeModels(base::TimeTicks monotonic_time) { // for target because it might have changed the run state while handling // previous keyframe_model in this loop (if they belong to same group). if (keyframe_model_waiting_for_target->run_state() == - KeyframeModel::WAITING_FOR_TARGET_AVAILABILITY) { - TargetProperties enqueued_properties; + gfx::KeyframeModel::WAITING_FOR_TARGET_AVAILABILITY) { + gfx::TargetProperties enqueued_properties; bool affects_active_elements = keyframe_model_waiting_for_target->affects_active_elements(); bool affects_pending_elements = keyframe_model_waiting_for_target->affects_pending_elements(); - enqueued_properties[keyframe_model_waiting_for_target - ->target_property_id()] = true; + enqueued_properties[keyframe_model_waiting_for_target->TargetProperty()] = + true; for (size_t j = keyframe_model_index + 1; j < keyframe_models_.size(); ++j) { if (keyframe_model_waiting_for_target->group() == keyframe_models_[j]->group()) { - enqueued_properties[keyframe_models_[j]->target_property_id()] = true; + enqueued_properties[keyframe_models_[j]->TargetProperty()] = true; affects_active_elements |= keyframe_models_[j]->affects_active_elements(); affects_pending_elements |= @@ -908,13 +844,13 @@ void KeyframeEffect::StartKeyframeModels(base::TimeTicks monotonic_time) { // If the intersection is null, then we are free to start the // KeyframeModels in the group. if (null_intersection) { - keyframe_model_waiting_for_target->SetRunState(KeyframeModel::STARTING, - monotonic_time); + keyframe_model_waiting_for_target->SetRunState( + gfx::KeyframeModel::STARTING, monotonic_time); for (size_t j = keyframe_model_index + 1; j < keyframe_models_.size(); ++j) { if (keyframe_model_waiting_for_target->group() == keyframe_models_[j]->group()) { - keyframe_models_[j]->SetRunState(KeyframeModel::STARTING, + keyframe_models_[j]->SetRunState(gfx::KeyframeModel::STARTING, monotonic_time); } } @@ -927,9 +863,9 @@ void KeyframeEffect::StartKeyframeModels(base::TimeTicks monotonic_time) { void KeyframeEffect::PromoteStartedKeyframeModels(AnimationEvents* events) { for (auto& keyframe_model : keyframe_models_) { - if (keyframe_model->run_state() == KeyframeModel::STARTING && + if (keyframe_model->run_state() == gfx::KeyframeModel::STARTING && keyframe_model->affects_active_elements()) { - keyframe_model->SetRunState(KeyframeModel::RUNNING, + keyframe_model->SetRunState(gfx::KeyframeModel::RUNNING, last_tick_time_.value_or(base::TimeTicks())); if (!keyframe_model->has_set_start_time() && !keyframe_model->needs_synchronized_start_time()) @@ -953,7 +889,7 @@ void KeyframeEffect::MarkKeyframeModelsForDeletion( AnimationEvents* events) { bool marked_keyframe_model_for_deletion = false; auto MarkForDeletion = [&](KeyframeModel* keyframe_model) { - keyframe_model->SetRunState(KeyframeModel::WAITING_FOR_DELETION, + keyframe_model->SetRunState(gfx::KeyframeModel::WAITING_FOR_DELETION, monotonic_time); marked_keyframe_model_for_deletion = true; }; @@ -965,7 +901,7 @@ void KeyframeEffect::MarkKeyframeModelsForDeletion( // deletion. for (size_t i = 0; i < keyframe_models_.size(); i++) { KeyframeModel* keyframe_model = keyframe_models_[i].get(); - if (keyframe_model->run_state() == KeyframeModel::ABORTED) { + if (keyframe_model->run_state() == gfx::KeyframeModel::ABORTED) { GenerateEvent(events, *keyframe_model, AnimationEvent::ABORTED, monotonic_time); // If this is the controlling instance or it has already received finish @@ -979,7 +915,7 @@ void KeyframeEffect::MarkKeyframeModelsForDeletion( // main thread, generate takeover event. if (keyframe_model->is_controlling_instance() && keyframe_model->run_state() == - KeyframeModel::ABORTED_BUT_NEEDS_COMPLETION) { + gfx::KeyframeModel::ABORTED_BUT_NEEDS_COMPLETION) { GenerateTakeoverEventForScrollAnimation(events, *keyframe_model, monotonic_time); // Remove the keyframe model from the impl thread. @@ -987,7 +923,7 @@ void KeyframeEffect::MarkKeyframeModelsForDeletion( continue; } - if (keyframe_model->run_state() != KeyframeModel::FINISHED) + if (keyframe_model->run_state() != gfx::KeyframeModel::FINISHED) continue; // Since deleting an animation on the main thread leads to its deletion @@ -1008,7 +944,7 @@ void KeyframeEffect::MarkKeyframeModelsForDeletion( keyframe_models_in_same_group.cend(), [&](size_t index) { KeyframeModel* keyframe_model = keyframe_models_[index].get(); return !keyframe_model->is_finished() || - (keyframe_model->run_state() == KeyframeModel::FINISHED && + (keyframe_model->run_state() == gfx::KeyframeModel::FINISHED && NeedsFinishedEvent(keyframe_model)); }); @@ -1024,8 +960,8 @@ void KeyframeEffect::MarkKeyframeModelsForDeletion( // Skip any keyframe model in this group which is already processed. if (same_group_keyframe_model->run_state() == - KeyframeModel::WAITING_FOR_DELETION || - same_group_keyframe_model->run_state() == KeyframeModel::ABORTED) + gfx::KeyframeModel::WAITING_FOR_DELETION || + same_group_keyframe_model->run_state() == gfx::KeyframeModel::ABORTED) continue; GenerateEvent(events, *same_group_keyframe_model, @@ -1048,18 +984,19 @@ void KeyframeEffect::MarkFinishedKeyframeModels( for (auto& keyframe_model : keyframe_models_) { if (!keyframe_model->is_finished() && keyframe_model->IsFinishedAt(monotonic_time)) { - keyframe_model->SetRunState(KeyframeModel::FINISHED, monotonic_time); + keyframe_model->SetRunState(gfx::KeyframeModel::FINISHED, monotonic_time); keyframe_model_finished = true; SetNeedsPushProperties(); } if (!keyframe_model->affects_active_elements() && !keyframe_model->affects_pending_elements()) { switch (keyframe_model->run_state()) { - case KeyframeModel::WAITING_FOR_TARGET_AVAILABILITY: - case KeyframeModel::STARTING: - case KeyframeModel::RUNNING: - case KeyframeModel::PAUSED: - keyframe_model->SetRunState(KeyframeModel::FINISHED, monotonic_time); + case gfx::KeyframeModel::WAITING_FOR_TARGET_AVAILABILITY: + case gfx::KeyframeModel::STARTING: + case gfx::KeyframeModel::RUNNING: + case gfx::KeyframeModel::PAUSED: + keyframe_model->SetRunState(gfx::KeyframeModel::FINISHED, + monotonic_time); keyframe_model_finished = true; break; default: @@ -1091,8 +1028,8 @@ void KeyframeEffect::GenerateEvent(AnimationEvents* events, AnimationEvent event(type, {animation_->animation_timeline()->id(), animation_->id(), keyframe_model.id()}, - keyframe_model.group(), - keyframe_model.target_property_id(), monotonic_time); + keyframe_model.group(), keyframe_model.TargetProperty(), + monotonic_time); event.is_impl_only = keyframe_model.is_impl_only(); if (!event.is_impl_only) { events->events_.push_back(event); @@ -1106,29 +1043,28 @@ void KeyframeEffect::GenerateTakeoverEventForScrollAnimation( AnimationEvents* events, const KeyframeModel& keyframe_model, base::TimeTicks monotonic_time) { - DCHECK_EQ(keyframe_model.target_property_id(), TargetProperty::SCROLL_OFFSET); + DCHECK_EQ(keyframe_model.TargetProperty(), TargetProperty::SCROLL_OFFSET); if (!events) return; - AnimationEvent takeover_event(AnimationEvent::TAKEOVER, - {animation_->animation_timeline()->id(), - animation_->id(), keyframe_model.id()}, - keyframe_model.group(), - keyframe_model.target_property_id(), - monotonic_time); + AnimationEvent takeover_event( + AnimationEvent::TAKEOVER, + {animation_->animation_timeline()->id(), animation_->id(), + keyframe_model.id()}, + keyframe_model.group(), keyframe_model.TargetProperty(), monotonic_time); takeover_event.animation_start_time = keyframe_model.start_time(); const ScrollOffsetAnimationCurve* scroll_offset_animation_curve = - keyframe_model.curve()->ToScrollOffsetAnimationCurve(); + ScrollOffsetAnimationCurve::ToScrollOffsetAnimationCurve( + keyframe_model.curve()); takeover_event.curve = scroll_offset_animation_curve->Clone(); // Notify main thread. events->events_.push_back(takeover_event); - AnimationEvent finished_event(AnimationEvent::FINISHED, - {animation_->animation_timeline()->id(), - animation_->id(), keyframe_model.id()}, - keyframe_model.group(), - keyframe_model.target_property_id(), - monotonic_time); + AnimationEvent finished_event( + AnimationEvent::FINISHED, + {animation_->animation_timeline()->id(), animation_->id(), + keyframe_model.id()}, + keyframe_model.group(), keyframe_model.TargetProperty(), monotonic_time); // Notify the compositor that the animation is finished. finished_event.is_impl_only = true; animation_->DispatchAndDelegateAnimationEvent(finished_event); diff --git a/chromium/cc/animation/keyframe_effect.h b/chromium/cc/animation/keyframe_effect.h index 0e1280f82e1..9e511add1f8 100644 --- a/chromium/cc/animation/keyframe_effect.h +++ b/chromium/cc/animation/keyframe_effect.h @@ -5,20 +5,22 @@ #ifndef CC_ANIMATION_KEYFRAME_EFFECT_H_ #define CC_ANIMATION_KEYFRAME_EFFECT_H_ +#include <memory> +#include <string> +#include <vector> + #include "base/memory/ref_counted.h" #include "base/time/time.h" #include "cc/animation/animation_events.h" #include "cc/animation/animation_export.h" #include "cc/animation/element_animations.h" +#include "cc/animation/keyframe_model.h" #include "cc/paint/element_id.h" #include "cc/trees/mutator_host_client.h" #include "cc/trees/target_property.h" #include "ui/gfx/geometry/box_f.h" #include "ui/gfx/geometry/scroll_offset.h" -#include <memory> -#include <vector> - namespace cc { class Animation; @@ -80,8 +82,7 @@ class CC_ANIMATION_EXPORT KeyframeEffect { virtual void Tick(base::TimeTicks monotonic_time); static void TickKeyframeModel(base::TimeTicks monotonic_time, - KeyframeModel* keyframe_model, - AnimationTarget* target); + KeyframeModel* keyframe_model); void RemoveFromTicking(); void UpdateState(bool start_ready_keyframe_models, AnimationEvents* events); @@ -115,15 +116,10 @@ class CC_ANIMATION_EXPORT KeyframeEffect { bool AnimationsPreserveAxisAlignment() const; - // Gets scales transform animations. On return, |maximum_scale| is the maximum - // scale along any dimension at any destination in active scale animations, - // and |starting_scale| is the maximum of starting animation scale along any - // dimension at any destination in active scale animations. They are set to - // kNotScaled if there is no active scale animation or the scales cannot be - // computed. Returns false if the scales cannot be computed. - bool GetAnimationScales(ElementListType, - float* maximum_scale, - float* starting_scale) const; + // Returns the maximum scale along any dimension at any destination in active + // scale animations, or kInvalidScale if there is no active transform + // animation or the scale cannot be computed. + float MaximumScale(ElementListType) const; // Returns true if there is a keyframe_model that is either currently // animating the given property or scheduled to animate this property in the @@ -153,6 +149,12 @@ class CC_ANIMATION_EXPORT KeyframeEffect { std::string KeyframeModelsToString() const; + // Iterates through all |keyframe_models_| and returns the minimum of their + // animation curve's tick intervals. + // Returns 0 if there is a continuous animation which should be ticked as + // fast as possible. + base::TimeDelta MinimumTickInterval() const; + private: void StartKeyframeModels(base::TimeTicks monotonic_time); void PromoteStartedKeyframeModels(AnimationEvents* events); diff --git a/chromium/cc/animation/keyframe_model.cc b/chromium/cc/animation/keyframe_model.cc index 0f73210ba14..7f4801ea695 100644 --- a/chromium/cc/animation/keyframe_model.cc +++ b/chromium/cc/animation/keyframe_model.cc @@ -16,51 +16,54 @@ #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/trace_event/trace_event.h" -#include "cc/animation/animation_curve.h" #include "cc/trees/target_property.h" +#include "ui/gfx/animation/keyframe/animation_curve.h" -namespace { +namespace cc { -// This should match the RunState enum. -static const char* const s_runStateNames[] = {"WAITING_FOR_TARGET_AVAILABILITY", - "WAITING_FOR_DELETION", - "STARTING", - "RUNNING", - "PAUSED", - "FINISHED", - "ABORTED", - "ABORTED_BUT_NEEDS_COMPLETION"}; +// static +const KeyframeModel* KeyframeModel::ToCcKeyframeModel( + const gfx::KeyframeModel* keyframe_model) { + return static_cast<const KeyframeModel*>(keyframe_model); +} -static_assert(static_cast<int>(cc::KeyframeModel::LAST_RUN_STATE) + 1 == - base::size(s_runStateNames), - "RunStateEnumSize should equal the number of elements in " - "s_runStateNames"); +// static +KeyframeModel* KeyframeModel::ToCcKeyframeModel( + gfx::KeyframeModel* keyframe_model) { + return static_cast<KeyframeModel*>(keyframe_model); +} -static const char* const s_curveTypeNames[] = { - "COLOR", "FLOAT", "TRANSFORM", "FILTER", "SCROLL_OFFSET", "SIZE"}; +KeyframeModel::TargetPropertyId::TargetPropertyId(int target_property_type) + : target_property_type_(target_property_type), + custom_property_name_(""), + native_property_type_(PaintWorkletInput::NativePropertyType::kInvalid) {} -static_assert(static_cast<int>(cc::AnimationCurve::LAST_CURVE_TYPE) + 1 == - base::size(s_curveTypeNames), - "CurveType enum should equal the number of elements in " - "s_runStateNames"); +KeyframeModel::TargetPropertyId::TargetPropertyId( + int target_property_type, + const std::string& custom_property_name) + : target_property_type_(target_property_type), + custom_property_name_(custom_property_name), + native_property_type_(PaintWorkletInput::NativePropertyType::kInvalid) {} -} // namespace +KeyframeModel::TargetPropertyId::TargetPropertyId( + int target_property_type, + PaintWorkletInput::NativePropertyType native_property_type) + : target_property_type_(target_property_type), + custom_property_name_(""), + native_property_type_(native_property_type) {} -namespace cc { +KeyframeModel::TargetPropertyId::TargetPropertyId( + const TargetPropertyId& other) = default; -std::string KeyframeModel::ToString(RunState state) { - return s_runStateNames[state]; -} +KeyframeModel::TargetPropertyId::~TargetPropertyId() = default; std::unique_ptr<KeyframeModel> KeyframeModel::Create( - std::unique_ptr<AnimationCurve> curve, + std::unique_ptr<gfx::AnimationCurve> curve, int keyframe_model_id, int group_id, - int target_property_id, - const std::string& custom_property_name) { + TargetPropertyId target_property_id) { return base::WrapUnique(new KeyframeModel(std::move(curve), keyframe_model_id, - group_id, target_property_id, - custom_property_name)); + group_id, target_property_id)); } std::unique_ptr<KeyframeModel> KeyframeModel::CreateImplInstance( @@ -69,65 +72,56 @@ std::unique_ptr<KeyframeModel> KeyframeModel::CreateImplInstance( // creating multiple controlling instances. DCHECK(!is_controlling_instance_); std::unique_ptr<KeyframeModel> to_return( - new KeyframeModel(curve_->Clone(), id_, group_, target_property_id_, - custom_property_name_)); + new KeyframeModel(curve()->Clone(), id(), group_, target_property_id_)); to_return->element_id_ = element_id_; - to_return->run_state_ = initial_run_state; - to_return->iterations_ = iterations_; - to_return->iteration_start_ = iteration_start_; - to_return->start_time_ = start_time_; - to_return->pause_time_ = pause_time_; - to_return->total_paused_duration_ = total_paused_duration_; - to_return->time_offset_ = time_offset_; - to_return->direction_ = direction_; - to_return->playback_rate_ = playback_rate_; - to_return->fill_mode_ = fill_mode_; + to_return->ForceRunState(initial_run_state); + to_return->set_iterations(iterations()); + to_return->set_iteration_start(iteration_start()); + to_return->set_start_time(start_time()); + to_return->set_pause_time(pause_time()); + to_return->set_total_paused_duration(total_paused_duration()); + to_return->set_time_offset(time_offset()); + to_return->set_direction(direction()); + to_return->set_playback_rate(playback_rate()); + to_return->set_fill_mode(fill_mode()); DCHECK(!to_return->is_controlling_instance_); to_return->is_controlling_instance_ = true; return to_return; } -KeyframeModel::KeyframeModel(std::unique_ptr<AnimationCurve> curve, +KeyframeModel::KeyframeModel(std::unique_ptr<gfx::AnimationCurve> curve, int keyframe_model_id, int group_id, - int target_property_id, - const std::string& custom_property_name) - : curve_(std::move(curve)), - id_(keyframe_model_id), + TargetPropertyId target_property_id) + : gfx::KeyframeModel(std::move(curve), + keyframe_model_id, + target_property_id.target_property_type()), group_(group_id), target_property_id_(target_property_id), - run_state_(WAITING_FOR_TARGET_AVAILABILITY), - iterations_(1), - iteration_start_(0), - direction_(Direction::NORMAL), - playback_rate_(1), - fill_mode_(FillMode::BOTH), needs_synchronized_start_time_(false), received_finished_event_(false), is_controlling_instance_(false), is_impl_only_(false), affects_active_elements_(true), - affects_pending_elements_(true), - custom_property_name_(custom_property_name) { - DCHECK(custom_property_name_.empty() || - target_property_id_ == TargetProperty::CSS_CUSTOM_PROPERTY); -} + affects_pending_elements_(true) {} -KeyframeModel::~KeyframeModel() { - if (run_state_ == RUNNING || run_state_ == PAUSED) - SetRunState(ABORTED, base::TimeTicks()); +KeyframeModel::~KeyframeModel() = default; + +int KeyframeModel::TargetProperty() const { + return target_property_id_.target_property_type(); } -void KeyframeModel::SetRunState(RunState run_state, +void KeyframeModel::SetRunState(RunState new_run_state, base::TimeTicks monotonic_time) { char name_buffer[256]; base::snprintf(name_buffer, sizeof(name_buffer), "%s-%d-%d", - s_curveTypeNames[curve_->Type()], target_property_id_, group_); + curve()->TypeName(), TargetProperty(), group_); bool is_waiting_to_start = - run_state_ == WAITING_FOR_TARGET_AVAILABILITY || run_state_ == STARTING; + run_state() == WAITING_FOR_TARGET_AVAILABILITY || run_state() == STARTING; - if (is_controlling_instance_ && is_waiting_to_start && run_state == RUNNING) { + if (is_controlling_instance_ && is_waiting_to_start && + new_run_state == RUNNING) { TRACE_EVENT_NESTABLE_ASYNC_BEGIN1("cc", "KeyframeModel", TRACE_ID_LOCAL(this), "Name", TRACE_STR_COPY(name_buffer)); @@ -135,15 +129,9 @@ void KeyframeModel::SetRunState(RunState run_state, bool was_finished = is_finished(); - const char* old_run_state_name = s_runStateNames[run_state_]; - - if (run_state == RUNNING && run_state_ == PAUSED) - total_paused_duration_ += (monotonic_time - pause_time_); - else if (run_state == PAUSED) - pause_time_ = monotonic_time; - run_state_ = run_state; - - const char* new_run_state_name = s_runStateNames[run_state]; + auto old_run_state_name = gfx::KeyframeModel::ToString(run_state()); + gfx::KeyframeModel::SetRunState(new_run_state, monotonic_time); + auto new_run_state_name = gfx::KeyframeModel::ToString(new_run_state); if (is_controlling_instance_ && !was_finished && is_finished()) { TRACE_EVENT_NESTABLE_ASYNC_END0("cc", "KeyframeModel", @@ -152,208 +140,37 @@ void KeyframeModel::SetRunState(RunState run_state, char state_buffer[256]; base::snprintf(state_buffer, sizeof(state_buffer), "%s->%s", - old_run_state_name, new_run_state_name); + old_run_state_name.c_str(), new_run_state_name.c_str()); TRACE_EVENT_INSTANT2( "cc", "ElementAnimations::SetRunState", TRACE_EVENT_SCOPE_THREAD, "Name", TRACE_STR_COPY(name_buffer), "State", TRACE_STR_COPY(state_buffer)); } -void KeyframeModel::Pause(base::TimeDelta pause_offset) { - // Convert pause offset which is in local time to monotonic time. - // TODO(crbug.com/912407): This should be scaled by playbackrate. - base::TimeTicks monotonic_time = - pause_offset + start_time_ + total_paused_duration_; - SetRunState(PAUSED, monotonic_time); -} - -bool KeyframeModel::IsFinishedAt(base::TimeTicks monotonic_time) const { - if (is_finished()) - return true; - - if (needs_synchronized_start_time_) - return false; - - if (playback_rate_ == 0) - return false; - - return run_state_ == RUNNING && std::isfinite(iterations_) && - (curve_->Duration() * (iterations_ / std::abs(playback_rate_))) <= - (ConvertMonotonicTimeToLocalTime(monotonic_time) + time_offset_); -} - -KeyframeModel::Phase KeyframeModel::CalculatePhaseForTesting( - base::TimeDelta local_time) const { - return CalculatePhase(local_time); -} - -KeyframeModel::Phase KeyframeModel::CalculatePhase( - base::TimeDelta local_time) const { - base::TimeDelta opposite_time_offset = time_offset_ == base::TimeDelta::Min() - ? base::TimeDelta::Max() - : -time_offset_; - base::TimeDelta before_active_boundary_time = - std::max(opposite_time_offset, base::TimeDelta()); - if (local_time < before_active_boundary_time || - (local_time == before_active_boundary_time && playback_rate_ < 0)) { - return KeyframeModel::Phase::BEFORE; - } - // TODO(crbug.com/909794): By spec end time = max(start delay + duration + - // end delay, 0). The logic should be updated once "end delay" is supported. - base::TimeDelta active_after_boundary_time = base::TimeDelta::Max(); - if (std::isfinite(iterations_)) { - // Scaling the duration is against spec but needed to comply with the cc - // implementation. By spec (in blink) the playback rate is an Animation - // level concept but in cc it's per KeyframeModel. We grab the active time - // calculated here and later scale it with the playback rate in order to get - // a proper progress. Therefore we need to un-scale it here. This can be - // fixed once we scale the local time by playback rate. See - // https://crbug.com/912407. - base::TimeDelta active_duration = - curve_->Duration() * iterations_ / std::abs(playback_rate_); - active_after_boundary_time = - std::max(opposite_time_offset + active_duration, base::TimeDelta()); - } - if (local_time > active_after_boundary_time || - (local_time == active_after_boundary_time && playback_rate_ > 0)) { - return KeyframeModel::Phase::AFTER; - } - return KeyframeModel::Phase::ACTIVE; -} - -base::Optional<base::TimeDelta> KeyframeModel::CalculateActiveTime( - base::TimeTicks monotonic_time) const { - base::TimeDelta local_time = ConvertMonotonicTimeToLocalTime(monotonic_time); - KeyframeModel::Phase phase = CalculatePhase(local_time); - DCHECK(playback_rate_); - switch (phase) { - case KeyframeModel::Phase::BEFORE: - if (fill_mode_ == FillMode::BACKWARDS || fill_mode_ == FillMode::BOTH) - return std::max(local_time + time_offset_, base::TimeDelta()); - return base::nullopt; - case KeyframeModel::Phase::ACTIVE: - return local_time + time_offset_; - case KeyframeModel::Phase::AFTER: - if (fill_mode_ == FillMode::FORWARDS || fill_mode_ == FillMode::BOTH) { - DCHECK_NE(iterations_, std::numeric_limits<double>::infinity()); - base::TimeDelta active_duration = - curve_->Duration() * iterations_ / std::abs(playback_rate_); - return std::max(std::min(local_time + time_offset_, active_duration), - base::TimeDelta()); - } - return base::nullopt; - default: - NOTREACHED(); - return base::nullopt; - } -} - bool KeyframeModel::InEffect(base::TimeTicks monotonic_time) const { return CalculateActiveTime(monotonic_time).has_value(); } -// TODO(crbug.com/912407): Local time should be scaled by playback rate by spec. -base::TimeDelta KeyframeModel::ConvertMonotonicTimeToLocalTime( - base::TimeTicks monotonic_time) const { - // When waiting on receiving a start time, then our global clock is 'stuck' at - // the initial state. - if ((run_state_ == STARTING && !has_set_start_time()) || - needs_synchronized_start_time()) - return base::TimeDelta(); - - // If we're paused, time is 'stuck' at the pause time. - base::TimeTicks time = (run_state_ == PAUSED) ? pause_time_ : monotonic_time; - return time - start_time_ - total_paused_duration_; -} - -base::TimeDelta KeyframeModel::TrimTimeToCurrentIteration( - base::TimeTicks monotonic_time) const { - DCHECK(playback_rate_); - DCHECK_GE(iteration_start_, 0); - - DCHECK(InEffect(monotonic_time)); - base::TimeDelta active_time = CalculateActiveTime(monotonic_time).value(); - base::TimeDelta start_offset = curve_->Duration() * iteration_start_; - - // Return start offset if we are before the start of the keyframe model - if (active_time < base::TimeDelta()) - return start_offset; - // Always return zero if we have no iterations. - if (!iterations_) - return base::TimeDelta(); - - // Don't attempt to trim if we have no duration. - if (curve_->Duration() <= base::TimeDelta()) - return base::TimeDelta(); - - base::TimeDelta repeated_duration = std::isfinite(iterations_) - ? (curve_->Duration() * iterations_) - : base::TimeDelta::Max(); - - // Calculate the scaled active time - base::TimeDelta scaled_active_time; - if (playback_rate_ < 0) { - DCHECK(std::isfinite(iterations_)); - base::TimeDelta active_duration = - repeated_duration / std::abs(playback_rate_); - scaled_active_time = - ((active_time - active_duration) * playback_rate_) + start_offset; - } else { - scaled_active_time = (active_time * playback_rate_) + start_offset; - } - - // Calculate the iteration time - base::TimeDelta iteration_time; - bool has_defined_time_delta = - (start_offset != scaled_active_time) || - !(start_offset.is_max() || start_offset.is_min()); - if (has_defined_time_delta && - scaled_active_time - start_offset == repeated_duration && - fmod(iterations_ + iteration_start_, 1) == 0) - iteration_time = curve_->Duration(); - else - iteration_time = scaled_active_time % curve_->Duration(); - - // Calculate the current iteration - int iteration; - if (scaled_active_time <= base::TimeDelta()) - iteration = 0; - else if (iteration_time == curve_->Duration()) - iteration = ceil(iteration_start_ + iterations_ - 1); - else - iteration = base::ClampFloor(scaled_active_time / curve_->Duration()); - - // Check if we are running the keyframe model in reverse direction for the - // current iteration - bool reverse = - (direction_ == Direction::REVERSE) || - (direction_ == Direction::ALTERNATE_NORMAL && iteration % 2 == 1) || - (direction_ == Direction::ALTERNATE_REVERSE && iteration % 2 == 0); - - // If we are running the keyframe model in reverse direction, reverse the - // result - if (reverse) - iteration_time = curve_->Duration() - iteration_time; - - return iteration_time; -} - void KeyframeModel::PushPropertiesTo(KeyframeModel* other) const { other->element_id_ = element_id_; - if (run_state_ == KeyframeModel::PAUSED || - other->run_state_ == KeyframeModel::PAUSED) { - other->run_state_ = run_state_; - other->pause_time_ = pause_time_; - other->total_paused_duration_ = total_paused_duration_; + if (run_state() == KeyframeModel::PAUSED || + other->run_state() == KeyframeModel::PAUSED) { + other->ForceRunState(run_state()); + other->set_pause_time(pause_time()); + other->set_total_paused_duration(total_paused_duration()); } } std::string KeyframeModel::ToString() const { return base::StringPrintf( - "KeyframeModel{id=%d, group=%d, target_property_id=%d, " - "run_state=%s}", - id_, group_, target_property_id_, - KeyframeModel::ToString(run_state_).c_str()); + "KeyframeModel{id=%d, group=%d, target_property_type=%d, " + "custom_property_name=%s, native_property_type=%d, run_state=%s, " + "element_id=%s}", + id(), group_, TargetProperty(), + target_property_id_.custom_property_name().c_str(), + static_cast<int>(target_property_id_.native_property_type()), + gfx::KeyframeModel::ToString(run_state()).c_str(), + element_id_.ToString().c_str()); } void KeyframeModel::SetIsImplOnly() { @@ -362,4 +179,9 @@ void KeyframeModel::SetIsImplOnly() { // controlling instance. is_controlling_instance_ = true; } + +bool KeyframeModel::StartShouldBeDeferred() const { + return needs_synchronized_start_time_; +} + } // namespace cc diff --git a/chromium/cc/animation/keyframe_model.h b/chromium/cc/animation/keyframe_model.h index b7b52cd8cbf..22c2a9037f2 100644 --- a/chromium/cc/animation/keyframe_model.h +++ b/chromium/cc/animation/keyframe_model.h @@ -6,130 +6,87 @@ #define CC_ANIMATION_KEYFRAME_MODEL_H_ #include <memory> +#include <string> #include "base/optional.h" #include "base/time/time.h" #include "cc/animation/animation_export.h" #include "cc/paint/element_id.h" +#include "cc/paint/paint_worklet_input.h" +#include "ui/gfx/animation/keyframe/keyframe_model.h" namespace cc { -class AnimationCurve; - // A KeyframeModel contains all the state required to play an AnimationCurve. // Specifically, the affected property, the run state (paused, finished, etc.), // loop count, last pause time, and the total time spent paused. // It represents a model of the keyframes (internally represented as a curve). -class CC_ANIMATION_EXPORT KeyframeModel { +class CC_ANIMATION_EXPORT KeyframeModel : public gfx::KeyframeModel { public: - // KeyframeModels begin in the 'WAITING_FOR_TARGET_AVAILABILITY' state. A - // KeyframeModel waiting for target availibility will run as soon as its - // target property is free (and all the KeyframeModels animating with it are - // also able to run). When this time arrives, the controller will move the - // keyframe model into the STARTING state, and then into the RUNNING state. - // RUNNING KeyframeModels may toggle between RUNNING and PAUSED, and may be - // stopped by moving into either the ABORTED or FINISHED states. A FINISHED - // keyframe model was allowed to run to completion, but an ABORTED keyframe - // model was not. An animation in the state ABORTED_BUT_NEEDS_COMPLETION is a - // keyframe model that was aborted for some reason, but needs to be finished. - // Currently this is for impl-only scroll offset KeyframeModels that need to - // be completed on the main thread. - enum RunState { - WAITING_FOR_TARGET_AVAILABILITY = 0, - WAITING_FOR_DELETION, - STARTING, - RUNNING, - PAUSED, - FINISHED, - ABORTED, - ABORTED_BUT_NEEDS_COMPLETION, - // This sentinel must be last. - LAST_RUN_STATE = ABORTED_BUT_NEEDS_COMPLETION + static const KeyframeModel* ToCcKeyframeModel( + const gfx::KeyframeModel* keyframe_model); + + static KeyframeModel* ToCcKeyframeModel(gfx::KeyframeModel* keyframe_model); + + // Bundles a property id with its name and native type. + class CC_ANIMATION_EXPORT TargetPropertyId { + public: + // For a property that is neither TargetProperty::CSS_CUSTOM_PROPERTY nor + // TargetProperty::NATIVE_PROPERTY. + explicit TargetPropertyId(int target_property_type); + // For TargetProperty::CSS_CUSTOM_PROPERTY, the string is the custom + // property name. + TargetPropertyId(int target_property_type, + const std::string& custom_property_name); + // For TargetProperty::NATIVE_PROPERTY. + TargetPropertyId( + int target_property_type, + PaintWorkletInput::NativePropertyType native_property_type); + TargetPropertyId(const TargetPropertyId&); + ~TargetPropertyId(); + + int target_property_type() const { return target_property_type_; } + const std::string& custom_property_name() const { + return custom_property_name_; + } + PaintWorkletInput::NativePropertyType native_property_type() const { + return native_property_type_; + } + + private: + int target_property_type_; + // Name of the animated custom property. Empty if it is an animated native + // property. + std::string custom_property_name_; + // Type of the animated native property. + PaintWorkletInput::NativePropertyType native_property_type_; }; - static std::string ToString(RunState); - - enum class Direction { NORMAL, REVERSE, ALTERNATE_NORMAL, ALTERNATE_REVERSE }; - - enum class FillMode { NONE, FORWARDS, BACKWARDS, BOTH, AUTO }; - - enum class Phase { BEFORE, ACTIVE, AFTER }; - // The |custom_property_name| has a default value of an empty string, - // indicating that the animated property is a native property. When it is an - // animated custom property, it should be the property name. static std::unique_ptr<KeyframeModel> Create( - std::unique_ptr<AnimationCurve> curve, + std::unique_ptr<gfx::AnimationCurve> curve, int keyframe_model_id, int group_id, - int target_property_id, - const std::string& custom_property_name = ""); + TargetPropertyId target_property_id); std::unique_ptr<KeyframeModel> CreateImplInstance( RunState initial_run_state) const; KeyframeModel(const KeyframeModel&) = delete; - virtual ~KeyframeModel(); + ~KeyframeModel() override; KeyframeModel& operator=(const KeyframeModel&) = delete; - int id() const { return id_; } int group() const { return group_; } - int target_property_id() const { return target_property_id_; } - - ElementId element_id() const { return element_id_; } - void set_element_id(ElementId element_id) { element_id_ = element_id; } - - RunState run_state() const { return run_state_; } - void SetRunState(RunState run_state, base::TimeTicks monotonic_time); - - // This is the number of times that the keyframe model will play. If this - // value is zero the keyframe model will not play. If it is negative, then - // the keyframe model will loop indefinitely. - double iterations() const { return iterations_; } - void set_iterations(double n) { iterations_ = n; } - - double iteration_start() const { return iteration_start_; } - void set_iteration_start(double iteration_start) { - iteration_start_ = iteration_start; - } - - base::TimeTicks start_time() const { return start_time_; } - - void set_start_time(base::TimeTicks monotonic_time) { - start_time_ = monotonic_time; - } - bool has_set_start_time() const { return !start_time_.is_null(); } - - base::TimeDelta time_offset() const { return time_offset_; } - void set_time_offset(base::TimeDelta monotonic_time) { - time_offset_ = monotonic_time; - } - // Pause the keyframe effect at local time |pause_offset|. - void Pause(base::TimeDelta pause_offset); + int TargetProperty() const override; - Direction direction() { return direction_; } - void set_direction(Direction direction) { direction_ = direction; } + void SetRunState(RunState run_state, base::TimeTicks monotonic_time) override; - FillMode fill_mode() { return fill_mode_; } - void set_fill_mode(FillMode fill_mode) { fill_mode_ = fill_mode; } - - double playback_rate() { return playback_rate_; } - void set_playback_rate(double playback_rate) { - playback_rate_ = playback_rate; - } - - bool IsFinishedAt(base::TimeTicks monotonic_time) const; - bool is_finished() const { - return run_state_ == FINISHED || run_state_ == ABORTED || - run_state_ == WAITING_FOR_DELETION; - } + ElementId element_id() const { return element_id_; } + void set_element_id(ElementId element_id) { element_id_ = element_id; } bool InEffect(base::TimeTicks monotonic_time) const; - AnimationCurve* curve() { return curve_.get(); } - const AnimationCurve* curve() const { return curve_.get(); } - // If this is true, even if the keyframe model is running, it will not be // tickable until it is given a start time. This is true for KeyframeModels // running on the main thread. @@ -148,11 +105,6 @@ class CC_ANIMATION_EXPORT KeyframeModel { received_finished_event_ = received_finished_event; } - // Takes the given absolute time, and using the start time and the number - // of iterations, returns the relative time in the current iteration. - base::TimeDelta TrimTimeToCurrentIteration( - base::TimeTicks monotonic_time) const; - void set_is_controlling_instance_for_test(bool is_controlling_instance) { is_controlling_instance_ = is_controlling_instance; } @@ -176,90 +128,35 @@ class CC_ANIMATION_EXPORT KeyframeModel { bool affects_pending_elements() const { return affects_pending_elements_; } const std::string& custom_property_name() const { - return custom_property_name_; + return target_property_id_.custom_property_name(); } - KeyframeModel::Phase CalculatePhaseForTesting( - base::TimeDelta local_time) const; + PaintWorkletInput::NativePropertyType native_property_type() const { + return target_property_id_.native_property_type(); + } + + bool StartShouldBeDeferred() const override; private: - KeyframeModel(std::unique_ptr<AnimationCurve> curve, + KeyframeModel(std::unique_ptr<gfx::AnimationCurve> curve, int keyframe_model_id, int group_id, - int target_property_id, - const std::string& custom_property_name); - - // Return local time for this keyframe model given the absolute monotonic - // time. - // - // Local time represents the time value that is used to tick this keyframe - // model and is relative to its start time. It is closely related to the local - // time concept in web animations [1]. It is: - // - for playing animation : wall time - start time - paused duration - // - for paused animation : paused time - // - otherwise : zero - // - // Here is small diagram that shows how active, local, and monotonic times - // relate to each other and to the run state. - // - // run state Starting (R)unning Paused (R) Paused (R) Finished - // ^ ^ - // | | - // monotonic time -------------------------------------------------> - // | | - // local time +-----------------+ +---+ +---------> - // | | - // active time + +------+ +---+ +------+ - // (-offset) - // - // [1] https://drafts.csswg.org/web-animations/#local-time-section - base::TimeDelta ConvertMonotonicTimeToLocalTime( - base::TimeTicks monotonic_time) const; - - KeyframeModel::Phase CalculatePhase(base::TimeDelta local_time) const; - base::Optional<base::TimeDelta> CalculateActiveTime( - base::TimeTicks monotonic_time) const; - - std::unique_ptr<AnimationCurve> curve_; - - // IDs must be unique. - int id_; - + TargetPropertyId target_property_id); // KeyframeModels that must be run together are called 'grouped' and have the // same group id. Grouped KeyframeModels are guaranteed to start at the same // time and no other KeyframeModels may animate any of the group's target // properties until all KeyframeModels in the group have finished animating. int group_; + TargetPropertyId target_property_id_; + // If specified, overrides the ElementId to apply this KeyframeModel's effect // value on. ElementId element_id_; - int target_property_id_; - RunState run_state_; - double iterations_; - double iteration_start_; - base::TimeTicks start_time_; - Direction direction_; - double playback_rate_; - FillMode fill_mode_; - - // The time offset effectively pushes the start of the keyframe model back in - // time. This is used for resuming paused KeyframeModels -- an animation is - // added with a non-zero time offset, causing the keyframe model to skip ahead - // to the desired point in time. - base::TimeDelta time_offset_; - bool needs_synchronized_start_time_; bool received_finished_event_; - // These are used when converting monotonic to local time to account for time - // spent while paused. This is not included in AnimationState since it - // there is absolutely no need for clients of this controller to know - // about these values. - base::TimeTicks pause_time_; - base::TimeDelta total_paused_duration_; - // KeyframeModels lead dual lives. An active keyframe model will be // conceptually owned by two controllers, one on the impl thread and one on // the main. In reality, there will be two separate KeyframeModel instances @@ -287,10 +184,6 @@ class CC_ANIMATION_EXPORT KeyframeModel { // longer affect any elements, and are deleted. bool affects_active_elements_; bool affects_pending_elements_; - - // Name of the animated custom property. Empty if it is an animated native - // property. - std::string custom_property_name_; }; } // namespace cc diff --git a/chromium/cc/animation/keyframe_model_unittest.cc b/chromium/cc/animation/keyframe_model_unittest.cc index 2d4e94bde8d..1e01ad2ac47 100644 --- a/chromium/cc/animation/keyframe_model_unittest.cc +++ b/chromium/cc/animation/keyframe_model_unittest.cc @@ -4,6 +4,8 @@ #include "cc/animation/keyframe_model.h" +#include <limits> + #include "base/strings/stringprintf.h" #include "cc/test/animation_test_common.h" #include "cc/trees/target_property.h" @@ -22,9 +24,9 @@ static base::TimeTicks TicksFromSecondsF(double seconds) { std::unique_ptr<KeyframeModel> CreateKeyframeModel(double iterations, double duration, double playback_rate) { - std::unique_ptr<KeyframeModel> to_return( - KeyframeModel::Create(std::make_unique<FakeFloatAnimationCurve>(duration), - 0, 1, TargetProperty::OPACITY)); + std::unique_ptr<KeyframeModel> to_return(KeyframeModel::Create( + std::make_unique<FakeFloatAnimationCurve>(duration), 0, 1, + KeyframeModel::TargetPropertyId(TargetProperty::OPACITY))); to_return->set_iterations(iterations); to_return->set_playback_rate(playback_rate); return to_return; @@ -1381,27 +1383,29 @@ TEST(KeyframeModelTest, CalculatePhaseWithMinTimeOffset) { } TEST(KeyframeModelTest, ToString) { - std::unique_ptr<KeyframeModel> keyframe_model = - KeyframeModel::Create(std::make_unique<FakeFloatAnimationCurve>(15), 42, - 73, TargetProperty::OPACITY); - EXPECT_EQ( - base::StringPrintf("KeyframeModel{id=%d, group=73, target_property_id=1, " - "run_state=WAITING_FOR_TARGET_AVAILABILITY}", - keyframe_model->id()), - keyframe_model->ToString()); + std::unique_ptr<KeyframeModel> keyframe_model = KeyframeModel::Create( + std::make_unique<FakeFloatAnimationCurve>(15), 42, 73, + KeyframeModel::TargetPropertyId(TargetProperty::OPACITY)); + EXPECT_EQ(base::StringPrintf( + "KeyframeModel{id=%d, group=73, target_property_type=1, " + "custom_property_name=, native_property_type=1, " + "run_state=WAITING_FOR_TARGET_AVAILABILITY, element_id=(0)}", + keyframe_model->id()), + keyframe_model->ToString()); } TEST(KeyframeModelTest, CustomPropertyKeyframe) { std::unique_ptr<KeyframeModel> keyframe_model = KeyframeModel::Create(std::make_unique<FakeFloatAnimationCurve>(1), 1, 1, - TargetProperty::CSS_CUSTOM_PROPERTY, "foo"); + KeyframeModel::TargetPropertyId( + TargetProperty::CSS_CUSTOM_PROPERTY, "foo")); EXPECT_EQ(keyframe_model->custom_property_name(), "foo"); } TEST(KeyframeModelTest, NonCustomPropertyKeyframe) { - std::unique_ptr<KeyframeModel> keyframe_model = - KeyframeModel::Create(std::make_unique<FakeFloatAnimationCurve>(1), 1, 1, - TargetProperty::TRANSFORM); + std::unique_ptr<KeyframeModel> keyframe_model = KeyframeModel::Create( + std::make_unique<FakeFloatAnimationCurve>(1), 1, 1, + KeyframeModel::TargetPropertyId(TargetProperty::TRANSFORM)); EXPECT_EQ(keyframe_model->custom_property_name(), ""); } diff --git a/chromium/cc/animation/keyframed_animation_curve.cc b/chromium/cc/animation/keyframed_animation_curve.cc deleted file mode 100644 index ce679e5d8e8..00000000000 --- a/chromium/cc/animation/keyframed_animation_curve.cc +++ /dev/null @@ -1,548 +0,0 @@ -// Copyright 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "cc/animation/keyframed_animation_curve.h" - -#include <stddef.h> - -#include <algorithm> -#include <memory> -#include <utility> - -#include "base/memory/ptr_util.h" -#include "ui/gfx/animation/tween.h" -#include "ui/gfx/geometry/box_f.h" - -namespace cc { - -namespace { - -template <class KeyframeType> -void InsertKeyframe(std::unique_ptr<KeyframeType> keyframe, - std::vector<std::unique_ptr<KeyframeType>>* keyframes) { - // Usually, the keyframes will be added in order, so this loop would be - // unnecessary and we should skip it if possible. - if (!keyframes->empty() && keyframe->Time() < keyframes->back()->Time()) { - for (size_t i = 0; i < keyframes->size(); ++i) { - if (keyframe->Time() < keyframes->at(i)->Time()) { - keyframes->insert(keyframes->begin() + i, std::move(keyframe)); - return; - } - } - } - - keyframes->push_back(std::move(keyframe)); -} - -template <typename KeyframeType> -base::TimeDelta TransformedAnimationTime( - const std::vector<std::unique_ptr<KeyframeType>>& keyframes, - const std::unique_ptr<TimingFunction>& timing_function, - double scaled_duration, - base::TimeDelta time) { - if (timing_function) { - base::TimeDelta start_time = keyframes.front()->Time() * scaled_duration; - base::TimeDelta duration = - (keyframes.back()->Time() - keyframes.front()->Time()) * - scaled_duration; - const double progress = - duration.is_zero() ? 1.0 : ((time - start_time) / duration); - - time = (duration * timing_function->GetValue(progress)) + start_time; - } - - return time; -} - -template <typename KeyframeType> -size_t GetActiveKeyframe( - const std::vector<std::unique_ptr<KeyframeType>>& keyframes, - double scaled_duration, - base::TimeDelta time) { - DCHECK_GE(keyframes.size(), 2ul); - size_t i = 0; - while ((i < keyframes.size() - 2) && // Last keyframe is never active. - (time >= (keyframes[i + 1]->Time() * scaled_duration))) - ++i; - - return i; -} - -template <typename KeyframeType> -double TransformedKeyframeProgress( - const std::vector<std::unique_ptr<KeyframeType>>& keyframes, - double scaled_duration, - base::TimeDelta time, - size_t i) { - const base::TimeDelta start_time = keyframes[i]->Time() * scaled_duration; - const base::TimeDelta duration = - keyframes[i + 1]->Time() * scaled_duration - start_time; - const double progress = - duration.is_zero() ? 1.0 : ((time - start_time) / duration); - - return keyframes[i]->timing_function() - ? keyframes[i]->timing_function()->GetValue(progress) - : progress; -} - -} // namespace - -Keyframe::Keyframe(base::TimeDelta time, - std::unique_ptr<TimingFunction> timing_function) - : time_(time), timing_function_(std::move(timing_function)) {} - -Keyframe::~Keyframe() = default; - -base::TimeDelta Keyframe::Time() const { - return time_; -} - -std::unique_ptr<ColorKeyframe> ColorKeyframe::Create( - base::TimeDelta time, - SkColor value, - std::unique_ptr<TimingFunction> timing_function) { - return base::WrapUnique( - new ColorKeyframe(time, value, std::move(timing_function))); -} - -ColorKeyframe::ColorKeyframe(base::TimeDelta time, - SkColor value, - std::unique_ptr<TimingFunction> timing_function) - : Keyframe(time, std::move(timing_function)), value_(value) {} - -ColorKeyframe::~ColorKeyframe() = default; - -SkColor ColorKeyframe::Value() const { return value_; } - -std::unique_ptr<ColorKeyframe> ColorKeyframe::Clone() const { - std::unique_ptr<TimingFunction> func; - if (timing_function()) - func = timing_function()->Clone(); - return ColorKeyframe::Create(Time(), Value(), std::move(func)); -} - -std::unique_ptr<FloatKeyframe> FloatKeyframe::Create( - base::TimeDelta time, - float value, - std::unique_ptr<TimingFunction> timing_function) { - return base::WrapUnique( - new FloatKeyframe(time, value, std::move(timing_function))); -} - -FloatKeyframe::FloatKeyframe(base::TimeDelta time, - float value, - std::unique_ptr<TimingFunction> timing_function) - : Keyframe(time, std::move(timing_function)), value_(value) {} - -FloatKeyframe::~FloatKeyframe() = default; - -float FloatKeyframe::Value() const { - return value_; -} - -std::unique_ptr<FloatKeyframe> FloatKeyframe::Clone() const { - std::unique_ptr<TimingFunction> func; - if (timing_function()) - func = timing_function()->Clone(); - return FloatKeyframe::Create(Time(), Value(), std::move(func)); -} - -std::unique_ptr<TransformKeyframe> TransformKeyframe::Create( - base::TimeDelta time, - const TransformOperations& value, - std::unique_ptr<TimingFunction> timing_function) { - return base::WrapUnique( - new TransformKeyframe(time, value, std::move(timing_function))); -} - -TransformKeyframe::TransformKeyframe( - base::TimeDelta time, - const TransformOperations& value, - std::unique_ptr<TimingFunction> timing_function) - : Keyframe(time, std::move(timing_function)), value_(value) {} - -TransformKeyframe::~TransformKeyframe() = default; - -const TransformOperations& TransformKeyframe::Value() const { - return value_; -} - -std::unique_ptr<TransformKeyframe> TransformKeyframe::Clone() const { - std::unique_ptr<TimingFunction> func; - if (timing_function()) - func = timing_function()->Clone(); - return TransformKeyframe::Create(Time(), Value(), std::move(func)); -} - -std::unique_ptr<FilterKeyframe> FilterKeyframe::Create( - base::TimeDelta time, - const FilterOperations& value, - std::unique_ptr<TimingFunction> timing_function) { - return base::WrapUnique( - new FilterKeyframe(time, value, std::move(timing_function))); -} - -FilterKeyframe::FilterKeyframe(base::TimeDelta time, - const FilterOperations& value, - std::unique_ptr<TimingFunction> timing_function) - : Keyframe(time, std::move(timing_function)), value_(value) {} - -FilterKeyframe::~FilterKeyframe() = default; - -const FilterOperations& FilterKeyframe::Value() const { - return value_; -} - -std::unique_ptr<FilterKeyframe> FilterKeyframe::Clone() const { - std::unique_ptr<TimingFunction> func; - if (timing_function()) - func = timing_function()->Clone(); - return FilterKeyframe::Create(Time(), Value(), std::move(func)); -} - -std::unique_ptr<SizeKeyframe> SizeKeyframe::Create( - base::TimeDelta time, - const gfx::SizeF& value, - std::unique_ptr<TimingFunction> timing_function) { - return base::WrapUnique( - new SizeKeyframe(time, value, std::move(timing_function))); -} - -SizeKeyframe::SizeKeyframe(base::TimeDelta time, - const gfx::SizeF& value, - std::unique_ptr<TimingFunction> timing_function) - : Keyframe(time, std::move(timing_function)), value_(value) {} - -SizeKeyframe::~SizeKeyframe() = default; - -const gfx::SizeF& SizeKeyframe::Value() const { - return value_; -} - -std::unique_ptr<SizeKeyframe> SizeKeyframe::Clone() const { - std::unique_ptr<TimingFunction> func; - if (timing_function()) - func = timing_function()->Clone(); - return SizeKeyframe::Create(Time(), Value(), std::move(func)); -} - -std::unique_ptr<KeyframedColorAnimationCurve> -KeyframedColorAnimationCurve::Create() { - return base::WrapUnique(new KeyframedColorAnimationCurve); -} - -KeyframedColorAnimationCurve::KeyframedColorAnimationCurve() - : scaled_duration_(1.0) {} - -KeyframedColorAnimationCurve::~KeyframedColorAnimationCurve() = default; - -void KeyframedColorAnimationCurve::AddKeyframe( - std::unique_ptr<ColorKeyframe> keyframe) { - InsertKeyframe(std::move(keyframe), &keyframes_); -} - -base::TimeDelta KeyframedColorAnimationCurve::Duration() const { - return (keyframes_.back()->Time() - keyframes_.front()->Time()) * - scaled_duration(); -} - -std::unique_ptr<AnimationCurve> KeyframedColorAnimationCurve::Clone() const { - std::unique_ptr<KeyframedColorAnimationCurve> to_return = - KeyframedColorAnimationCurve::Create(); - for (const auto& keyframe : keyframes_) - to_return->AddKeyframe(keyframe->Clone()); - - if (timing_function_) - to_return->SetTimingFunction(timing_function_->Clone()); - - to_return->set_scaled_duration(scaled_duration()); - - return std::move(to_return); -} - -SkColor KeyframedColorAnimationCurve::GetValue(base::TimeDelta t) const { - if (t <= (keyframes_.front()->Time() * scaled_duration())) - return keyframes_.front()->Value(); - - if (t >= (keyframes_.back()->Time() * scaled_duration())) - return keyframes_.back()->Value(); - - t = TransformedAnimationTime(keyframes_, timing_function_, scaled_duration(), - t); - size_t i = GetActiveKeyframe(keyframes_, scaled_duration(), t); - double progress = - TransformedKeyframeProgress(keyframes_, scaled_duration(), t, i); - - return gfx::Tween::ColorValueBetween( - progress, keyframes_[i]->Value(), keyframes_[i + 1]->Value()); -} - -std::unique_ptr<KeyframedFloatAnimationCurve> -KeyframedFloatAnimationCurve::Create() { - return base::WrapUnique(new KeyframedFloatAnimationCurve); -} - -KeyframedFloatAnimationCurve::KeyframedFloatAnimationCurve() - : scaled_duration_(1.0) {} - -KeyframedFloatAnimationCurve::~KeyframedFloatAnimationCurve() = default; - -void KeyframedFloatAnimationCurve::AddKeyframe( - std::unique_ptr<FloatKeyframe> keyframe) { - InsertKeyframe(std::move(keyframe), &keyframes_); -} - -base::TimeDelta KeyframedFloatAnimationCurve::Duration() const { - return (keyframes_.back()->Time() - keyframes_.front()->Time()) * - scaled_duration(); -} - -std::unique_ptr<AnimationCurve> KeyframedFloatAnimationCurve::Clone() const { - std::unique_ptr<KeyframedFloatAnimationCurve> to_return = - KeyframedFloatAnimationCurve::Create(); - for (const auto& keyframe : keyframes_) - to_return->AddKeyframe(keyframe->Clone()); - - if (timing_function_) - to_return->SetTimingFunction(timing_function_->Clone()); - - to_return->set_scaled_duration(scaled_duration()); - - return std::move(to_return); -} - -float KeyframedFloatAnimationCurve::GetValue(base::TimeDelta t) const { - if (t <= (keyframes_.front()->Time() * scaled_duration())) - return keyframes_.front()->Value(); - - if (t >= (keyframes_.back()->Time() * scaled_duration())) - return keyframes_.back()->Value(); - - t = TransformedAnimationTime(keyframes_, timing_function_, scaled_duration(), - t); - size_t i = GetActiveKeyframe(keyframes_, scaled_duration(), t); - double progress = - TransformedKeyframeProgress(keyframes_, scaled_duration(), t, i); - - return keyframes_[i]->Value() + - (keyframes_[i+1]->Value() - keyframes_[i]->Value()) * progress; -} - -std::unique_ptr<KeyframedTransformAnimationCurve> -KeyframedTransformAnimationCurve::Create() { - return base::WrapUnique(new KeyframedTransformAnimationCurve); -} - -KeyframedTransformAnimationCurve::KeyframedTransformAnimationCurve() - : scaled_duration_(1.0) {} - -KeyframedTransformAnimationCurve::~KeyframedTransformAnimationCurve() = default; - -void KeyframedTransformAnimationCurve::AddKeyframe( - std::unique_ptr<TransformKeyframe> keyframe) { - InsertKeyframe(std::move(keyframe), &keyframes_); -} - -base::TimeDelta KeyframedTransformAnimationCurve::Duration() const { - return (keyframes_.back()->Time() - keyframes_.front()->Time()) * - scaled_duration(); -} - -std::unique_ptr<AnimationCurve> KeyframedTransformAnimationCurve::Clone() - const { - std::unique_ptr<KeyframedTransformAnimationCurve> to_return = - KeyframedTransformAnimationCurve::Create(); - for (const auto& keyframe : keyframes_) - to_return->AddKeyframe(keyframe->Clone()); - - if (timing_function_) - to_return->SetTimingFunction(timing_function_->Clone()); - - to_return->set_scaled_duration(scaled_duration()); - - return std::move(to_return); -} - -TransformOperations KeyframedTransformAnimationCurve::GetValue( - base::TimeDelta t) const { - if (t <= (keyframes_.front()->Time() * scaled_duration())) - return keyframes_.front()->Value(); - - if (t >= (keyframes_.back()->Time() * scaled_duration())) - return keyframes_.back()->Value(); - - t = TransformedAnimationTime(keyframes_, timing_function_, scaled_duration(), - t); - size_t i = GetActiveKeyframe(keyframes_, scaled_duration(), t); - double progress = - TransformedKeyframeProgress(keyframes_, scaled_duration(), t, i); - - return keyframes_[i + 1]->Value().Blend(keyframes_[i]->Value(), progress); -} - -bool KeyframedTransformAnimationCurve::PreservesAxisAlignment() const { - for (const auto& keyframe : keyframes_) { - if (!keyframe->Value().PreservesAxisAlignment()) - return false; - } - return true; -} - -bool KeyframedTransformAnimationCurve::IsTranslation() const { - for (const auto& keyframe : keyframes_) { - if (!keyframe->Value().IsTranslation() && !keyframe->Value().IsIdentity()) - return false; - } - return true; -} - -bool KeyframedTransformAnimationCurve::AnimationStartScale( - bool forward_direction, - float* start_scale) const { - DCHECK_GE(keyframes_.size(), 2ul); - *start_scale = 0.f; - size_t start_location = 0; - if (!forward_direction) { - start_location = keyframes_.size() - 1; - } - - return keyframes_[start_location]->Value().ScaleComponent(start_scale); -} - -bool KeyframedTransformAnimationCurve::MaximumTargetScale( - bool forward_direction, - float* max_scale) const { - DCHECK_GE(keyframes_.size(), 2ul); - *max_scale = 0.f; - - // If |forward_direction| is true, then skip the first frame, otherwise - // skip the last frame, since that is the original position in the animation. - size_t start = 1; - size_t end = keyframes_.size(); - if (!forward_direction) { - --start; - --end; - } - - for (size_t i = start; i < end; ++i) { - float target_scale_for_segment = 0.f; - if (!keyframes_[i]->Value().ScaleComponent(&target_scale_for_segment)) - return false; - *max_scale = fmax(*max_scale, target_scale_for_segment); - } - return true; -} - -std::unique_ptr<KeyframedFilterAnimationCurve> -KeyframedFilterAnimationCurve::Create() { - return base::WrapUnique(new KeyframedFilterAnimationCurve); -} - -KeyframedFilterAnimationCurve::KeyframedFilterAnimationCurve() - : scaled_duration_(1.0) {} - -KeyframedFilterAnimationCurve::~KeyframedFilterAnimationCurve() = default; - -void KeyframedFilterAnimationCurve::AddKeyframe( - std::unique_ptr<FilterKeyframe> keyframe) { - InsertKeyframe(std::move(keyframe), &keyframes_); -} - -base::TimeDelta KeyframedFilterAnimationCurve::Duration() const { - return (keyframes_.back()->Time() - keyframes_.front()->Time()) * - scaled_duration(); -} - -std::unique_ptr<AnimationCurve> KeyframedFilterAnimationCurve::Clone() const { - std::unique_ptr<KeyframedFilterAnimationCurve> to_return = - KeyframedFilterAnimationCurve::Create(); - for (const auto& keyframe : keyframes_) - to_return->AddKeyframe(keyframe->Clone()); - - if (timing_function_) - to_return->SetTimingFunction(timing_function_->Clone()); - - to_return->set_scaled_duration(scaled_duration()); - - return std::move(to_return); -} - -FilterOperations KeyframedFilterAnimationCurve::GetValue( - base::TimeDelta t) const { - if (t <= (keyframes_.front()->Time() * scaled_duration())) - return keyframes_.front()->Value(); - - if (t >= (keyframes_.back()->Time() * scaled_duration())) - return keyframes_.back()->Value(); - - t = TransformedAnimationTime(keyframes_, timing_function_, scaled_duration(), - t); - size_t i = GetActiveKeyframe(keyframes_, scaled_duration(), t); - double progress = - TransformedKeyframeProgress(keyframes_, scaled_duration(), t, i); - - return keyframes_[i + 1]->Value().Blend(keyframes_[i]->Value(), progress); -} - -bool KeyframedFilterAnimationCurve::HasFilterThatMovesPixels() const { - for (const auto& keyframe : keyframes_) { - if (keyframe->Value().HasFilterThatMovesPixels()) { - return true; - } - } - return false; -} - -std::unique_ptr<KeyframedSizeAnimationCurve> -KeyframedSizeAnimationCurve::Create() { - return base::WrapUnique(new KeyframedSizeAnimationCurve); -} - -KeyframedSizeAnimationCurve::KeyframedSizeAnimationCurve() - : scaled_duration_(1.0) {} - -KeyframedSizeAnimationCurve::~KeyframedSizeAnimationCurve() = default; - -void KeyframedSizeAnimationCurve::AddKeyframe( - std::unique_ptr<SizeKeyframe> keyframe) { - InsertKeyframe(std::move(keyframe), &keyframes_); -} - -base::TimeDelta KeyframedSizeAnimationCurve::Duration() const { - return (keyframes_.back()->Time() - keyframes_.front()->Time()) * - scaled_duration(); -} - -std::unique_ptr<AnimationCurve> KeyframedSizeAnimationCurve::Clone() const { - std::unique_ptr<KeyframedSizeAnimationCurve> to_return = - KeyframedSizeAnimationCurve::Create(); - for (const auto& keyframe : keyframes_) - to_return->AddKeyframe(keyframe->Clone()); - - if (timing_function_) - to_return->SetTimingFunction(timing_function_->Clone()); - - to_return->set_scaled_duration(scaled_duration()); - - return std::move(to_return); -} - -gfx::SizeF KeyframedSizeAnimationCurve::GetValue(base::TimeDelta t) const { - if (t <= (keyframes_.front()->Time() * scaled_duration())) - return keyframes_.front()->Value(); - - if (t >= (keyframes_.back()->Time() * scaled_duration())) - return keyframes_.back()->Value(); - - t = TransformedAnimationTime(keyframes_, timing_function_, scaled_duration(), - t); - size_t i = GetActiveKeyframe(keyframes_, scaled_duration(), t); - double progress = - TransformedKeyframeProgress(keyframes_, scaled_duration(), t, i); - - return gfx::Tween::SizeFValueBetween(progress, keyframes_[i]->Value(), - keyframes_[i + 1]->Value()); -} - -} // namespace cc diff --git a/chromium/cc/animation/keyframed_animation_curve.h b/chromium/cc/animation/keyframed_animation_curve.h deleted file mode 100644 index c843fc7f782..00000000000 --- a/chromium/cc/animation/keyframed_animation_curve.h +++ /dev/null @@ -1,349 +0,0 @@ -// Copyright 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CC_ANIMATION_KEYFRAMED_ANIMATION_CURVE_H_ -#define CC_ANIMATION_KEYFRAMED_ANIMATION_CURVE_H_ - -#include <vector> - -#include "base/time/time.h" -#include "cc/animation/animation_curve.h" -#include "cc/animation/animation_export.h" -#include "cc/animation/timing_function.h" -#include "cc/animation/transform_operations.h" -#include "ui/gfx/geometry/size_f.h" - -namespace cc { - -class CC_ANIMATION_EXPORT Keyframe { - public: - Keyframe(const Keyframe&) = delete; - Keyframe& operator=(const Keyframe&) = delete; - - base::TimeDelta Time() const; - const TimingFunction* timing_function() const { - return timing_function_.get(); - } - - protected: - Keyframe(base::TimeDelta time, - std::unique_ptr<TimingFunction> timing_function); - virtual ~Keyframe(); - - private: - base::TimeDelta time_; - std::unique_ptr<TimingFunction> timing_function_; -}; - -class CC_ANIMATION_EXPORT ColorKeyframe : public Keyframe { - public: - static std::unique_ptr<ColorKeyframe> Create( - base::TimeDelta time, - SkColor value, - std::unique_ptr<TimingFunction> timing_function); - ~ColorKeyframe() override; - - SkColor Value() const; - - std::unique_ptr<ColorKeyframe> Clone() const; - - private: - ColorKeyframe(base::TimeDelta time, - SkColor value, - std::unique_ptr<TimingFunction> timing_function); - - SkColor value_; -}; - -class CC_ANIMATION_EXPORT FloatKeyframe : public Keyframe { - public: - static std::unique_ptr<FloatKeyframe> Create( - base::TimeDelta time, - float value, - std::unique_ptr<TimingFunction> timing_function); - ~FloatKeyframe() override; - - float Value() const; - - std::unique_ptr<FloatKeyframe> Clone() const; - - private: - FloatKeyframe(base::TimeDelta time, - float value, - std::unique_ptr<TimingFunction> timing_function); - - float value_; -}; - -class CC_ANIMATION_EXPORT TransformKeyframe : public Keyframe { - public: - static std::unique_ptr<TransformKeyframe> Create( - base::TimeDelta time, - const TransformOperations& value, - std::unique_ptr<TimingFunction> timing_function); - ~TransformKeyframe() override; - - const TransformOperations& Value() const; - - std::unique_ptr<TransformKeyframe> Clone() const; - - private: - TransformKeyframe(base::TimeDelta time, - const TransformOperations& value, - std::unique_ptr<TimingFunction> timing_function); - - TransformOperations value_; -}; - -class CC_ANIMATION_EXPORT FilterKeyframe : public Keyframe { - public: - static std::unique_ptr<FilterKeyframe> Create( - base::TimeDelta time, - const FilterOperations& value, - std::unique_ptr<TimingFunction> timing_function); - ~FilterKeyframe() override; - - const FilterOperations& Value() const; - - std::unique_ptr<FilterKeyframe> Clone() const; - - private: - FilterKeyframe(base::TimeDelta time, - const FilterOperations& value, - std::unique_ptr<TimingFunction> timing_function); - - FilterOperations value_; -}; - -class CC_ANIMATION_EXPORT SizeKeyframe : public Keyframe { - public: - static std::unique_ptr<SizeKeyframe> Create( - base::TimeDelta time, - const gfx::SizeF& bounds, - std::unique_ptr<TimingFunction> timing_function); - ~SizeKeyframe() override; - - const gfx::SizeF& Value() const; - - std::unique_ptr<SizeKeyframe> Clone() const; - - private: - SizeKeyframe(base::TimeDelta time, - const gfx::SizeF& value, - std::unique_ptr<TimingFunction> timing_function); - - gfx::SizeF value_; -}; - -class CC_ANIMATION_EXPORT KeyframedColorAnimationCurve - : public ColorAnimationCurve { - public: - // It is required that the keyframes be sorted by time. - static std::unique_ptr<KeyframedColorAnimationCurve> Create(); - - KeyframedColorAnimationCurve(const KeyframedColorAnimationCurve&) = delete; - ~KeyframedColorAnimationCurve() override; - - KeyframedColorAnimationCurve& operator=(const KeyframedColorAnimationCurve&) = - delete; - - void AddKeyframe(std::unique_ptr<ColorKeyframe> keyframe); - void SetTimingFunction(std::unique_ptr<TimingFunction> timing_function) { - timing_function_ = std::move(timing_function); - } - double scaled_duration() const { return scaled_duration_; } - void set_scaled_duration(double scaled_duration) { - scaled_duration_ = scaled_duration; - } - - // AnimationCurve implementation - base::TimeDelta Duration() const override; - std::unique_ptr<AnimationCurve> Clone() const override; - - // BackgrounColorAnimationCurve implementation - SkColor GetValue(base::TimeDelta t) const override; - - using Keyframes = std::vector<std::unique_ptr<ColorKeyframe>>; - const Keyframes& keyframes_for_testing() const { return keyframes_; } - - private: - KeyframedColorAnimationCurve(); - - // Always sorted in order of increasing time. No two keyframes have the - // same time. - Keyframes keyframes_; - std::unique_ptr<TimingFunction> timing_function_; - double scaled_duration_; -}; - -class CC_ANIMATION_EXPORT KeyframedFloatAnimationCurve - : public FloatAnimationCurve { - public: - // It is required that the keyframes be sorted by time. - static std::unique_ptr<KeyframedFloatAnimationCurve> Create(); - - KeyframedFloatAnimationCurve(const KeyframedFloatAnimationCurve&) = delete; - ~KeyframedFloatAnimationCurve() override; - - KeyframedFloatAnimationCurve& operator=(const KeyframedFloatAnimationCurve&) = - delete; - - void AddKeyframe(std::unique_ptr<FloatKeyframe> keyframe); - - void SetTimingFunction(std::unique_ptr<TimingFunction> timing_function) { - timing_function_ = std::move(timing_function); - } - TimingFunction* timing_function_for_testing() const { - return timing_function_.get(); - } - double scaled_duration() const { return scaled_duration_; } - void set_scaled_duration(double scaled_duration) { - scaled_duration_ = scaled_duration; - } - - // AnimationCurve implementation - base::TimeDelta Duration() const override; - std::unique_ptr<AnimationCurve> Clone() const override; - - // FloatAnimationCurve implementation - float GetValue(base::TimeDelta t) const override; - - using Keyframes = std::vector<std::unique_ptr<FloatKeyframe>>; - const Keyframes& keyframes_for_testing() const { return keyframes_; } - - private: - KeyframedFloatAnimationCurve(); - - // Always sorted in order of increasing time. No two keyframes have the - // same time. - Keyframes keyframes_; - std::unique_ptr<TimingFunction> timing_function_; - double scaled_duration_; -}; - -class CC_ANIMATION_EXPORT KeyframedTransformAnimationCurve - : public TransformAnimationCurve { - public: - // It is required that the keyframes be sorted by time. - static std::unique_ptr<KeyframedTransformAnimationCurve> Create(); - - KeyframedTransformAnimationCurve(const KeyframedTransformAnimationCurve&) = - delete; - ~KeyframedTransformAnimationCurve() override; - - KeyframedTransformAnimationCurve& operator=( - const KeyframedTransformAnimationCurve&) = delete; - - void AddKeyframe(std::unique_ptr<TransformKeyframe> keyframe); - void SetTimingFunction(std::unique_ptr<TimingFunction> timing_function) { - timing_function_ = std::move(timing_function); - } - double scaled_duration() const { return scaled_duration_; } - void set_scaled_duration(double scaled_duration) { - scaled_duration_ = scaled_duration; - } - - // AnimationCurve implementation - base::TimeDelta Duration() const override; - std::unique_ptr<AnimationCurve> Clone() const override; - - // TransformAnimationCurve implementation - TransformOperations GetValue(base::TimeDelta t) const override; - bool PreservesAxisAlignment() const override; - bool IsTranslation() const override; - bool AnimationStartScale(bool forward_direction, - float* start_scale) const override; - bool MaximumTargetScale(bool forward_direction, - float* max_scale) const override; - - private: - KeyframedTransformAnimationCurve(); - - // Always sorted in order of increasing time. No two keyframes have the - // same time. - std::vector<std::unique_ptr<TransformKeyframe>> keyframes_; - std::unique_ptr<TimingFunction> timing_function_; - double scaled_duration_; -}; - -class CC_ANIMATION_EXPORT KeyframedFilterAnimationCurve - : public FilterAnimationCurve { - public: - // It is required that the keyframes be sorted by time. - static std::unique_ptr<KeyframedFilterAnimationCurve> Create(); - - KeyframedFilterAnimationCurve(const KeyframedFilterAnimationCurve&) = delete; - ~KeyframedFilterAnimationCurve() override; - - KeyframedFilterAnimationCurve& operator=( - const KeyframedFilterAnimationCurve&) = delete; - - void AddKeyframe(std::unique_ptr<FilterKeyframe> keyframe); - void SetTimingFunction(std::unique_ptr<TimingFunction> timing_function) { - timing_function_ = std::move(timing_function); - } - double scaled_duration() const { return scaled_duration_; } - void set_scaled_duration(double scaled_duration) { - scaled_duration_ = scaled_duration; - } - - // AnimationCurve implementation - base::TimeDelta Duration() const override; - std::unique_ptr<AnimationCurve> Clone() const override; - - // FilterAnimationCurve implementation - FilterOperations GetValue(base::TimeDelta t) const override; - bool HasFilterThatMovesPixels() const override; - - private: - KeyframedFilterAnimationCurve(); - - // Always sorted in order of increasing time. No two keyframes have the - // same time. - std::vector<std::unique_ptr<FilterKeyframe>> keyframes_; - std::unique_ptr<TimingFunction> timing_function_; - double scaled_duration_; -}; - -class CC_ANIMATION_EXPORT KeyframedSizeAnimationCurve - : public SizeAnimationCurve { - public: - // It is required that the keyframes be sorted by time. - static std::unique_ptr<KeyframedSizeAnimationCurve> Create(); - - KeyframedSizeAnimationCurve(const KeyframedSizeAnimationCurve&) = delete; - ~KeyframedSizeAnimationCurve() override; - - KeyframedSizeAnimationCurve& operator=(const KeyframedSizeAnimationCurve&) = - delete; - - void AddKeyframe(std::unique_ptr<SizeKeyframe> keyframe); - void SetTimingFunction(std::unique_ptr<TimingFunction> timing_function) { - timing_function_ = std::move(timing_function); - } - double scaled_duration() const { return scaled_duration_; } - void set_scaled_duration(double scaled_duration) { - scaled_duration_ = scaled_duration; - } - - // AnimationCurve implementation - base::TimeDelta Duration() const override; - std::unique_ptr<AnimationCurve> Clone() const override; - - // SizeAnimationCurve implementation - gfx::SizeF GetValue(base::TimeDelta t) const override; - - private: - KeyframedSizeAnimationCurve(); - - // Always sorted in order of increasing time. No two keyframes have the - // same time. - std::vector<std::unique_ptr<SizeKeyframe>> keyframes_; - std::unique_ptr<TimingFunction> timing_function_; - double scaled_duration_; -}; - -} // namespace cc - -#endif // CC_ANIMATION_KEYFRAMED_ANIMATION_CURVE_H_ diff --git a/chromium/cc/animation/keyframed_animation_curve_unittest.cc b/chromium/cc/animation/keyframed_animation_curve_unittest.cc deleted file mode 100644 index ba2cfbac84c..00000000000 --- a/chromium/cc/animation/keyframed_animation_curve_unittest.cc +++ /dev/null @@ -1,1093 +0,0 @@ -// Copyright 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "cc/animation/keyframed_animation_curve.h" - -#include "cc/animation/transform_operations.h" -#include "cc/test/geometry_test_utils.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "ui/gfx/animation/tween.h" -#include "ui/gfx/geometry/box_f.h" -#include "ui/gfx/test/gfx_util.h" - -namespace cc { -namespace { - -void ExpectTranslateX(SkScalar translate_x, - const TransformOperations& operations) { - EXPECT_FLOAT_EQ(translate_x, operations.Apply().matrix().get(0, 3)); -} - -void ExpectBrightness(double brightness, const FilterOperations& filter) { - EXPECT_EQ(1u, filter.size()); - EXPECT_EQ(FilterOperation::BRIGHTNESS, filter.at(0).type()); - EXPECT_FLOAT_EQ(brightness, filter.at(0).amount()); -} - -// Tests that a color animation with one keyframe works as expected. -TEST(KeyframedAnimationCurveTest, OneColorKeyFrame) { - SkColor color = SkColorSetARGB(255, 255, 255, 255); - std::unique_ptr<KeyframedColorAnimationCurve> curve( - KeyframedColorAnimationCurve::Create()); - curve->AddKeyframe(ColorKeyframe::Create(base::TimeDelta(), color, nullptr)); - - EXPECT_SKCOLOR_EQ(color, - curve->GetValue(base::TimeDelta::FromSecondsD(-1.f))); - EXPECT_SKCOLOR_EQ(color, curve->GetValue(base::TimeDelta::FromSecondsD(0.f))); - EXPECT_SKCOLOR_EQ(color, - curve->GetValue(base::TimeDelta::FromSecondsD(0.5f))); - EXPECT_SKCOLOR_EQ(color, curve->GetValue(base::TimeDelta::FromSecondsD(1.f))); - EXPECT_SKCOLOR_EQ(color, curve->GetValue(base::TimeDelta::FromSecondsD(2.f))); -} - -// Tests that a color animation with two keyframes works as expected. -TEST(KeyframedAnimationCurveTest, TwoColorKeyFrame) { - SkColor color_a = SkColorSetARGB(255, 255, 0, 0); - SkColor color_b = SkColorSetARGB(255, 0, 255, 0); - SkColor color_midpoint = gfx::Tween::ColorValueBetween(0.5, color_a, color_b); - std::unique_ptr<KeyframedColorAnimationCurve> curve( - KeyframedColorAnimationCurve::Create()); - curve->AddKeyframe( - ColorKeyframe::Create(base::TimeDelta(), color_a, nullptr)); - curve->AddKeyframe(ColorKeyframe::Create(base::TimeDelta::FromSecondsD(1.0), - color_b, nullptr)); - - EXPECT_SKCOLOR_EQ(color_a, - curve->GetValue(base::TimeDelta::FromSecondsD(-1.f))); - EXPECT_SKCOLOR_EQ(color_a, - curve->GetValue(base::TimeDelta::FromSecondsD(0.f))); - EXPECT_SKCOLOR_EQ(color_midpoint, - curve->GetValue(base::TimeDelta::FromSecondsD(0.5f))); - EXPECT_SKCOLOR_EQ(color_b, - curve->GetValue(base::TimeDelta::FromSecondsD(1.f))); - EXPECT_SKCOLOR_EQ(color_b, - curve->GetValue(base::TimeDelta::FromSecondsD(2.f))); -} - -// Tests that a color animation with three keyframes works as expected. -TEST(KeyframedAnimationCurveTest, ThreeColorKeyFrame) { - SkColor color_a = SkColorSetARGB(255, 255, 0, 0); - SkColor color_b = SkColorSetARGB(255, 0, 255, 0); - SkColor color_c = SkColorSetARGB(255, 0, 0, 255); - SkColor color_midpoint1 = - gfx::Tween::ColorValueBetween(0.5, color_a, color_b); - SkColor color_midpoint2 = - gfx::Tween::ColorValueBetween(0.5, color_b, color_c); - std::unique_ptr<KeyframedColorAnimationCurve> curve( - KeyframedColorAnimationCurve::Create()); - curve->AddKeyframe( - ColorKeyframe::Create(base::TimeDelta(), color_a, nullptr)); - curve->AddKeyframe(ColorKeyframe::Create(base::TimeDelta::FromSecondsD(1.0), - color_b, nullptr)); - curve->AddKeyframe(ColorKeyframe::Create(base::TimeDelta::FromSecondsD(2.0), - color_c, nullptr)); - - EXPECT_SKCOLOR_EQ(color_a, - curve->GetValue(base::TimeDelta::FromSecondsD(-1.f))); - EXPECT_SKCOLOR_EQ(color_a, - curve->GetValue(base::TimeDelta::FromSecondsD(0.f))); - EXPECT_SKCOLOR_EQ(color_midpoint1, - curve->GetValue(base::TimeDelta::FromSecondsD(0.5f))); - EXPECT_SKCOLOR_EQ(color_b, - curve->GetValue(base::TimeDelta::FromSecondsD(1.f))); - EXPECT_SKCOLOR_EQ(color_midpoint2, - curve->GetValue(base::TimeDelta::FromSecondsD(1.5f))); - EXPECT_SKCOLOR_EQ(color_c, - curve->GetValue(base::TimeDelta::FromSecondsD(2.f))); - EXPECT_SKCOLOR_EQ(color_c, - curve->GetValue(base::TimeDelta::FromSecondsD(3.f))); -} - -// Tests that a color animation with multiple keys at a given time works sanely. -TEST(KeyframedAnimationCurveTest, RepeatedColorKeyFrame) { - SkColor color_a = SkColorSetARGB(255, 64, 0, 0); - SkColor color_b = SkColorSetARGB(255, 192, 0, 0); - - std::unique_ptr<KeyframedColorAnimationCurve> curve( - KeyframedColorAnimationCurve::Create()); - curve->AddKeyframe( - ColorKeyframe::Create(base::TimeDelta(), color_a, nullptr)); - curve->AddKeyframe(ColorKeyframe::Create(base::TimeDelta::FromSecondsD(1.0), - color_a, nullptr)); - curve->AddKeyframe(ColorKeyframe::Create(base::TimeDelta::FromSecondsD(1.0), - color_b, nullptr)); - curve->AddKeyframe(ColorKeyframe::Create(base::TimeDelta::FromSecondsD(2.0), - color_b, nullptr)); - - EXPECT_SKCOLOR_EQ(color_a, - curve->GetValue(base::TimeDelta::FromSecondsD(-1.f))); - EXPECT_SKCOLOR_EQ(color_a, - curve->GetValue(base::TimeDelta::FromSecondsD(0.f))); - EXPECT_SKCOLOR_EQ(color_a, - curve->GetValue(base::TimeDelta::FromSecondsD(0.5f))); - - SkColor value = curve->GetValue(base::TimeDelta::FromSecondsD(1.0f)); - EXPECT_EQ(255u, SkColorGetA(value)); - int red_value = SkColorGetR(value); - EXPECT_LE(64, red_value); - EXPECT_GE(192, red_value); - - EXPECT_SKCOLOR_EQ(color_b, - curve->GetValue(base::TimeDelta::FromSecondsD(1.5f))); - EXPECT_SKCOLOR_EQ(color_b, - curve->GetValue(base::TimeDelta::FromSecondsD(2.f))); - EXPECT_SKCOLOR_EQ(color_b, - curve->GetValue(base::TimeDelta::FromSecondsD(3.f))); -} - -// Tests that a float animation with one keyframe works as expected. -TEST(KeyframedAnimationCurveTest, OneFloatKeyframe) { - std::unique_ptr<KeyframedFloatAnimationCurve> curve( - KeyframedFloatAnimationCurve::Create()); - curve->AddKeyframe(FloatKeyframe::Create(base::TimeDelta(), 2.f, nullptr)); - EXPECT_FLOAT_EQ(2.f, curve->GetValue(base::TimeDelta::FromSecondsD(-1.f))); - EXPECT_FLOAT_EQ(2.f, curve->GetValue(base::TimeDelta::FromSecondsD(0.f))); - EXPECT_FLOAT_EQ(2.f, curve->GetValue(base::TimeDelta::FromSecondsD(0.5f))); - EXPECT_FLOAT_EQ(2.f, curve->GetValue(base::TimeDelta::FromSecondsD(1.f))); - EXPECT_FLOAT_EQ(2.f, curve->GetValue(base::TimeDelta::FromSecondsD(2.f))); -} - -// Tests that a float animation with two keyframes works as expected. -TEST(KeyframedAnimationCurveTest, TwoFloatKeyframe) { - std::unique_ptr<KeyframedFloatAnimationCurve> curve( - KeyframedFloatAnimationCurve::Create()); - curve->AddKeyframe(FloatKeyframe::Create(base::TimeDelta(), 2.f, nullptr)); - curve->AddKeyframe( - FloatKeyframe::Create(base::TimeDelta::FromSecondsD(1.0), 4.f, nullptr)); - EXPECT_FLOAT_EQ(2.f, curve->GetValue(base::TimeDelta::FromSecondsD(-1.f))); - EXPECT_FLOAT_EQ(2.f, curve->GetValue(base::TimeDelta::FromSecondsD(0.f))); - EXPECT_FLOAT_EQ(3.f, curve->GetValue(base::TimeDelta::FromSecondsD(0.5f))); - EXPECT_FLOAT_EQ(4.f, curve->GetValue(base::TimeDelta::FromSecondsD(1.f))); - EXPECT_FLOAT_EQ(4.f, curve->GetValue(base::TimeDelta::FromSecondsD(2.f))); -} - -// Tests that a float animation with three keyframes works as expected. -TEST(KeyframedAnimationCurveTest, ThreeFloatKeyframe) { - std::unique_ptr<KeyframedFloatAnimationCurve> curve( - KeyframedFloatAnimationCurve::Create()); - curve->AddKeyframe(FloatKeyframe::Create(base::TimeDelta(), 2.f, nullptr)); - curve->AddKeyframe( - FloatKeyframe::Create(base::TimeDelta::FromSecondsD(1.0), 4.f, nullptr)); - curve->AddKeyframe( - FloatKeyframe::Create(base::TimeDelta::FromSecondsD(2.0), 8.f, nullptr)); - EXPECT_FLOAT_EQ(2.f, curve->GetValue(base::TimeDelta::FromSecondsD(-1.f))); - EXPECT_FLOAT_EQ(2.f, curve->GetValue(base::TimeDelta::FromSecondsD(0.f))); - EXPECT_FLOAT_EQ(3.f, curve->GetValue(base::TimeDelta::FromSecondsD(0.5f))); - EXPECT_FLOAT_EQ(4.f, curve->GetValue(base::TimeDelta::FromSecondsD(1.f))); - EXPECT_FLOAT_EQ(6.f, curve->GetValue(base::TimeDelta::FromSecondsD(1.5f))); - EXPECT_FLOAT_EQ(8.f, curve->GetValue(base::TimeDelta::FromSecondsD(2.f))); - EXPECT_FLOAT_EQ(8.f, curve->GetValue(base::TimeDelta::FromSecondsD(3.f))); -} - -// Tests that a float animation with multiple keys at a given time works sanely. -TEST(KeyframedAnimationCurveTest, RepeatedFloatKeyTimes) { - std::unique_ptr<KeyframedFloatAnimationCurve> curve( - KeyframedFloatAnimationCurve::Create()); - curve->AddKeyframe(FloatKeyframe::Create(base::TimeDelta(), 4.f, nullptr)); - curve->AddKeyframe( - FloatKeyframe::Create(base::TimeDelta::FromSecondsD(1.0), 4.f, nullptr)); - curve->AddKeyframe( - FloatKeyframe::Create(base::TimeDelta::FromSecondsD(1.0), 6.f, nullptr)); - curve->AddKeyframe( - FloatKeyframe::Create(base::TimeDelta::FromSecondsD(2.0), 6.f, nullptr)); - - EXPECT_FLOAT_EQ(4.f, curve->GetValue(base::TimeDelta::FromSecondsD(-1.f))); - EXPECT_FLOAT_EQ(4.f, curve->GetValue(base::TimeDelta::FromSecondsD(0.f))); - EXPECT_FLOAT_EQ(4.f, curve->GetValue(base::TimeDelta::FromSecondsD(0.5f))); - - // There is a discontinuity at 1. Any value between 4 and 6 is valid. - float value = curve->GetValue(base::TimeDelta::FromSecondsD(1.f)); - EXPECT_TRUE(value >= 4 && value <= 6); - - EXPECT_FLOAT_EQ(6.f, curve->GetValue(base::TimeDelta::FromSecondsD(1.5f))); - EXPECT_FLOAT_EQ(6.f, curve->GetValue(base::TimeDelta::FromSecondsD(2.f))); - EXPECT_FLOAT_EQ(6.f, curve->GetValue(base::TimeDelta::FromSecondsD(3.f))); -} - -// Tests that a transform animation with one keyframe works as expected. -TEST(KeyframedAnimationCurveTest, OneTransformKeyframe) { - std::unique_ptr<KeyframedTransformAnimationCurve> curve( - KeyframedTransformAnimationCurve::Create()); - TransformOperations operations; - operations.AppendTranslate(2.f, 0.f, 0.f); - curve->AddKeyframe( - TransformKeyframe::Create(base::TimeDelta(), operations, nullptr)); - - ExpectTranslateX(2.f, curve->GetValue(base::TimeDelta::FromSecondsD(-1.f))); - ExpectTranslateX(2.f, curve->GetValue(base::TimeDelta::FromSecondsD(0.f))); - ExpectTranslateX(2.f, curve->GetValue(base::TimeDelta::FromSecondsD(0.5f))); - ExpectTranslateX(2.f, curve->GetValue(base::TimeDelta::FromSecondsD(1.f))); - ExpectTranslateX(2.f, curve->GetValue(base::TimeDelta::FromSecondsD(2.f))); -} - -// Tests that a transform animation with two keyframes works as expected. -TEST(KeyframedAnimationCurveTest, TwoTransformKeyframe) { - std::unique_ptr<KeyframedTransformAnimationCurve> curve( - KeyframedTransformAnimationCurve::Create()); - TransformOperations operations1; - operations1.AppendTranslate(2.f, 0.f, 0.f); - TransformOperations operations2; - operations2.AppendTranslate(4.f, 0.f, 0.f); - - curve->AddKeyframe( - TransformKeyframe::Create(base::TimeDelta(), operations1, nullptr)); - curve->AddKeyframe(TransformKeyframe::Create( - base::TimeDelta::FromSecondsD(1.0), operations2, nullptr)); - ExpectTranslateX(2.f, curve->GetValue(base::TimeDelta::FromSecondsD(-1.f))); - ExpectTranslateX(2.f, curve->GetValue(base::TimeDelta::FromSecondsD(0.f))); - ExpectTranslateX(3.f, curve->GetValue(base::TimeDelta::FromSecondsD(0.5f))); - ExpectTranslateX(4.f, curve->GetValue(base::TimeDelta::FromSecondsD(1.f))); - ExpectTranslateX(4.f, curve->GetValue(base::TimeDelta::FromSecondsD(2.f))); -} - -// Tests that a transform animation with three keyframes works as expected. -TEST(KeyframedAnimationCurveTest, ThreeTransformKeyframe) { - std::unique_ptr<KeyframedTransformAnimationCurve> curve( - KeyframedTransformAnimationCurve::Create()); - TransformOperations operations1; - operations1.AppendTranslate(2.f, 0.f, 0.f); - TransformOperations operations2; - operations2.AppendTranslate(4.f, 0.f, 0.f); - TransformOperations operations3; - operations3.AppendTranslate(8.f, 0.f, 0.f); - curve->AddKeyframe( - TransformKeyframe::Create(base::TimeDelta(), operations1, nullptr)); - curve->AddKeyframe(TransformKeyframe::Create( - base::TimeDelta::FromSecondsD(1.0), operations2, nullptr)); - curve->AddKeyframe(TransformKeyframe::Create( - base::TimeDelta::FromSecondsD(2.0), operations3, nullptr)); - ExpectTranslateX(2.f, curve->GetValue(base::TimeDelta::FromSecondsD(-1.f))); - ExpectTranslateX(2.f, curve->GetValue(base::TimeDelta::FromSecondsD(0.f))); - ExpectTranslateX(3.f, curve->GetValue(base::TimeDelta::FromSecondsD(0.5f))); - ExpectTranslateX(4.f, curve->GetValue(base::TimeDelta::FromSecondsD(1.f))); - ExpectTranslateX(6.f, curve->GetValue(base::TimeDelta::FromSecondsD(1.5f))); - ExpectTranslateX(8.f, curve->GetValue(base::TimeDelta::FromSecondsD(2.f))); - ExpectTranslateX(8.f, curve->GetValue(base::TimeDelta::FromSecondsD(3.f))); -} - -// Tests that a transform animation with multiple keys at a given time works -// sanely. -TEST(KeyframedAnimationCurveTest, RepeatedTransformKeyTimes) { - std::unique_ptr<KeyframedTransformAnimationCurve> curve( - KeyframedTransformAnimationCurve::Create()); - // A step function. - TransformOperations operations1; - operations1.AppendTranslate(4.f, 0.f, 0.f); - TransformOperations operations2; - operations2.AppendTranslate(4.f, 0.f, 0.f); - TransformOperations operations3; - operations3.AppendTranslate(6.f, 0.f, 0.f); - TransformOperations operations4; - operations4.AppendTranslate(6.f, 0.f, 0.f); - curve->AddKeyframe( - TransformKeyframe::Create(base::TimeDelta(), operations1, nullptr)); - curve->AddKeyframe(TransformKeyframe::Create( - base::TimeDelta::FromSecondsD(1.0), operations2, nullptr)); - curve->AddKeyframe(TransformKeyframe::Create( - base::TimeDelta::FromSecondsD(1.0), operations3, nullptr)); - curve->AddKeyframe(TransformKeyframe::Create( - base::TimeDelta::FromSecondsD(2.0), operations4, nullptr)); - - ExpectTranslateX(4.f, curve->GetValue(base::TimeDelta::FromSecondsD(-1.f))); - ExpectTranslateX(4.f, curve->GetValue(base::TimeDelta::FromSecondsD(0.f))); - ExpectTranslateX(4.f, curve->GetValue(base::TimeDelta::FromSecondsD(0.5f))); - - // There is a discontinuity at 1. Any value between 4 and 6 is valid. - gfx::Transform value = - curve->GetValue(base::TimeDelta::FromSecondsD(1.f)).Apply(); - EXPECT_GE(value.matrix().get(0, 3), 4.f); - EXPECT_LE(value.matrix().get(0, 3), 6.f); - - ExpectTranslateX(6.f, curve->GetValue(base::TimeDelta::FromSecondsD(1.5f))); - ExpectTranslateX(6.f, curve->GetValue(base::TimeDelta::FromSecondsD(2.f))); - ExpectTranslateX(6.f, curve->GetValue(base::TimeDelta::FromSecondsD(3.f))); -} - -// Tests that a discrete transform animation (e.g. where one or more keyframes -// is a non-invertible matrix) works as expected. -TEST(KeyframedAnimationCurveTest, DiscreteLinearTransformAnimation) { - gfx::Transform non_invertible_matrix(0, 0, 0, 0, 0, 0); - gfx::Transform identity_matrix; - - std::unique_ptr<KeyframedTransformAnimationCurve> curve( - KeyframedTransformAnimationCurve::Create()); - TransformOperations operations1; - operations1.AppendMatrix(non_invertible_matrix); - TransformOperations operations2; - operations2.AppendMatrix(identity_matrix); - TransformOperations operations3; - operations3.AppendMatrix(non_invertible_matrix); - - curve->AddKeyframe( - TransformKeyframe::Create(base::TimeDelta(), operations1, nullptr)); - curve->AddKeyframe(TransformKeyframe::Create( - base::TimeDelta::FromSecondsD(1.0), operations2, nullptr)); - curve->AddKeyframe(TransformKeyframe::Create( - base::TimeDelta::FromSecondsD(2.0), operations3, nullptr)); - - TransformOperations result; - - // Between 0 and 0.5 seconds, the first keyframe should be returned. - result = curve->GetValue(base::TimeDelta::FromSecondsD(0.01f)); - EXPECT_TRANSFORMATION_MATRIX_EQ(non_invertible_matrix, result.Apply()); - - result = curve->GetValue(base::TimeDelta::FromSecondsD(0.49f)); - EXPECT_TRANSFORMATION_MATRIX_EQ(non_invertible_matrix, result.Apply()); - - // Between 0.5 and 1.5 seconds, the middle keyframe should be returned. - result = curve->GetValue(base::TimeDelta::FromSecondsD(0.5f)); - EXPECT_TRANSFORMATION_MATRIX_EQ(identity_matrix, result.Apply()); - - result = curve->GetValue(base::TimeDelta::FromSecondsD(1.49f)); - EXPECT_TRANSFORMATION_MATRIX_EQ(identity_matrix, result.Apply()); - - // Between 1.5 and 2.0 seconds, the last keyframe should be returned. - result = curve->GetValue(base::TimeDelta::FromSecondsD(1.5f)); - EXPECT_TRANSFORMATION_MATRIX_EQ(non_invertible_matrix, result.Apply()); - - result = curve->GetValue(base::TimeDelta::FromSecondsD(2.0f)); - EXPECT_TRANSFORMATION_MATRIX_EQ(non_invertible_matrix, result.Apply()); -} - -TEST(KeyframedAnimationCurveTest, DiscreteCubicBezierTransformAnimation) { - gfx::Transform non_invertible_matrix(0, 0, 0, 0, 0, 0); - gfx::Transform identity_matrix; - - std::unique_ptr<KeyframedTransformAnimationCurve> curve( - KeyframedTransformAnimationCurve::Create()); - TransformOperations operations1; - operations1.AppendMatrix(non_invertible_matrix); - TransformOperations operations2; - operations2.AppendMatrix(identity_matrix); - TransformOperations operations3; - operations3.AppendMatrix(non_invertible_matrix); - - // The cubic-bezier here is a nice fairly strong ease-in curve, where 50% - // progression is at approximately 85% of the time. - curve->AddKeyframe(TransformKeyframe::Create( - base::TimeDelta(), operations1, - CubicBezierTimingFunction::Create(0.75f, 0.25f, 0.9f, 0.4f))); - curve->AddKeyframe(TransformKeyframe::Create( - base::TimeDelta::FromSecondsD(1.0), operations2, - CubicBezierTimingFunction::Create(0.75f, 0.25f, 0.9f, 0.4f))); - curve->AddKeyframe(TransformKeyframe::Create( - base::TimeDelta::FromSecondsD(2.0), operations3, - CubicBezierTimingFunction::Create(0.75f, 0.25f, 0.9f, 0.4f))); - - TransformOperations result; - - // Due to the cubic-bezier, the first keyframe is returned almost all the way - // to 1 second. - result = curve->GetValue(base::TimeDelta::FromSecondsD(0.01f)); - EXPECT_TRANSFORMATION_MATRIX_EQ(non_invertible_matrix, result.Apply()); - - result = curve->GetValue(base::TimeDelta::FromSecondsD(0.8f)); - EXPECT_TRANSFORMATION_MATRIX_EQ(non_invertible_matrix, result.Apply()); - - // Between ~0.85 and ~1.85 seconds, the middle keyframe should be returned. - result = curve->GetValue(base::TimeDelta::FromSecondsD(0.85f)); - EXPECT_TRANSFORMATION_MATRIX_EQ(identity_matrix, result.Apply()); - - result = curve->GetValue(base::TimeDelta::FromSecondsD(1.8f)); - EXPECT_TRANSFORMATION_MATRIX_EQ(identity_matrix, result.Apply()); - - // Finally the last keyframe only takes effect after ~1.85 seconds. - result = curve->GetValue(base::TimeDelta::FromSecondsD(1.85f)); - EXPECT_TRANSFORMATION_MATRIX_EQ(non_invertible_matrix, result.Apply()); - - result = curve->GetValue(base::TimeDelta::FromSecondsD(2.0f)); - EXPECT_TRANSFORMATION_MATRIX_EQ(non_invertible_matrix, result.Apply()); -} - -// Tests that a filter animation with one keyframe works as expected. -TEST(KeyframedAnimationCurveTest, OneFilterKeyframe) { - std::unique_ptr<KeyframedFilterAnimationCurve> curve( - KeyframedFilterAnimationCurve::Create()); - FilterOperations operations; - operations.Append(FilterOperation::CreateBrightnessFilter(2.f)); - curve->AddKeyframe( - FilterKeyframe::Create(base::TimeDelta(), operations, nullptr)); - - ExpectBrightness(2.f, curve->GetValue(base::TimeDelta::FromSecondsD(-1.f))); - ExpectBrightness(2.f, curve->GetValue(base::TimeDelta::FromSecondsD(0.f))); - ExpectBrightness(2.f, curve->GetValue(base::TimeDelta::FromSecondsD(0.5f))); - ExpectBrightness(2.f, curve->GetValue(base::TimeDelta::FromSecondsD(1.f))); - ExpectBrightness(2.f, curve->GetValue(base::TimeDelta::FromSecondsD(2.f))); -} - -// Tests that a filter animation with two keyframes works as expected. -TEST(KeyframedAnimationCurveTest, TwoFilterKeyframe) { - std::unique_ptr<KeyframedFilterAnimationCurve> curve( - KeyframedFilterAnimationCurve::Create()); - FilterOperations operations1; - operations1.Append(FilterOperation::CreateBrightnessFilter(2.f)); - FilterOperations operations2; - operations2.Append(FilterOperation::CreateBrightnessFilter(4.f)); - - curve->AddKeyframe( - FilterKeyframe::Create(base::TimeDelta(), operations1, nullptr)); - curve->AddKeyframe(FilterKeyframe::Create(base::TimeDelta::FromSecondsD(1.f), - operations2, nullptr)); - ExpectBrightness(2.f, curve->GetValue(base::TimeDelta::FromSecondsD(-1.f))); - ExpectBrightness(2.f, curve->GetValue(base::TimeDelta::FromSecondsD(0.f))); - ExpectBrightness(3.f, curve->GetValue(base::TimeDelta::FromSecondsD(0.5f))); - ExpectBrightness(4.f, curve->GetValue(base::TimeDelta::FromSecondsD(1.f))); - ExpectBrightness(4.f, curve->GetValue(base::TimeDelta::FromSecondsD(2.f))); -} - -// Tests that a filter animation with three keyframes works as expected. -TEST(KeyframedAnimationCurveTest, ThreeFilterKeyframe) { - std::unique_ptr<KeyframedFilterAnimationCurve> curve( - KeyframedFilterAnimationCurve::Create()); - FilterOperations operations1; - operations1.Append(FilterOperation::CreateBrightnessFilter(2.f)); - FilterOperations operations2; - operations2.Append(FilterOperation::CreateBrightnessFilter(4.f)); - FilterOperations operations3; - operations3.Append(FilterOperation::CreateBrightnessFilter(8.f)); - curve->AddKeyframe( - FilterKeyframe::Create(base::TimeDelta(), operations1, nullptr)); - curve->AddKeyframe(FilterKeyframe::Create(base::TimeDelta::FromSecondsD(1.f), - operations2, nullptr)); - curve->AddKeyframe(FilterKeyframe::Create(base::TimeDelta::FromSecondsD(2.f), - operations3, nullptr)); - ExpectBrightness(2.f, curve->GetValue(base::TimeDelta::FromSecondsD(-1.f))); - ExpectBrightness(2.f, curve->GetValue(base::TimeDelta::FromSecondsD(0.f))); - ExpectBrightness(3.f, curve->GetValue(base::TimeDelta::FromSecondsD(0.5f))); - ExpectBrightness(4.f, curve->GetValue(base::TimeDelta::FromSecondsD(1.f))); - ExpectBrightness(6.f, curve->GetValue(base::TimeDelta::FromSecondsD(1.5f))); - ExpectBrightness(8.f, curve->GetValue(base::TimeDelta::FromSecondsD(2.f))); - ExpectBrightness(8.f, curve->GetValue(base::TimeDelta::FromSecondsD(3.f))); -} - -// Tests that a filter animation with multiple keys at a given time works -// sanely. -TEST(KeyframedAnimationCurveTest, RepeatedFilterKeyTimes) { - std::unique_ptr<KeyframedFilterAnimationCurve> curve( - KeyframedFilterAnimationCurve::Create()); - // A step function. - FilterOperations operations1; - operations1.Append(FilterOperation::CreateBrightnessFilter(4.f)); - FilterOperations operations2; - operations2.Append(FilterOperation::CreateBrightnessFilter(4.f)); - FilterOperations operations3; - operations3.Append(FilterOperation::CreateBrightnessFilter(6.f)); - FilterOperations operations4; - operations4.Append(FilterOperation::CreateBrightnessFilter(6.f)); - curve->AddKeyframe( - FilterKeyframe::Create(base::TimeDelta(), operations1, nullptr)); - curve->AddKeyframe(FilterKeyframe::Create(base::TimeDelta::FromSecondsD(1.f), - operations2, nullptr)); - curve->AddKeyframe(FilterKeyframe::Create(base::TimeDelta::FromSecondsD(1.f), - operations3, nullptr)); - curve->AddKeyframe(FilterKeyframe::Create(base::TimeDelta::FromSecondsD(2.f), - operations4, nullptr)); - - ExpectBrightness(4.f, curve->GetValue(base::TimeDelta::FromSecondsD(-1.f))); - ExpectBrightness(4.f, curve->GetValue(base::TimeDelta::FromSecondsD(0.f))); - ExpectBrightness(4.f, curve->GetValue(base::TimeDelta::FromSecondsD(0.5f))); - - // There is a discontinuity at 1. Any value between 4 and 6 is valid. - FilterOperations value = curve->GetValue(base::TimeDelta::FromSecondsD(1.f)); - EXPECT_EQ(1u, value.size()); - EXPECT_EQ(FilterOperation::BRIGHTNESS, value.at(0).type()); - EXPECT_GE(value.at(0).amount(), 4); - EXPECT_LE(value.at(0).amount(), 6); - - ExpectBrightness(6.f, curve->GetValue(base::TimeDelta::FromSecondsD(1.5f))); - ExpectBrightness(6.f, curve->GetValue(base::TimeDelta::FromSecondsD(2.f))); - ExpectBrightness(6.f, curve->GetValue(base::TimeDelta::FromSecondsD(3.f))); -} - -// Tests that the keyframes may be added out of order. -TEST(KeyframedAnimationCurveTest, UnsortedKeyframes) { - std::unique_ptr<KeyframedFloatAnimationCurve> curve( - KeyframedFloatAnimationCurve::Create()); - curve->AddKeyframe( - FloatKeyframe::Create(base::TimeDelta::FromSecondsD(2.f), 8.f, nullptr)); - curve->AddKeyframe(FloatKeyframe::Create(base::TimeDelta(), 2.f, nullptr)); - curve->AddKeyframe( - FloatKeyframe::Create(base::TimeDelta::FromSecondsD(1.f), 4.f, nullptr)); - EXPECT_FLOAT_EQ(2.f, curve->GetValue(base::TimeDelta::FromSecondsD(-1.f))); - EXPECT_FLOAT_EQ(2.f, curve->GetValue(base::TimeDelta::FromSecondsD(0.f))); - EXPECT_FLOAT_EQ(3.f, curve->GetValue(base::TimeDelta::FromSecondsD(0.5f))); - EXPECT_FLOAT_EQ(4.f, curve->GetValue(base::TimeDelta::FromSecondsD(1.f))); - EXPECT_FLOAT_EQ(6.f, curve->GetValue(base::TimeDelta::FromSecondsD(1.5f))); - EXPECT_FLOAT_EQ(8.f, curve->GetValue(base::TimeDelta::FromSecondsD(2.f))); - EXPECT_FLOAT_EQ(8.f, curve->GetValue(base::TimeDelta::FromSecondsD(3.f))); -} - -// Tests that a linear timing function works as expected. -TEST(KeyframedAnimationCurveTest, LinearTimingFunction) { - std::unique_ptr<KeyframedFloatAnimationCurve> curve( - KeyframedFloatAnimationCurve::Create()); - curve->AddKeyframe(FloatKeyframe::Create(base::TimeDelta(), 0.f, - LinearTimingFunction::Create())); - curve->AddKeyframe( - FloatKeyframe::Create(base::TimeDelta::FromSecondsD(1.0), 1.f, nullptr)); - - EXPECT_FLOAT_EQ(0.f, curve->GetValue(base::TimeDelta::FromSecondsD(0.f))); - EXPECT_FLOAT_EQ(0.75f, curve->GetValue(base::TimeDelta::FromSecondsD(0.75f))); -} - -// Tests that a cubic bezier timing function works as expected. -TEST(KeyframedAnimationCurveTest, CubicBezierTimingFunction) { - std::unique_ptr<KeyframedFloatAnimationCurve> curve( - KeyframedFloatAnimationCurve::Create()); - curve->AddKeyframe(FloatKeyframe::Create( - base::TimeDelta(), 0.f, - CubicBezierTimingFunction::Create(0.25f, 0.f, 0.75f, 1.f))); - curve->AddKeyframe( - FloatKeyframe::Create(base::TimeDelta::FromSecondsD(1.0), 1.f, nullptr)); - - EXPECT_FLOAT_EQ(0.f, curve->GetValue(base::TimeDelta::FromSecondsD(0.f))); - EXPECT_LT(0.f, curve->GetValue(base::TimeDelta::FromSecondsD(0.25f))); - EXPECT_GT(0.25f, curve->GetValue(base::TimeDelta::FromSecondsD(0.25f))); - EXPECT_NEAR(curve->GetValue(base::TimeDelta::FromSecondsD(0.5f)), 0.5f, - 0.00015f); - EXPECT_LT(0.75f, curve->GetValue(base::TimeDelta::FromSecondsD(0.75f))); - EXPECT_GT(1.f, curve->GetValue(base::TimeDelta::FromSecondsD(0.75f))); - EXPECT_FLOAT_EQ(1.f, curve->GetValue(base::TimeDelta::FromSecondsD(1.f))); -} - -// Tests a step timing function if the change of values occur at the start. -TEST(KeyframedAnimationCurveTest, StepsTimingFunctionStepAtStart) { - std::unique_ptr<KeyframedFloatAnimationCurve> curve( - KeyframedFloatAnimationCurve::Create()); - const int num_steps = 36; - curve->AddKeyframe(FloatKeyframe::Create( - base::TimeDelta(), 0.f, - StepsTimingFunction::Create(num_steps, - StepsTimingFunction::StepPosition::START))); - curve->AddKeyframe(FloatKeyframe::Create(base::TimeDelta::FromSecondsD(1.0), - num_steps, nullptr)); - - const float time_threshold = 0.0001f; - - for (float i = 0.f; i < num_steps; i += 1.f) { - const base::TimeDelta time1 = - base::TimeDelta::FromSecondsD(i / num_steps - time_threshold); - const base::TimeDelta time2 = - base::TimeDelta::FromSecondsD(i / num_steps + time_threshold); - EXPECT_FLOAT_EQ(std::ceil(i), curve->GetValue(time1)); - EXPECT_FLOAT_EQ(std::ceil(i) + 1.f, curve->GetValue(time2)); - } - EXPECT_FLOAT_EQ(num_steps, - curve->GetValue(base::TimeDelta::FromSecondsD(1.0))); - - for (float i = 0.5f; i <= num_steps; i += 1.0f) { - const base::TimeDelta time = base::TimeDelta::FromSecondsD(i / num_steps); - EXPECT_FLOAT_EQ(std::ceil(i), curve->GetValue(time)); - } -} - -// Tests a step timing function if the change of values occur at the end. -TEST(KeyframedAnimationCurveTest, StepsTimingFunctionStepAtEnd) { - std::unique_ptr<KeyframedFloatAnimationCurve> curve( - KeyframedFloatAnimationCurve::Create()); - const int num_steps = 36; - curve->AddKeyframe(FloatKeyframe::Create( - base::TimeDelta(), 0.f, - StepsTimingFunction::Create(num_steps, - StepsTimingFunction::StepPosition::END))); - curve->AddKeyframe(FloatKeyframe::Create(base::TimeDelta::FromSecondsD(1.0), - num_steps, nullptr)); - - const float time_threshold = 0.0001f; - - EXPECT_FLOAT_EQ(0.f, curve->GetValue(base::TimeDelta())); - for (float i = 1.f; i <= num_steps; i += 1.f) { - const base::TimeDelta time1 = - base::TimeDelta::FromSecondsD(i / num_steps - time_threshold); - const base::TimeDelta time2 = - base::TimeDelta::FromSecondsD(i / num_steps + time_threshold); - EXPECT_FLOAT_EQ(std::floor(i) - 1.f, curve->GetValue(time1)); - EXPECT_FLOAT_EQ(std::floor(i), curve->GetValue(time2)); - } - EXPECT_FLOAT_EQ(num_steps, - curve->GetValue(base::TimeDelta::FromSecondsD(1.0))); - - for (float i = 0.5f; i <= num_steps; i += 1.0f) { - const base::TimeDelta time = base::TimeDelta::FromSecondsD(i / num_steps); - EXPECT_FLOAT_EQ(std::floor(i), curve->GetValue(time)); - } -} - -// Tests that animations that are translations are correctly identified. -TEST(KeyframedAnimationCurveTest, IsTranslation) { - std::unique_ptr<KeyframedTransformAnimationCurve> curve( - KeyframedTransformAnimationCurve::Create()); - - TransformOperations operations1; - curve->AddKeyframe( - TransformKeyframe::Create(base::TimeDelta(), operations1, nullptr)); - operations1.AppendTranslate(2.0, 3.0, -1.0); - TransformOperations operations2; - operations2.AppendTranslate(4.0, 1.0, 2.0); - curve->AddKeyframe(TransformKeyframe::Create( - base::TimeDelta::FromSecondsD(1.f), operations2, nullptr)); - - EXPECT_TRUE(curve->IsTranslation()); - - TransformOperations operations3; - operations3.AppendScale(2.f, 2.f, 2.f); - curve->AddKeyframe(TransformKeyframe::Create( - base::TimeDelta::FromSecondsD(2.f), operations3, nullptr)); - - EXPECT_FALSE(curve->IsTranslation()); - - TransformOperations operations4; - operations3.AppendTranslate(2.f, 2.f, 2.f); - curve->AddKeyframe(TransformKeyframe::Create( - base::TimeDelta::FromSecondsD(3.f), operations4, nullptr)); - - EXPECT_FALSE(curve->IsTranslation()); -} - -// Tests that maximum target scale is computed as expected. -TEST(KeyframedAnimationCurveTest, MaximumTargetScale) { - std::unique_ptr<KeyframedTransformAnimationCurve> curve( - KeyframedTransformAnimationCurve::Create()); - - TransformOperations operations1; - curve->AddKeyframe( - TransformKeyframe::Create(base::TimeDelta(), operations1, nullptr)); - operations1.AppendScale(2.f, -3.f, 1.f); - curve->AddKeyframe(TransformKeyframe::Create( - base::TimeDelta::FromSecondsD(1.f), operations1, - CubicBezierTimingFunction::CreatePreset( - CubicBezierTimingFunction::EaseType::EASE))); - - float maximum_scale = 0.f; - EXPECT_TRUE(curve->MaximumTargetScale(true, &maximum_scale)); - EXPECT_EQ(3.f, maximum_scale); - - TransformOperations operations2; - operations2.AppendScale(6.f, 3.f, 2.f); - curve->AddKeyframe(TransformKeyframe::Create( - base::TimeDelta::FromSecondsD(2.f), operations2, - CubicBezierTimingFunction::CreatePreset( - CubicBezierTimingFunction::EaseType::EASE))); - - EXPECT_TRUE(curve->MaximumTargetScale(true, &maximum_scale)); - EXPECT_EQ(6.f, maximum_scale); - - TransformOperations operations3; - operations3.AppendRotate(1.f, 0.f, 0.f, 90.f); - curve->AddKeyframe(TransformKeyframe::Create( - base::TimeDelta::FromSecondsD(3.f), operations3, - CubicBezierTimingFunction::CreatePreset( - CubicBezierTimingFunction::EaseType::EASE))); - - EXPECT_TRUE(curve->MaximumTargetScale(true, &maximum_scale)); - EXPECT_EQ(6.f, maximum_scale); - - TransformOperations operations4; - operations4.AppendPerspective(3.f); - curve->AddKeyframe(TransformKeyframe::Create( - base::TimeDelta::FromSecondsD(4.f), operations4, - CubicBezierTimingFunction::CreatePreset( - CubicBezierTimingFunction::EaseType::EASE))); - - EXPECT_FALSE(curve->MaximumTargetScale(true, &maximum_scale)); - - // The original scale is not used in computing the max. - std::unique_ptr<KeyframedTransformAnimationCurve> curve2( - KeyframedTransformAnimationCurve::Create()); - - TransformOperations operations5; - operations5.AppendScale(0.4f, 0.2f, 0.6f); - curve2->AddKeyframe(TransformKeyframe::Create( - base::TimeDelta(), operations5, - CubicBezierTimingFunction::CreatePreset( - CubicBezierTimingFunction::EaseType::EASE))); - TransformOperations operations6; - operations6.AppendScale(0.5f, 0.3f, -0.8f); - curve2->AddKeyframe(TransformKeyframe::Create( - base::TimeDelta::FromSecondsD(1.f), operations6, - CubicBezierTimingFunction::CreatePreset( - CubicBezierTimingFunction::EaseType::EASE))); - - EXPECT_TRUE(curve2->MaximumTargetScale(true, &maximum_scale)); - EXPECT_EQ(0.8f, maximum_scale); - - EXPECT_TRUE(curve2->MaximumTargetScale(false, &maximum_scale)); - EXPECT_EQ(0.6f, maximum_scale); -} - -// Tests that starting animation scale is computed as expected. -TEST(KeyframedAnimationCurveTest, AnimationStartScale) { - std::unique_ptr<KeyframedTransformAnimationCurve> curve( - KeyframedTransformAnimationCurve::Create()); - - TransformOperations operations1; - curve->AddKeyframe( - TransformKeyframe::Create(base::TimeDelta(), operations1, nullptr)); - operations1.AppendScale(2.f, -3.f, 1.f); - curve->AddKeyframe(TransformKeyframe::Create( - base::TimeDelta::FromSecondsD(1.f), operations1, - CubicBezierTimingFunction::CreatePreset( - CubicBezierTimingFunction::EaseType::EASE))); - - float start_scale = 0.f; - - // Forward direction - EXPECT_TRUE(curve->AnimationStartScale(true, &start_scale)); - EXPECT_EQ(1.f, start_scale); - - // Backward direction - EXPECT_TRUE(curve->AnimationStartScale(false, &start_scale)); - EXPECT_EQ(3.f, start_scale); - - TransformOperations operations2; - operations2.AppendScale(6.f, 3.f, 2.f); - curve->AddKeyframe(TransformKeyframe::Create( - base::TimeDelta::FromSecondsD(2.f), operations2, - CubicBezierTimingFunction::CreatePreset( - CubicBezierTimingFunction::EaseType::EASE))); - - // Forward direction - EXPECT_TRUE(curve->AnimationStartScale(true, &start_scale)); - EXPECT_EQ(1.f, start_scale); - - // Backward direction - EXPECT_TRUE(curve->AnimationStartScale(false, &start_scale)); - EXPECT_EQ(6.f, start_scale); - - TransformOperations operations3; - operations3.AppendRotate(1.f, 0.f, 0.f, 90.f); - curve->AddKeyframe(TransformKeyframe::Create( - base::TimeDelta::FromSecondsD(3.f), operations3, - CubicBezierTimingFunction::CreatePreset( - CubicBezierTimingFunction::EaseType::EASE))); - - EXPECT_TRUE(curve->AnimationStartScale(false, &start_scale)); - EXPECT_EQ(1.f, start_scale); - - TransformOperations operations4; - operations4.AppendPerspective(90.f); - curve->AddKeyframe(TransformKeyframe::Create( - base::TimeDelta::FromSecondsD(4.f), operations4, - CubicBezierTimingFunction::CreatePreset( - CubicBezierTimingFunction::EaseType::EASE))); - - EXPECT_FALSE(curve->AnimationStartScale(false, &start_scale)); - EXPECT_EQ(0.f, start_scale); -} - -// Tests that an animation with a curve timing function works as expected. -TEST(KeyframedAnimationCurveTest, CurveTiming) { - std::unique_ptr<KeyframedFloatAnimationCurve> curve( - KeyframedFloatAnimationCurve::Create()); - curve->AddKeyframe(FloatKeyframe::Create(base::TimeDelta(), 0.f, nullptr)); - curve->AddKeyframe( - FloatKeyframe::Create(base::TimeDelta::FromSecondsD(1.f), 1.f, nullptr)); - curve->SetTimingFunction( - CubicBezierTimingFunction::Create(0.75f, 0.f, 0.25f, 1.f)); - EXPECT_FLOAT_EQ(0.f, curve->GetValue(base::TimeDelta::FromSecondsD(-1.f))); - EXPECT_FLOAT_EQ(0.f, curve->GetValue(base::TimeDelta::FromSecondsD(0.f))); - EXPECT_NEAR(0.05f, curve->GetValue(base::TimeDelta::FromSecondsD(0.25f)), - 0.005f); - EXPECT_FLOAT_EQ(0.5f, curve->GetValue(base::TimeDelta::FromSecondsD(0.5f))); - EXPECT_NEAR(0.95f, curve->GetValue(base::TimeDelta::FromSecondsD(0.75f)), - 0.005f); - EXPECT_FLOAT_EQ(1.f, curve->GetValue(base::TimeDelta::FromSecondsD(1.f))); - EXPECT_FLOAT_EQ(1.f, curve->GetValue(base::TimeDelta::FromSecondsD(2.f))); -} - -// Tests that an animation with a curve and keyframe timing function works as -// expected. -TEST(KeyframedAnimationCurveTest, CurveAndKeyframeTiming) { - std::unique_ptr<KeyframedFloatAnimationCurve> curve( - KeyframedFloatAnimationCurve::Create()); - curve->AddKeyframe(FloatKeyframe::Create( - base::TimeDelta(), 0.f, - CubicBezierTimingFunction::Create(0.35f, 0.f, 0.65f, 1.f))); - curve->AddKeyframe( - FloatKeyframe::Create(base::TimeDelta::FromSecondsD(1.f), 1.f, nullptr)); - // Curve timing function producing outputs outside of range [0,1]. - curve->SetTimingFunction( - CubicBezierTimingFunction::Create(0.5f, -0.5f, 0.5f, 1.5f)); - EXPECT_FLOAT_EQ(0.f, curve->GetValue(base::TimeDelta::FromSecondsD(-1.f))); - EXPECT_FLOAT_EQ(0.f, curve->GetValue(base::TimeDelta::FromSecondsD(0.f))); - EXPECT_FLOAT_EQ(0.f, curve->GetValue(base::TimeDelta::FromSecondsD( - 0.25f))); // Clamped. c(.25) < 0 - EXPECT_NEAR(0.17f, curve->GetValue(base::TimeDelta::FromSecondsD(0.42f)), - 0.005f); // c(.42)=.27, k(.27)=.17 - EXPECT_FLOAT_EQ(0.5f, curve->GetValue(base::TimeDelta::FromSecondsD(0.5f))); - EXPECT_NEAR(0.83f, curve->GetValue(base::TimeDelta::FromSecondsD(0.58f)), - 0.005f); // c(.58)=.73, k(.73)=.83 - EXPECT_FLOAT_EQ(1.f, curve->GetValue(base::TimeDelta::FromSecondsD( - 0.75f))); // Clamped. c(.75) > 1 - EXPECT_FLOAT_EQ(1.f, curve->GetValue(base::TimeDelta::FromSecondsD(1.f))); - EXPECT_FLOAT_EQ(1.f, curve->GetValue(base::TimeDelta::FromSecondsD(2.f))); -} - -// Tests that a linear timing function works as expected for inputs outside of -// range [0,1] -TEST(KeyframedAnimationCurveTest, LinearTimingInputsOutsideZeroOneRange) { - std::unique_ptr<KeyframedFloatAnimationCurve> curve( - KeyframedFloatAnimationCurve::Create()); - curve->AddKeyframe(FloatKeyframe::Create(base::TimeDelta(), 0.f, nullptr)); - curve->AddKeyframe( - FloatKeyframe::Create(base::TimeDelta::FromSecondsD(1.0), 2.f, nullptr)); - // Curve timing function producing timing outputs outside of range [0,1]. - curve->SetTimingFunction( - CubicBezierTimingFunction::Create(0.5f, -0.5f, 0.5f, 1.5f)); - - EXPECT_NEAR(-0.076f, curve->GetValue(base::TimeDelta::FromSecondsD(0.25f)), - 0.001f); - EXPECT_NEAR(2.076f, curve->GetValue(base::TimeDelta::FromSecondsD(0.75f)), - 0.001f); -} - -// If a curve cubic-bezier timing function produces timing outputs outside -// the range [0, 1] then a keyframe cubic-bezier timing function -// should consume that input properly (using end-point gradients). -TEST(KeyframedAnimationCurveTest, CurveTimingInputsOutsideZeroOneRange) { - std::unique_ptr<KeyframedFloatAnimationCurve> curve( - KeyframedFloatAnimationCurve::Create()); - // Keyframe timing function with 0.5 gradients at each end. - curve->AddKeyframe(FloatKeyframe::Create( - base::TimeDelta(), 0.f, - CubicBezierTimingFunction::Create(0.5f, 0.25f, 0.5f, 0.75f))); - curve->AddKeyframe( - FloatKeyframe::Create(base::TimeDelta::FromSecondsD(1.f), 1.f, nullptr)); - // Curve timing function producing timing outputs outside of range [0,1]. - curve->SetTimingFunction( - CubicBezierTimingFunction::Create(0.5f, -0.5f, 0.5f, 1.5f)); - - EXPECT_NEAR(-0.02f, curve->GetValue(base::TimeDelta::FromSecondsD(0.25f)), - 0.002f); // c(.25)=-.04, -.04*0.5=-0.02 - EXPECT_NEAR(0.33f, curve->GetValue(base::TimeDelta::FromSecondsD(0.46f)), - 0.002f); // c(.46)=.38, k(.38)=.33 - - EXPECT_NEAR(0.67f, curve->GetValue(base::TimeDelta::FromSecondsD(0.54f)), - 0.002f); // c(.54)=.62, k(.62)=.67 - EXPECT_NEAR(1.02f, curve->GetValue(base::TimeDelta::FromSecondsD(0.75f)), - 0.002f); // c(.75)=1.04 1+.04*0.5=1.02 -} - -// Tests that a step timing function works as expected for inputs outside of -// range [0,1] -TEST(KeyframedAnimationCurveTest, StepsTimingStartInputsOutsideZeroOneRange) { - std::unique_ptr<KeyframedFloatAnimationCurve> curve( - KeyframedFloatAnimationCurve::Create()); - curve->AddKeyframe( - FloatKeyframe::Create(base::TimeDelta(), 0.f, - StepsTimingFunction::Create( - 4, StepsTimingFunction::StepPosition::START))); - curve->AddKeyframe( - FloatKeyframe::Create(base::TimeDelta::FromSecondsD(1.0), 2.f, nullptr)); - // Curve timing function producing timing outputs outside of range [0,1]. - curve->SetTimingFunction( - CubicBezierTimingFunction::Create(0.5f, -0.5f, 0.5f, 1.5f)); - - EXPECT_FLOAT_EQ(0.f, curve->GetValue(base::TimeDelta::FromSecondsD(0.25f))); - EXPECT_FLOAT_EQ(2.5f, curve->GetValue(base::TimeDelta::FromSecondsD(0.75f))); -} - -TEST(KeyframedAnimationCurveTest, StepsTimingEndInputsOutsideZeroOneRange) { - std::unique_ptr<KeyframedFloatAnimationCurve> curve( - KeyframedFloatAnimationCurve::Create()); - curve->AddKeyframe(FloatKeyframe::Create( - base::TimeDelta(), 0.f, - StepsTimingFunction::Create(4, StepsTimingFunction::StepPosition::END))); - curve->AddKeyframe( - FloatKeyframe::Create(base::TimeDelta::FromSecondsD(1.0), 2.f, nullptr)); - // Curve timing function producing timing outputs outside of range [0,1]. - curve->SetTimingFunction( - CubicBezierTimingFunction::Create(0.5f, -0.5f, 0.5f, 1.5f)); - - EXPECT_FLOAT_EQ(-0.5f, curve->GetValue(base::TimeDelta::FromSecondsD(0.25f))); - EXPECT_FLOAT_EQ(2.f, curve->GetValue(base::TimeDelta::FromSecondsD(0.75f))); -} - -// Tests that an animation with a curve timing function and multiple keyframes -// works as expected. -TEST(KeyframedAnimationCurveTest, CurveTimingMultipleKeyframes) { - std::unique_ptr<KeyframedFloatAnimationCurve> curve( - KeyframedFloatAnimationCurve::Create()); - curve->AddKeyframe(FloatKeyframe::Create(base::TimeDelta(), 0.f, nullptr)); - curve->AddKeyframe( - FloatKeyframe::Create(base::TimeDelta::FromSecondsD(1.f), 1.f, nullptr)); - curve->AddKeyframe( - FloatKeyframe::Create(base::TimeDelta::FromSecondsD(2.f), 3.f, nullptr)); - curve->AddKeyframe( - FloatKeyframe::Create(base::TimeDelta::FromSecondsD(3.f), 6.f, nullptr)); - curve->AddKeyframe( - FloatKeyframe::Create(base::TimeDelta::FromSecondsD(4.f), 9.f, nullptr)); - curve->SetTimingFunction( - CubicBezierTimingFunction::Create(0.5f, 0.f, 0.5f, 1.f)); - EXPECT_FLOAT_EQ(0.f, curve->GetValue(base::TimeDelta::FromSecondsD(-1.f))); - EXPECT_FLOAT_EQ(0.f, curve->GetValue(base::TimeDelta::FromSecondsD(0.f))); - EXPECT_NEAR(0.42f, curve->GetValue(base::TimeDelta::FromSecondsD(1.f)), - 0.005f); - EXPECT_NEAR(1.f, curve->GetValue(base::TimeDelta::FromSecondsD(1.455f)), - 0.005f); - EXPECT_FLOAT_EQ(3.f, curve->GetValue(base::TimeDelta::FromSecondsD(2.f))); - EXPECT_NEAR(8.72f, curve->GetValue(base::TimeDelta::FromSecondsD(3.5f)), - 0.01f); - EXPECT_FLOAT_EQ(9.f, curve->GetValue(base::TimeDelta::FromSecondsD(4.f))); - EXPECT_FLOAT_EQ(9.f, curve->GetValue(base::TimeDelta::FromSecondsD(5.f))); -} - -// Tests that an animation with a curve timing function that overshoots works as -// expected. -TEST(KeyframedAnimationCurveTest, CurveTimingOvershootMultipeKeyframes) { - std::unique_ptr<KeyframedFloatAnimationCurve> curve( - KeyframedFloatAnimationCurve::Create()); - curve->AddKeyframe(FloatKeyframe::Create(base::TimeDelta(), 0.f, nullptr)); - curve->AddKeyframe( - FloatKeyframe::Create(base::TimeDelta::FromSecondsD(1.0), 1.f, nullptr)); - curve->AddKeyframe( - FloatKeyframe::Create(base::TimeDelta::FromSecondsD(2.0), 3.f, nullptr)); - curve->AddKeyframe( - FloatKeyframe::Create(base::TimeDelta::FromSecondsD(3.0), 6.f, nullptr)); - curve->AddKeyframe( - FloatKeyframe::Create(base::TimeDelta::FromSecondsD(4.0), 9.f, nullptr)); - // Curve timing function producing outputs outside of range [0,1]. - curve->SetTimingFunction( - CubicBezierTimingFunction::Create(0.5f, -0.5f, 0.5f, 1.5f)); - EXPECT_LE(curve->GetValue(base::TimeDelta::FromSecondsD(1.f)), - 0.f); // c(.25) < 0 - EXPECT_GE(curve->GetValue(base::TimeDelta::FromSecondsD(3.f)), - 9.f); // c(.75) > 1 -} - -// Tests that a float animation with multiple keys works with scaled duration. -TEST(KeyframedAnimationCurveTest, ScaledDuration) { - std::unique_ptr<KeyframedFloatAnimationCurve> curve( - KeyframedFloatAnimationCurve::Create()); - curve->AddKeyframe(FloatKeyframe::Create(base::TimeDelta(), 0.f, nullptr)); - curve->AddKeyframe( - FloatKeyframe::Create(base::TimeDelta::FromSecondsD(1.f), 1.f, nullptr)); - curve->AddKeyframe( - FloatKeyframe::Create(base::TimeDelta::FromSecondsD(2.f), 3.f, nullptr)); - curve->AddKeyframe( - FloatKeyframe::Create(base::TimeDelta::FromSecondsD(3.f), 6.f, nullptr)); - curve->AddKeyframe( - FloatKeyframe::Create(base::TimeDelta::FromSecondsD(4.f), 9.f, nullptr)); - curve->SetTimingFunction( - CubicBezierTimingFunction::Create(0.5f, 0.f, 0.5f, 1.f)); - - const double scale = 1000.0; - curve->set_scaled_duration(scale); - - EXPECT_DOUBLE_EQ(scale * 4, curve->Duration().InSecondsF()); - - EXPECT_FLOAT_EQ(0.f, - curve->GetValue(base::TimeDelta::FromSecondsD(scale * -1.f))); - EXPECT_FLOAT_EQ(0.f, - curve->GetValue(base::TimeDelta::FromSecondsD(scale * 0.f))); - EXPECT_NEAR(0.42f, - curve->GetValue(base::TimeDelta::FromSecondsD(scale * 1.f)), - 0.005f); - EXPECT_NEAR(1.f, - curve->GetValue(base::TimeDelta::FromSecondsD(scale * 1.455f)), - 0.005f); - EXPECT_FLOAT_EQ(3.f, - curve->GetValue(base::TimeDelta::FromSecondsD(scale * 2.f))); - EXPECT_NEAR(8.72f, - curve->GetValue(base::TimeDelta::FromSecondsD(scale * 3.5f)), - 0.01f); - EXPECT_FLOAT_EQ(9.f, - curve->GetValue(base::TimeDelta::FromSecondsD(scale * 4.f))); - EXPECT_FLOAT_EQ(9.f, - curve->GetValue(base::TimeDelta::FromSecondsD(scale * 5.f))); -} - -// Tests that a size animation with one keyframe works as expected. -TEST(KeyframedAnimationCurveTest, OneSizeKeyFrame) { - gfx::SizeF size = gfx::SizeF(100, 100); - std::unique_ptr<KeyframedSizeAnimationCurve> curve( - KeyframedSizeAnimationCurve::Create()); - curve->AddKeyframe(SizeKeyframe::Create(base::TimeDelta(), size, nullptr)); - - EXPECT_SIZEF_EQ(size, curve->GetValue(base::TimeDelta::FromSecondsD(-1.f))); - EXPECT_SIZEF_EQ(size, curve->GetValue(base::TimeDelta::FromSecondsD(0.f))); - EXPECT_SIZEF_EQ(size, curve->GetValue(base::TimeDelta::FromSecondsD(0.5f))); - EXPECT_SIZEF_EQ(size, curve->GetValue(base::TimeDelta::FromSecondsD(1.f))); - EXPECT_SIZEF_EQ(size, curve->GetValue(base::TimeDelta::FromSecondsD(2.f))); -} - -// Tests that a size animation with two keyframes works as expected. -TEST(KeyframedAnimationCurveTest, TwoSizeKeyFrame) { - gfx::SizeF size_a = gfx::SizeF(100, 100); - gfx::SizeF size_b = gfx::SizeF(100, 0); - gfx::SizeF size_midpoint = gfx::Tween::SizeFValueBetween(0.5, size_a, size_b); - std::unique_ptr<KeyframedSizeAnimationCurve> curve( - KeyframedSizeAnimationCurve::Create()); - curve->AddKeyframe(SizeKeyframe::Create(base::TimeDelta(), size_a, nullptr)); - curve->AddKeyframe(SizeKeyframe::Create(base::TimeDelta::FromSecondsD(1.0), - size_b, nullptr)); - - EXPECT_SIZEF_EQ(size_a, curve->GetValue(base::TimeDelta::FromSecondsD(-1.f))); - EXPECT_SIZEF_EQ(size_a, curve->GetValue(base::TimeDelta::FromSecondsD(0.f))); - EXPECT_SIZEF_EQ(size_midpoint, - curve->GetValue(base::TimeDelta::FromSecondsD(0.5f))); - EXPECT_SIZEF_EQ(size_b, curve->GetValue(base::TimeDelta::FromSecondsD(1.f))); - EXPECT_SIZEF_EQ(size_b, curve->GetValue(base::TimeDelta::FromSecondsD(2.f))); -} - -// Tests that a size animation with three keyframes works as expected. -TEST(KeyframedAnimationCurveTest, ThreeSizeKeyFrame) { - gfx::SizeF size_a = gfx::SizeF(100, 100); - gfx::SizeF size_b = gfx::SizeF(100, 0); - gfx::SizeF size_c = gfx::SizeF(200, 0); - gfx::SizeF size_midpoint1 = - gfx::Tween::SizeFValueBetween(0.5, size_a, size_b); - gfx::SizeF size_midpoint2 = - gfx::Tween::SizeFValueBetween(0.5, size_b, size_c); - std::unique_ptr<KeyframedSizeAnimationCurve> curve( - KeyframedSizeAnimationCurve::Create()); - curve->AddKeyframe(SizeKeyframe::Create(base::TimeDelta(), size_a, nullptr)); - curve->AddKeyframe(SizeKeyframe::Create(base::TimeDelta::FromSecondsD(1.0), - size_b, nullptr)); - curve->AddKeyframe(SizeKeyframe::Create(base::TimeDelta::FromSecondsD(2.0), - size_c, nullptr)); - - EXPECT_SIZEF_EQ(size_a, curve->GetValue(base::TimeDelta::FromSecondsD(-1.f))); - EXPECT_SIZEF_EQ(size_a, curve->GetValue(base::TimeDelta::FromSecondsD(0.f))); - EXPECT_SIZEF_EQ(size_midpoint1, - curve->GetValue(base::TimeDelta::FromSecondsD(0.5f))); - EXPECT_SIZEF_EQ(size_b, curve->GetValue(base::TimeDelta::FromSecondsD(1.f))); - EXPECT_SIZEF_EQ(size_midpoint2, - curve->GetValue(base::TimeDelta::FromSecondsD(1.5f))); - EXPECT_SIZEF_EQ(size_c, curve->GetValue(base::TimeDelta::FromSecondsD(2.f))); - EXPECT_SIZEF_EQ(size_c, curve->GetValue(base::TimeDelta::FromSecondsD(3.f))); -} - -// Tests that a size animation with multiple keys at a given time works sanely. -TEST(KeyframedAnimationCurveTest, RepeatedSizeKeyFrame) { - gfx::SizeF size_a = gfx::SizeF(100, 64); - gfx::SizeF size_b = gfx::SizeF(100, 192); - - std::unique_ptr<KeyframedSizeAnimationCurve> curve( - KeyframedSizeAnimationCurve::Create()); - curve->AddKeyframe(SizeKeyframe::Create(base::TimeDelta(), size_a, nullptr)); - curve->AddKeyframe(SizeKeyframe::Create(base::TimeDelta::FromSecondsD(1.0), - size_a, nullptr)); - curve->AddKeyframe(SizeKeyframe::Create(base::TimeDelta::FromSecondsD(1.0), - size_b, nullptr)); - curve->AddKeyframe(SizeKeyframe::Create(base::TimeDelta::FromSecondsD(2.0), - size_b, nullptr)); - - EXPECT_SIZEF_EQ(size_a, curve->GetValue(base::TimeDelta::FromSecondsD(-1.f))); - EXPECT_SIZEF_EQ(size_a, curve->GetValue(base::TimeDelta::FromSecondsD(0.f))); - EXPECT_SIZEF_EQ(size_a, curve->GetValue(base::TimeDelta::FromSecondsD(0.5f))); - - gfx::SizeF value = curve->GetValue(base::TimeDelta::FromSecondsD(1.0f)); - EXPECT_FLOAT_EQ(100.0f, value.width()); - EXPECT_LE(64.0f, value.height()); - EXPECT_GE(192.0f, value.height()); - - EXPECT_SIZEF_EQ(size_b, curve->GetValue(base::TimeDelta::FromSecondsD(1.5f))); - EXPECT_SIZEF_EQ(size_b, curve->GetValue(base::TimeDelta::FromSecondsD(2.f))); - EXPECT_SIZEF_EQ(size_b, curve->GetValue(base::TimeDelta::FromSecondsD(3.f))); -} - -} // namespace -} // namespace cc diff --git a/chromium/cc/animation/scroll_offset_animation_curve.cc b/chromium/cc/animation/scroll_offset_animation_curve.cc index 8a67e0f0a39..5a645b4bad1 100644 --- a/chromium/cc/animation/scroll_offset_animation_curve.cc +++ b/chromium/cc/animation/scroll_offset_animation_curve.cc @@ -11,7 +11,7 @@ #include "base/check_op.h" #include "base/memory/ptr_util.h" #include "base/numerics/ranges.h" -#include "cc/animation/timing_function.h" +#include "ui/gfx/animation/keyframe/timing_function.h" #include "ui/gfx/animation/tween.h" const double kConstantDuration = 9.0; @@ -32,6 +32,10 @@ const double kInverseDeltaSlope = const double kInverseDeltaOffset = kInverseDeltaMaxDuration - kInverseDeltaRampStartPx * kInverseDeltaSlope; +using gfx::CubicBezierTimingFunction; +using gfx::LinearTimingFunction; +using gfx::TimingFunction; + namespace cc { namespace { @@ -136,7 +140,8 @@ ScrollOffsetAnimationCurve::ScrollOffsetAnimationCurve( animation_type_(animation_type), duration_behavior_(duration_behavior), has_set_initial_value_(false) { - DCHECK_EQ((animation_type == AnimationType::kEaseInOut), + DCHECK_EQ((animation_type == AnimationType::kEaseInOut || + animation_type == AnimationType::kImpulse), duration_behavior.has_value()); switch (animation_type) { case AnimationType::kEaseInOut: @@ -162,7 +167,8 @@ ScrollOffsetAnimationCurve::ScrollOffsetAnimationCurve( animation_type_(animation_type), duration_behavior_(duration_behavior), has_set_initial_value_(false) { - DCHECK_EQ((animation_type == AnimationType::kEaseInOut), + DCHECK_EQ((animation_type == AnimationType::kEaseInOut || + animation_type == AnimationType::kImpulse), duration_behavior.has_value()); } @@ -314,14 +320,27 @@ base::TimeDelta ScrollOffsetAnimationCurve::Duration() const { return total_animation_duration_; } -AnimationCurve::CurveType ScrollOffsetAnimationCurve::Type() const { - return SCROLL_OFFSET; +int ScrollOffsetAnimationCurve::Type() const { + return AnimationCurve::SCROLL_OFFSET; +} + +const char* ScrollOffsetAnimationCurve::TypeName() const { + return "ScrollOffset"; } -std::unique_ptr<AnimationCurve> ScrollOffsetAnimationCurve::Clone() const { +std::unique_ptr<gfx::AnimationCurve> ScrollOffsetAnimationCurve::Clone() const { return CloneToScrollOffsetAnimationCurve(); } +void ScrollOffsetAnimationCurve::Tick( + base::TimeDelta t, + int property_id, + gfx::KeyframeModel* keyframe_model) const { + if (target_) { + target_->OnScrollOffsetAnimated(GetValue(t), property_id, keyframe_model); + } +} + std::unique_ptr<ScrollOffsetAnimationCurve> ScrollOffsetAnimationCurve::CloneToScrollOffsetAnimationCurve() const { std::unique_ptr<TimingFunction> timing_function( @@ -359,6 +378,11 @@ void ScrollOffsetAnimationCurve::UpdateTarget( DCHECK_NE(animation_type_, AnimationType::kLinear) << "UpdateTarget is not supported on linear scroll animations."; + // UpdateTarget is still called for linear animations occasionally. This is + // tracked via crbug.com/1164008. + if (animation_type_ == AnimationType::kLinear) + return; + // If the new UpdateTarget actually happened before the previous one, keep // |t| as the most recent, but reduce the duration of any generated // animation. @@ -399,11 +423,8 @@ void ScrollOffsetAnimationCurve::UpdateTarget( return; } - base::TimeDelta new_duration = - (animation_type_ == AnimationType::kEaseInOut) - ? EaseInOutBoundedSegmentDuration(new_delta, t, delayed_by) - : ImpulseSegmentDuration(new_delta, delayed_by); - + const base::TimeDelta new_duration = + EaseInOutBoundedSegmentDuration(new_delta, t, delayed_by); if (new_duration.InSecondsF() < kEpsilon) { // The duration is (close to) 0, so stop the animation. target_value_ = new_target; @@ -417,23 +438,34 @@ void ScrollOffsetAnimationCurve::UpdateTarget( double new_slope = velocity * (new_duration.InSecondsF() / MaximumDimension(new_delta)); - if (animation_type_ == AnimationType::kEaseInOut) { - timing_function_ = EaseInOutWithInitialSlope(new_slope); - } else { - DCHECK_EQ(animation_type_, AnimationType::kImpulse); - if (IsNewTargetInOppositeDirection(current_position, target_value_, - new_target)) { - // Prevent any rubber-banding by setting the velocity (and subsequently, - // the slope) to 0 when moving in the opposite direciton. - new_slope = 0; - } - timing_function_ = ImpulseCurveWithInitialSlope(new_slope); + DCHECK(animation_type_ == AnimationType::kImpulse || + animation_type_ == AnimationType::kEaseInOut); + if (animation_type_ == AnimationType::kImpulse && + IsNewTargetInOppositeDirection(current_position, target_value_, + new_target)) { + // Prevent any rubber-banding by setting the velocity (and subsequently, the + // slope) to 0 when moving in the opposite direciton. + new_slope = 0; } + timing_function_ = EaseInOutWithInitialSlope(new_slope); initial_value_ = current_position; target_value_ = new_target; total_animation_duration_ = t + new_duration; last_retarget_ = t; } +const ScrollOffsetAnimationCurve* +ScrollOffsetAnimationCurve::ToScrollOffsetAnimationCurve( + const AnimationCurve* c) { + DCHECK_EQ(ScrollOffsetAnimationCurve::SCROLL_OFFSET, c->Type()); + return static_cast<const ScrollOffsetAnimationCurve*>(c); +} + +ScrollOffsetAnimationCurve* +ScrollOffsetAnimationCurve::ToScrollOffsetAnimationCurve(AnimationCurve* c) { + DCHECK_EQ(ScrollOffsetAnimationCurve::SCROLL_OFFSET, c->Type()); + return static_cast<ScrollOffsetAnimationCurve*>(c); +} + } // namespace cc diff --git a/chromium/cc/animation/scroll_offset_animation_curve.h b/chromium/cc/animation/scroll_offset_animation_curve.h index 889cbcc453a..633ccf79b84 100644 --- a/chromium/cc/animation/scroll_offset_animation_curve.h +++ b/chromium/cc/animation/scroll_offset_animation_curve.h @@ -8,13 +8,15 @@ #include <memory> #include "base/time/time.h" -#include "cc/animation/animation_curve.h" #include "cc/animation/animation_export.h" +#include "ui/gfx/animation/keyframe/animation_curve.h" #include "ui/gfx/geometry/scroll_offset.h" -namespace cc { - +namespace gfx { class TimingFunction; +} // namespace gfx + +namespace cc { // ScrollOffsetAnimationCurve computes scroll offset as a function of time // during a scroll offset animation. @@ -23,9 +25,18 @@ class TimingFunction; // user input or programmatic scroll operations. For more information about // scheduling and servicing scroll animations, see blink::ScrollAnimator and // blink::ProgrammaticScrollAnimator. - -class CC_ANIMATION_EXPORT ScrollOffsetAnimationCurve : public AnimationCurve { +class CC_ANIMATION_EXPORT ScrollOffsetAnimationCurve + : public gfx::AnimationCurve { public: + class Target { + public: + ~Target() = default; + + virtual void OnScrollOffsetAnimated(const gfx::ScrollOffset& value, + int target_property_id, + gfx::KeyframeModel* keyframe_model) = 0; + }; + // Indicates how the animation duration should be computed for Ease-in-out // style scroll animation curves. enum class DurationBehavior { @@ -39,6 +50,12 @@ class CC_ANIMATION_EXPORT ScrollOffsetAnimationCurve : public AnimationCurve { INVERSE_DELTA }; + static const ScrollOffsetAnimationCurve* ToScrollOffsetAnimationCurve( + const AnimationCurve* c); + + static ScrollOffsetAnimationCurve* ToScrollOffsetAnimationCurve( + AnimationCurve* c); + // There is inherent delay in input processing; it may take many milliseconds // from the time of user input to when when we're actually able to handle it // here. This delay is represented by the |delayed_by| value. The way we have @@ -84,14 +101,21 @@ class CC_ANIMATION_EXPORT ScrollOffsetAnimationCurve : public AnimationCurve { // AnimationCurve implementation base::TimeDelta Duration() const override; - CurveType Type() const override; - std::unique_ptr<AnimationCurve> Clone() const override; + int Type() const override; + const char* TypeName() const override; + std::unique_ptr<gfx::AnimationCurve> Clone() const override; std::unique_ptr<ScrollOffsetAnimationCurve> CloneToScrollOffsetAnimationCurve() const; - + void Tick(base::TimeDelta t, + int property_id, + gfx::KeyframeModel* keyframe_model) const override; static void SetAnimationDurationForTesting(base::TimeDelta duration); + void set_target(Target* target) { target_ = target; } private: + FRIEND_TEST_ALL_PREFIXES(ScrollOffsetAnimationCurveTest, ImpulseUpdateTarget); + FRIEND_TEST_ALL_PREFIXES(ScrollOffsetAnimationCurveTest, + ImpulseUpdateTargetSwitchDirections); friend class ScrollOffsetAnimationCurveFactory; enum class AnimationType { kLinear, kEaseInOut, kImpulse }; @@ -103,7 +127,7 @@ class CC_ANIMATION_EXPORT ScrollOffsetAnimationCurve : public AnimationCurve { base::Optional<DurationBehavior> duration_behavior = base::nullopt); ScrollOffsetAnimationCurve( const gfx::ScrollOffset& target_value, - std::unique_ptr<TimingFunction> timing_function, + std::unique_ptr<gfx::TimingFunction> timing_function, AnimationType animation_type, base::Optional<DurationBehavior> duration_behavior); @@ -127,7 +151,7 @@ class CC_ANIMATION_EXPORT ScrollOffsetAnimationCurve : public AnimationCurve { // Time from animation start to most recent UpdateTarget. base::TimeDelta last_retarget_; - std::unique_ptr<TimingFunction> timing_function_; + std::unique_ptr<gfx::TimingFunction> timing_function_; AnimationType animation_type_; // Only valid when |animation_type_| is EASE_IN_OUT. @@ -136,6 +160,8 @@ class CC_ANIMATION_EXPORT ScrollOffsetAnimationCurve : public AnimationCurve { bool has_set_initial_value_; static base::Optional<double> animation_duration_for_testing_; + + Target* target_ = nullptr; }; } // namespace cc diff --git a/chromium/cc/animation/scroll_offset_animation_curve_factory.cc b/chromium/cc/animation/scroll_offset_animation_curve_factory.cc index 29f5cbc5d30..dcb9cb40662 100644 --- a/chromium/cc/animation/scroll_offset_animation_curve_factory.cc +++ b/chromium/cc/animation/scroll_offset_animation_curve_factory.cc @@ -4,9 +4,10 @@ #include "cc/animation/scroll_offset_animation_curve_factory.h" +#include <memory> #include "base/memory/ptr_util.h" -#include "cc/animation/timing_function.h" #include "cc/base/features.h" +#include "ui/gfx/animation/keyframe/timing_function.h" namespace cc { namespace { @@ -86,6 +87,7 @@ std::unique_ptr<ScrollOffsetAnimationCurve> ScrollOffsetAnimationCurveFactory::CreateImpulseAnimation( const gfx::ScrollOffset& target_value) { return base::WrapUnique(new ScrollOffsetAnimationCurve( - target_value, ScrollOffsetAnimationCurve::AnimationType::kImpulse)); + target_value, ScrollOffsetAnimationCurve::AnimationType::kImpulse, + ScrollOffsetAnimationCurve::DurationBehavior::INVERSE_DELTA)); } } // namespace cc diff --git a/chromium/cc/animation/scroll_offset_animation_curve_unittest.cc b/chromium/cc/animation/scroll_offset_animation_curve_unittest.cc index f0e8c423aa0..bbfb91218bc 100644 --- a/chromium/cc/animation/scroll_offset_animation_curve_unittest.cc +++ b/chromium/cc/animation/scroll_offset_animation_curve_unittest.cc @@ -5,9 +5,9 @@ #include "cc/animation/scroll_offset_animation_curve.h" #include "cc/animation/scroll_offset_animation_curve_factory.h" -#include "cc/animation/timing_function.h" #include "cc/test/geometry_test_utils.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/gfx/animation/keyframe/timing_function.h" using DurationBehavior = cc::ScrollOffsetAnimationCurve::DurationBehavior; @@ -21,6 +21,8 @@ namespace { // This is the value of the default Impulse bezier curve when t = 0.5 constexpr double halfway_through_default_impulse_curve = 0.874246; +} // namespace + TEST(ScrollOffsetAnimationCurveTest, DeltaBasedDuration) { gfx::ScrollOffset target_value(100.f, 200.f); std::unique_ptr<ScrollOffsetAnimationCurve> curve( @@ -75,7 +77,6 @@ TEST(ScrollOffsetAnimationCurveTest, GetValue) { EXPECT_GT(curve->Duration().InSecondsF(), 0); EXPECT_LT(curve->Duration().InSecondsF(), 0.1); - EXPECT_EQ(AnimationCurve::SCROLL_OFFSET, curve->Type()); EXPECT_EQ(duration, curve->Duration()); EXPECT_VECTOR2DF_EQ(initial_value, @@ -104,30 +105,25 @@ TEST(ScrollOffsetAnimationCurveTest, Clone) { curve->SetInitialValue(initial_value); base::TimeDelta duration = curve->Duration(); - std::unique_ptr<AnimationCurve> clone(curve->Clone()); + std::unique_ptr<gfx::AnimationCurve> clone(curve->Clone()); - EXPECT_EQ(AnimationCurve::SCROLL_OFFSET, clone->Type()); EXPECT_EQ(duration, clone->Duration()); - EXPECT_VECTOR2DF_EQ(initial_value, - clone->ToScrollOffsetAnimationCurve()->GetValue( - base::TimeDelta::FromSecondsD(-1.0))); - EXPECT_VECTOR2DF_EQ( - initial_value, - clone->ToScrollOffsetAnimationCurve()->GetValue(base::TimeDelta())); - EXPECT_VECTOR2DF_NEAR( - gfx::ScrollOffset(6.f, 30.f), - clone->ToScrollOffsetAnimationCurve()->GetValue(duration * 0.5f), - 0.00025); + ScrollOffsetAnimationCurve* cloned_curve = + ScrollOffsetAnimationCurve::ToScrollOffsetAnimationCurve(clone.get()); + + EXPECT_VECTOR2DF_EQ(initial_value, cloned_curve->GetValue( + base::TimeDelta::FromSecondsD(-1.0))); + EXPECT_VECTOR2DF_EQ(initial_value, cloned_curve->GetValue(base::TimeDelta())); + EXPECT_VECTOR2DF_NEAR(gfx::ScrollOffset(6.f, 30.f), + cloned_curve->GetValue(duration * 0.5f), 0.00025); + EXPECT_VECTOR2DF_EQ(target_value, cloned_curve->GetValue(duration)); EXPECT_VECTOR2DF_EQ( - target_value, clone->ToScrollOffsetAnimationCurve()->GetValue(duration)); - EXPECT_VECTOR2DF_EQ(target_value, - clone->ToScrollOffsetAnimationCurve()->GetValue( - duration + base::TimeDelta::FromSecondsD(1.f))); + target_value, + cloned_curve->GetValue(duration + base::TimeDelta::FromSecondsD(1.f))); // Verify that the timing function was cloned correctly. - gfx::ScrollOffset value = - clone->ToScrollOffsetAnimationCurve()->GetValue(duration * 0.25f); + gfx::ScrollOffset value = cloned_curve->GetValue(duration * 0.25f); EXPECT_NEAR(3.0333f, value.x(), 0.0002f); EXPECT_NEAR(37.4168f, value.y(), 0.0002f); } @@ -208,8 +204,8 @@ TEST(ScrollOffsetAnimationCurveTest, ImpulseUpdateTarget) { gfx::Vector2dF new_delta = new_target_value.DeltaFrom(distance_halfway_through_initial_animation); base::TimeDelta updated_segment_duration = - ScrollOffsetAnimationCurve::ImpulseSegmentDuration(new_delta, - base::TimeDelta()); + curve->EaseInOutBoundedSegmentDuration(new_delta, base::TimeDelta(), + base::TimeDelta()); base::TimeDelta overall_duration = time_of_update + updated_segment_duration; EXPECT_NEAR(overall_duration.InSecondsF(), curve->Duration().InSecondsF(), @@ -257,25 +253,26 @@ TEST(ScrollOffsetAnimationCurveTest, ImpulseUpdateTargetSwitchDirections) { curve->UpdateTarget(base::TimeDelta::FromSecondsD(initial_duration / 2), updated_target); - double updated_duration = - ScrollOffsetAnimationCurve::ImpulseSegmentDuration( - gfx::Vector2dF(updated_initial_value.x(), updated_initial_value.y()), - base::TimeDelta()) - .InSecondsF(); - EXPECT_NEAR( initial_target_value.y() * halfway_through_default_impulse_curve, curve->GetValue(base::TimeDelta::FromSecondsD(initial_duration / 2.0)) .y(), 0.01f); - EXPECT_NEAR( - updated_initial_value.y() * (1.0 - halfway_through_default_impulse_curve), - curve - ->GetValue(base::TimeDelta::FromSecondsD(initial_duration / 2.0 + - updated_duration / 2.0)) - .y(), - 0.01f); + // Once the impulse style curve is updated, it turns to an ease-in ease-out + // type curve. + double updated_duration = curve + ->EaseInOutBoundedSegmentDuration( + gfx::Vector2dF(updated_initial_value.x(), + updated_initial_value.y()), + base::TimeDelta(), base::TimeDelta()) + .InSecondsF(); + EXPECT_NEAR(updated_initial_value.y() * 0.5, + curve + ->GetValue(base::TimeDelta::FromSecondsD( + initial_duration / 2.0 + updated_duration / 2.0)) + .y(), + 0.01f); EXPECT_NEAR(0.0, curve ->GetValue(base::TimeDelta::FromSecondsD( @@ -453,5 +450,4 @@ TEST(ScrollOffsetAnimationCurveTest, UpdateTargetZeroLastSegmentDuration) { EXPECT_NEAR(expected_duration, curve->Duration().InSecondsF(), 0.0002f); } -} // namespace } // namespace cc diff --git a/chromium/cc/animation/scroll_offset_animations_impl.cc b/chromium/cc/animation/scroll_offset_animations_impl.cc index 498992120c9..73fc4ed2935 100644 --- a/chromium/cc/animation/scroll_offset_animations_impl.cc +++ b/chromium/cc/animation/scroll_offset_animations_impl.cc @@ -4,6 +4,8 @@ #include "cc/animation/scroll_offset_animations_impl.h" +#include <utility> + #include "base/trace_event/trace_event.h" #include "base/trace_event/traced_value.h" #include "cc/animation/animation.h" @@ -12,7 +14,7 @@ #include "cc/animation/animation_timeline.h" #include "cc/animation/element_animations.h" #include "cc/animation/scroll_offset_animation_curve_factory.h" -#include "cc/animation/timing_function.h" +#include "ui/gfx/animation/keyframe/timing_function.h" namespace cc { @@ -69,14 +71,15 @@ void ScrollOffsetAnimationsImpl::MouseWheelScrollAnimationCreate( void ScrollOffsetAnimationsImpl::ScrollAnimationCreateInternal( ElementId element_id, - std::unique_ptr<AnimationCurve> curve, + std::unique_ptr<gfx::AnimationCurve> curve, base::TimeDelta animation_start_offset) { TRACE_EVENT_INSTANT1("cc", "ScrollAnimationCreate", TRACE_EVENT_SCOPE_THREAD, "Duration", curve->Duration().InMillisecondsF()); std::unique_ptr<KeyframeModel> keyframe_model = KeyframeModel::Create( std::move(curve), AnimationIdProvider::NextKeyframeModelId(), - AnimationIdProvider::NextGroupId(), TargetProperty::SCROLL_OFFSET); + AnimationIdProvider::NextGroupId(), + KeyframeModel::TargetPropertyId(TargetProperty::SCROLL_OFFSET)); keyframe_model->set_time_offset(animation_start_offset); keyframe_model->SetIsImplOnly(); @@ -111,7 +114,8 @@ bool ScrollOffsetAnimationsImpl::ScrollAnimationUpdateTarget( return true; ScrollOffsetAnimationCurve* curve = - keyframe_model->curve()->ToScrollOffsetAnimationCurve(); + ScrollOffsetAnimationCurve::ToScrollOffsetAnimationCurve( + keyframe_model->curve()); gfx::ScrollOffset new_target = gfx::ScrollOffsetWithDelta(curve->target_value(), scroll_delta); @@ -164,14 +168,15 @@ void ScrollOffsetAnimationsImpl::ScrollAnimationApplyAdjustment( } std::unique_ptr<ScrollOffsetAnimationCurve> new_curve = - keyframe_model->curve() - ->ToScrollOffsetAnimationCurve() + ScrollOffsetAnimationCurve::ToScrollOffsetAnimationCurve( + keyframe_model->curve()) ->CloneToScrollOffsetAnimationCurve(); new_curve->ApplyAdjustment(adjustment); std::unique_ptr<KeyframeModel> new_keyframe_model = KeyframeModel::Create( std::move(new_curve), AnimationIdProvider::NextKeyframeModelId(), - AnimationIdProvider::NextGroupId(), TargetProperty::SCROLL_OFFSET); + AnimationIdProvider::NextGroupId(), + KeyframeModel::TargetPropertyId(TargetProperty::SCROLL_OFFSET)); new_keyframe_model->set_start_time(keyframe_model->start_time()); new_keyframe_model->SetIsImplOnly(); new_keyframe_model->set_affects_active_elements(false); diff --git a/chromium/cc/animation/scroll_offset_animations_impl.h b/chromium/cc/animation/scroll_offset_animations_impl.h index c2aa77e4f2c..50a78945fcf 100644 --- a/chromium/cc/animation/scroll_offset_animations_impl.h +++ b/chromium/cc/animation/scroll_offset_animations_impl.h @@ -5,6 +5,8 @@ #ifndef CC_ANIMATION_SCROLL_OFFSET_ANIMATIONS_IMPL_H_ #define CC_ANIMATION_SCROLL_OFFSET_ANIMATIONS_IMPL_H_ +#include <memory> + #include "base/memory/ref_counted.h" #include "cc/animation/animation_delegate.h" #include "cc/animation/scroll_offset_animation_curve.h" @@ -69,11 +71,11 @@ class CC_ANIMATION_EXPORT ScrollOffsetAnimationsImpl void NotifyAnimationAborted(base::TimeTicks monotonic_time, int target_property, int group) override {} - void NotifyAnimationTakeover(base::TimeTicks monotonic_time, - int target_property, - base::TimeTicks animation_start_time, - std::unique_ptr<AnimationCurve> curve) override { - } + void NotifyAnimationTakeover( + base::TimeTicks monotonic_time, + int target_property, + base::TimeTicks animation_start_time, + std::unique_ptr<gfx::AnimationCurve> curve) override {} void NotifyLocalTimeUpdated( base::Optional<base::TimeDelta> local_time) override {} @@ -82,7 +84,7 @@ class CC_ANIMATION_EXPORT ScrollOffsetAnimationsImpl private: void ScrollAnimationCreateInternal(ElementId element_id, - std::unique_ptr<AnimationCurve> curve, + std::unique_ptr<gfx::AnimationCurve> curve, base::TimeDelta animation_start_offset); void ReattachScrollOffsetAnimationIfNeeded(ElementId element_id); diff --git a/chromium/cc/animation/scroll_timeline.h b/chromium/cc/animation/scroll_timeline.h index c79001fb825..fbdbc46783f 100644 --- a/chromium/cc/animation/scroll_timeline.h +++ b/chromium/cc/animation/scroll_timeline.h @@ -5,6 +5,7 @@ #ifndef CC_ANIMATION_SCROLL_TIMELINE_H_ #define CC_ANIMATION_SCROLL_TIMELINE_H_ +#include <vector> #include "base/optional.h" #include "base/time/time.h" #include "cc/animation/animation_export.h" @@ -124,14 +125,22 @@ inline ScrollTimeline* ToScrollTimeline(AnimationTimeline* timeline) { template <typename T> double ComputeProgress(double current_offset, const T& resolved_offsets) { DCHECK_GE(resolved_offsets.size(), 2u); + // When start offset is greater than end offset, current time is calculated + // outside of this method. + DCHECK(resolved_offsets[0] < resolved_offsets[resolved_offsets.size() - 1]); DCHECK(current_offset < resolved_offsets[resolved_offsets.size() - 1]); - // Look for scroll offset that contains the current offset. + // Traverse scroll offsets from the back to find first interval that + // contains the current offset. In case of overlapping offsets, last matching + // interval in the list is used to calculate the current time. The rational + // for choosing last matching offset is to be consistent with CSS property + // overrides. unsigned int offset_id; - for (offset_id = 1; offset_id < resolved_offsets.size() && - resolved_offsets[offset_id] <= current_offset; - offset_id++) { + for (offset_id = resolved_offsets.size() - 1; + offset_id > 0 && !(resolved_offsets[offset_id - 1] <= current_offset && + current_offset < resolved_offsets[offset_id]); + offset_id--) { } - DCHECK(offset_id < resolved_offsets.size()); + DCHECK_GE(offset_id, 1u); // Weight of each offset within time range is distributed equally. double offset_distance = 1.0 / (resolved_offsets.size() - 1); // Progress of the current offset within its offset range. diff --git a/chromium/cc/animation/scroll_timeline_unittest.cc b/chromium/cc/animation/scroll_timeline_unittest.cc index 15589ae2f18..4ee60fc6bd4 100644 --- a/chromium/cc/animation/scroll_timeline_unittest.cc +++ b/chromium/cc/animation/scroll_timeline_unittest.cc @@ -205,6 +205,51 @@ TEST_F(ScrollTimelineTest, MultipleScrollOffsetsCurrentTimeCalculations) { time_range, vertical_timeline->CurrentTime(scroll_tree(), false)); } +TEST_F(ScrollTimelineTest, OverlappingScrollOffsets) { + double time_range = 100.0; + + // Start offset is greater than end offset ==> animation progress is + // either 0% or 100%. + std::vector<double> scroll_offsets = {350.0, 200.0, 50.0}; + + scoped_refptr<ScrollTimeline> vertical_timeline = ScrollTimeline::Create( + scroller_id(), ScrollTimeline::ScrollDown, scroll_offsets, time_range); + + // Offset is less than start offset ==> current time is 0. + SetScrollOffset(&property_trees(), scroller_id(), gfx::ScrollOffset(0, 300)); + EXPECT_SCROLL_TIMELINE_TIME_NEAR( + 0, vertical_timeline->CurrentTime(scroll_tree(), false)); + + // Offset is greater than end offset ==> current time is time_range. + SetScrollOffset(&property_trees(), scroller_id(), gfx::ScrollOffset(0, 360)); + EXPECT_SCROLL_TIMELINE_TIME_NEAR( + time_range, vertical_timeline->CurrentTime(scroll_tree(), false)); + + scroll_offsets = {0.0, 400.0, 200.0}; + + vertical_timeline = ScrollTimeline::Create( + scroller_id(), ScrollTimeline::ScrollDown, scroll_offsets, time_range); + + SetScrollOffset(&property_trees(), scroller_id(), gfx::ScrollOffset(0, 100)); + // Scroll offset is 25% of [0, 400) range, which maps to [0% 50%) of the + // entire scroll range. + EXPECT_SCROLL_TIMELINE_TIME_NEAR( + time_range * 0.5 * 0.25, + vertical_timeline->CurrentTime(scroll_tree(), false)); + + scroll_offsets = {200.0, 0.0, 400.0}; + + vertical_timeline = ScrollTimeline::Create( + scroller_id(), ScrollTimeline::ScrollDown, scroll_offsets, time_range); + + SetScrollOffset(&property_trees(), scroller_id(), gfx::ScrollOffset(0, 300)); + // Scroll offset is 75% of [0, 400) range, which maps to [50% 100%) of the + // entire scroll range. + EXPECT_SCROLL_TIMELINE_TIME_NEAR( + time_range * (0.5 + 0.5 * 0.75), + vertical_timeline->CurrentTime(scroll_tree(), false)); +} + TEST_F(ScrollTimelineTest, CurrentTimeIsAdjustedForTimeRange) { double time_range = content_size().height() - container_size().height(); diff --git a/chromium/cc/animation/timing_function.cc b/chromium/cc/animation/timing_function.cc deleted file mode 100644 index 8906be4d9f7..00000000000 --- a/chromium/cc/animation/timing_function.cc +++ /dev/null @@ -1,182 +0,0 @@ -// Copyright 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "cc/animation/timing_function.h" - -#include <cmath> -#include <memory> - -#include "base/check_op.h" -#include "base/memory/ptr_util.h" -#include "base/notreached.h" - -namespace cc { - -TimingFunction::TimingFunction() = default; - -TimingFunction::~TimingFunction() = default; - -std::unique_ptr<CubicBezierTimingFunction> -CubicBezierTimingFunction::CreatePreset(EaseType ease_type) { - // These numbers come from - // http://www.w3.org/TR/css3-transitions/#transition-timing-function_tag. - switch (ease_type) { - case EaseType::EASE: - return base::WrapUnique( - new CubicBezierTimingFunction(ease_type, 0.25, 0.1, 0.25, 1.0)); - case EaseType::EASE_IN: - return base::WrapUnique( - new CubicBezierTimingFunction(ease_type, 0.42, 0.0, 1.0, 1.0)); - case EaseType::EASE_OUT: - return base::WrapUnique( - new CubicBezierTimingFunction(ease_type, 0.0, 0.0, 0.58, 1.0)); - case EaseType::EASE_IN_OUT: - return base::WrapUnique( - new CubicBezierTimingFunction(ease_type, 0.42, 0.0, 0.58, 1)); - default: - NOTREACHED(); - return nullptr; - } -} -std::unique_ptr<CubicBezierTimingFunction> -CubicBezierTimingFunction::Create(double x1, double y1, double x2, double y2) { - return base::WrapUnique( - new CubicBezierTimingFunction(EaseType::CUSTOM, x1, y1, x2, y2)); -} - -CubicBezierTimingFunction::CubicBezierTimingFunction(EaseType ease_type, - double x1, - double y1, - double x2, - double y2) - : bezier_(x1, y1, x2, y2), ease_type_(ease_type) {} - -CubicBezierTimingFunction::~CubicBezierTimingFunction() = default; - -TimingFunction::Type CubicBezierTimingFunction::GetType() const { - return Type::CUBIC_BEZIER; -} - -double CubicBezierTimingFunction::GetValue(double x) const { - return bezier_.Solve(x); -} - -double CubicBezierTimingFunction::Velocity(double x) const { - return bezier_.Slope(x); -} - -std::unique_ptr<TimingFunction> CubicBezierTimingFunction::Clone() const { - return base::WrapUnique(new CubicBezierTimingFunction(*this)); -} - -std::unique_ptr<StepsTimingFunction> StepsTimingFunction::Create( - int steps, - StepPosition step_position) { - return base::WrapUnique(new StepsTimingFunction(steps, step_position)); -} - -StepsTimingFunction::StepsTimingFunction(int steps, StepPosition step_position) - : steps_(steps), step_position_(step_position) {} - -StepsTimingFunction::~StepsTimingFunction() = default; - -TimingFunction::Type StepsTimingFunction::GetType() const { - return Type::STEPS; -} - -double StepsTimingFunction::GetValue(double t) const { - return GetPreciseValue(t, TimingFunction::LimitDirection::RIGHT); -} - -std::unique_ptr<TimingFunction> StepsTimingFunction::Clone() const { - return base::WrapUnique(new StepsTimingFunction(*this)); -} - -double StepsTimingFunction::Velocity(double x) const { - return 0; -} - -double StepsTimingFunction::GetPreciseValue(double t, - LimitDirection direction) const { - const double steps = static_cast<double>(steps_); - double current_step = std::floor((steps * t) + GetStepsStartOffset()); - // Adjust step if using a left limit at a discontinuous step boundary. - if (direction == LimitDirection::LEFT && - steps * t - std::floor(steps * t) == 0) { - current_step -= 1; - } - // Jumps may differ from steps based on the number of end-point - // discontinuities, which may be 0, 1 or 2. - int jumps = NumberOfJumps(); - if (t >= 0 && current_step < 0) - current_step = 0; - if (t <= 1 && current_step > jumps) - current_step = jumps; - return current_step / jumps; -} - -int StepsTimingFunction::NumberOfJumps() const { - switch (step_position_) { - case StepPosition::END: - case StepPosition::START: - case StepPosition::JUMP_END: - case StepPosition::JUMP_START: - return steps_; - - case StepPosition::JUMP_BOTH: - return steps_ + 1; - - case StepPosition::JUMP_NONE: - DCHECK_GT(steps_, 1); - return steps_ - 1; - - default: - NOTREACHED(); - return steps_; - } -} - -float StepsTimingFunction::GetStepsStartOffset() const { - switch (step_position_) { - case StepPosition::JUMP_BOTH: - case StepPosition::JUMP_START: - case StepPosition::START: - return 1; - - case StepPosition::JUMP_END: - case StepPosition::JUMP_NONE: - case StepPosition::END: - return 0; - - default: - NOTREACHED(); - return 1; - } -} - -std::unique_ptr<LinearTimingFunction> LinearTimingFunction::Create() { - return base::WrapUnique(new LinearTimingFunction()); -} - -LinearTimingFunction::LinearTimingFunction() = default; - -LinearTimingFunction::~LinearTimingFunction() = default; - -TimingFunction::Type LinearTimingFunction::GetType() const { - return Type::LINEAR; -} - -std::unique_ptr<TimingFunction> LinearTimingFunction::Clone() const { - return base::WrapUnique(new LinearTimingFunction(*this)); -} - -double LinearTimingFunction::Velocity(double x) const { - return 0; -} - -double LinearTimingFunction::GetValue(double t) const { - return t; -} - -} // namespace cc diff --git a/chromium/cc/animation/timing_function.h b/chromium/cc/animation/timing_function.h deleted file mode 100644 index 4422a4a6f7d..00000000000 --- a/chromium/cc/animation/timing_function.h +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CC_ANIMATION_TIMING_FUNCTION_H_ -#define CC_ANIMATION_TIMING_FUNCTION_H_ - -#include <memory> - -#include "cc/animation/animation_export.h" -#include "ui/gfx/geometry/cubic_bezier.h" - -namespace cc { - -// See http://www.w3.org/TR/css3-transitions/. -class CC_ANIMATION_EXPORT TimingFunction { - public: - virtual ~TimingFunction(); - - TimingFunction& operator=(const TimingFunction&) = delete; - - // Note that LINEAR is a nullptr TimingFunction (for now). - enum class Type { LINEAR, CUBIC_BEZIER, STEPS }; - - // Which limit to apply at a discontinuous boundary. - enum class LimitDirection { LEFT, RIGHT }; - - virtual Type GetType() const = 0; - virtual double GetValue(double t) const = 0; - virtual double Velocity(double time) const = 0; - virtual std::unique_ptr<TimingFunction> Clone() const = 0; - - protected: - TimingFunction(); -}; - -class CC_ANIMATION_EXPORT CubicBezierTimingFunction : public TimingFunction { - public: - enum class EaseType { EASE, EASE_IN, EASE_OUT, EASE_IN_OUT, CUSTOM }; - - static std::unique_ptr<CubicBezierTimingFunction> CreatePreset( - EaseType ease_type); - static std::unique_ptr<CubicBezierTimingFunction> Create(double x1, - double y1, - double x2, - double y2); - ~CubicBezierTimingFunction() override; - - CubicBezierTimingFunction& operator=(const CubicBezierTimingFunction&) = - delete; - - // TimingFunction implementation. - Type GetType() const override; - double GetValue(double time) const override; - double Velocity(double time) const override; - std::unique_ptr<TimingFunction> Clone() const override; - - EaseType ease_type() const { return ease_type_; } - const gfx::CubicBezier& bezier() const { return bezier_; } - - private: - CubicBezierTimingFunction(EaseType ease_type, - double x1, - double y1, - double x2, - double y2); - - gfx::CubicBezier bezier_; - EaseType ease_type_; -}; - -class CC_ANIMATION_EXPORT StepsTimingFunction : public TimingFunction { - public: - // step-timing-function values - // https://drafts.csswg.org/css-easing-1/#typedef-step-timing-function - enum class StepPosition { - START, // Discontinuity at progress = 0. - // Alias for jump-start. Maintaining a separate enumerated value - // for serialization. - END, // Discontinuity at progress = 1. - // Alias for jump-end. Maintaining a separate enumerated value - // for serialization. - JUMP_BOTH, // Discontinuities at progress = 0 and 1. - JUMP_END, // Discontinuity at progress = 1. - JUMP_NONE, // Continuous at progress = 0 and 1. - JUMP_START // Discontinuity at progress = 0. - }; - - static std::unique_ptr<StepsTimingFunction> Create( - int steps, - StepPosition step_position); - ~StepsTimingFunction() override; - - StepsTimingFunction& operator=(const StepsTimingFunction&) = delete; - - // TimingFunction implementation. - Type GetType() const override; - double GetValue(double t) const override; - std::unique_ptr<TimingFunction> Clone() const override; - double Velocity(double time) const override; - - int steps() const { return steps_; } - StepPosition step_position() const { return step_position_; } - double GetPreciseValue(double t, LimitDirection limit_direction) const; - - private: - StepsTimingFunction(int steps, StepPosition step_position); - - // The number of jumps is the number of discontinuities in the timing - // function. There is a subtle distinction between the number of steps and - // jumps. The number of steps is the number of intervals in the timing - // function. The number of jumps differs from the number of steps when either - // both or neither end point has a discontinuity. - // https://drafts.csswg.org/css-easing-1/#step-easing-functions - int NumberOfJumps() const; - - float GetStepsStartOffset() const; - - int steps_; - StepPosition step_position_; -}; - -class CC_ANIMATION_EXPORT LinearTimingFunction : public TimingFunction { - public: - static std::unique_ptr<LinearTimingFunction> Create(); - ~LinearTimingFunction() override; - - // TimingFunction implementation. - Type GetType() const override; - double GetValue(double t) const override; - std::unique_ptr<TimingFunction> Clone() const override; - double Velocity(double time) const override; - - private: - LinearTimingFunction(); -}; - -} // namespace cc - -#endif // CC_ANIMATION_TIMING_FUNCTION_H_ diff --git a/chromium/cc/animation/transform_operation.cc b/chromium/cc/animation/transform_operation.cc deleted file mode 100644 index cbc222b5fec..00000000000 --- a/chromium/cc/animation/transform_operation.cc +++ /dev/null @@ -1,514 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <limits> -#include <utility> - -#include "base/check_op.h" -#include "base/notreached.h" -#include "base/numerics/math_constants.h" -#include "base/numerics/ranges.h" -#include "cc/animation/transform_operation.h" -#include "cc/animation/transform_operations.h" -#include "ui/gfx/geometry/angle_conversions.h" -#include "ui/gfx/geometry/box_f.h" -#include "ui/gfx/geometry/vector3d_f.h" -#include "ui/gfx/transform_util.h" - -namespace { -const SkScalar kAngleEpsilon = 1e-4f; -} - -namespace cc { - -bool TransformOperation::IsIdentity() const { - return matrix.IsIdentity(); -} - -static bool IsOperationIdentity(const TransformOperation* operation) { - return !operation || operation->IsIdentity(); -} - -static bool ShareSameAxis(const TransformOperation* from, - const TransformOperation* to, - SkScalar* axis_x, - SkScalar* axis_y, - SkScalar* axis_z, - SkScalar* angle_from) { - if (IsOperationIdentity(from) && IsOperationIdentity(to)) - return false; - - if (IsOperationIdentity(from) && !IsOperationIdentity(to)) { - *axis_x = to->rotate.axis.x; - *axis_y = to->rotate.axis.y; - *axis_z = to->rotate.axis.z; - *angle_from = 0; - return true; - } - - if (!IsOperationIdentity(from) && IsOperationIdentity(to)) { - *axis_x = from->rotate.axis.x; - *axis_y = from->rotate.axis.y; - *axis_z = from->rotate.axis.z; - *angle_from = from->rotate.angle; - return true; - } - - SkScalar length_2 = from->rotate.axis.x * from->rotate.axis.x + - from->rotate.axis.y * from->rotate.axis.y + - from->rotate.axis.z * from->rotate.axis.z; - SkScalar other_length_2 = to->rotate.axis.x * to->rotate.axis.x + - to->rotate.axis.y * to->rotate.axis.y + - to->rotate.axis.z * to->rotate.axis.z; - - if (length_2 <= kAngleEpsilon || other_length_2 <= kAngleEpsilon) - return false; - - SkScalar dot = to->rotate.axis.x * from->rotate.axis.x + - to->rotate.axis.y * from->rotate.axis.y + - to->rotate.axis.z * from->rotate.axis.z; - SkScalar error = - SkScalarAbs(SK_Scalar1 - (dot * dot) / (length_2 * other_length_2)); - bool result = error < kAngleEpsilon; - if (result) { - *axis_x = to->rotate.axis.x; - *axis_y = to->rotate.axis.y; - *axis_z = to->rotate.axis.z; - // If the axes are pointing in opposite directions, we need to reverse - // the angle. - *angle_from = dot > 0 ? from->rotate.angle : -from->rotate.angle; - } - return result; -} - -static SkScalar BlendSkScalars(SkScalar from, SkScalar to, SkScalar progress) { - return from * (1 - progress) + to * progress; -} - -void TransformOperation::Bake() { - matrix.MakeIdentity(); - switch (type) { - case TransformOperation::TRANSFORM_OPERATION_TRANSLATE: - matrix.Translate3d(translate.x, translate.y, translate.z); - break; - case TransformOperation::TRANSFORM_OPERATION_ROTATE: - matrix.RotateAbout( - gfx::Vector3dF(rotate.axis.x, rotate.axis.y, rotate.axis.z), - rotate.angle); - break; - case TransformOperation::TRANSFORM_OPERATION_SCALE: - matrix.Scale3d(scale.x, scale.y, scale.z); - break; - case TransformOperation::TRANSFORM_OPERATION_SKEWX: - case TransformOperation::TRANSFORM_OPERATION_SKEWY: - case TransformOperation::TRANSFORM_OPERATION_SKEW: - matrix.Skew(skew.x, skew.y); - break; - case TransformOperation::TRANSFORM_OPERATION_PERSPECTIVE: - matrix.ApplyPerspectiveDepth(perspective_depth); - break; - case TransformOperation::TRANSFORM_OPERATION_MATRIX: - case TransformOperation::TRANSFORM_OPERATION_IDENTITY: - break; - } -} - -bool TransformOperation::ApproximatelyEqual(const TransformOperation& other, - SkScalar tolerance) const { - DCHECK_LE(0, tolerance); - if (type != other.type) - return false; - switch (type) { - case TransformOperation::TRANSFORM_OPERATION_TRANSLATE: - return base::IsApproximatelyEqual(translate.x, other.translate.x, - tolerance) && - base::IsApproximatelyEqual(translate.y, other.translate.y, - tolerance) && - base::IsApproximatelyEqual(translate.z, other.translate.z, - tolerance); - case TransformOperation::TRANSFORM_OPERATION_ROTATE: - return base::IsApproximatelyEqual(rotate.axis.x, other.rotate.axis.x, - tolerance) && - base::IsApproximatelyEqual(rotate.axis.y, other.rotate.axis.y, - tolerance) && - base::IsApproximatelyEqual(rotate.axis.z, other.rotate.axis.z, - tolerance) && - base::IsApproximatelyEqual(rotate.angle, other.rotate.angle, - tolerance); - case TransformOperation::TRANSFORM_OPERATION_SCALE: - return base::IsApproximatelyEqual(scale.x, other.scale.x, tolerance) && - base::IsApproximatelyEqual(scale.y, other.scale.y, tolerance) && - base::IsApproximatelyEqual(scale.z, other.scale.z, tolerance); - case TransformOperation::TRANSFORM_OPERATION_SKEWX: - case TransformOperation::TRANSFORM_OPERATION_SKEWY: - case TransformOperation::TRANSFORM_OPERATION_SKEW: - return base::IsApproximatelyEqual(skew.x, other.skew.x, tolerance) && - base::IsApproximatelyEqual(skew.y, other.skew.y, tolerance); - case TransformOperation::TRANSFORM_OPERATION_PERSPECTIVE: - return base::IsApproximatelyEqual(perspective_depth, - other.perspective_depth, tolerance); - case TransformOperation::TRANSFORM_OPERATION_MATRIX: - // TODO(vollick): we could expose a tolerance on gfx::Transform, but it's - // complex since we need a different tolerance per component. Driving this - // with a single tolerance will take some care. For now, we will check - // exact equality where the tolerance is 0.0f, otherwise we will use the - // unparameterized version of gfx::Transform::ApproximatelyEqual. - if (tolerance == 0.0f) - return matrix == other.matrix; - else - return matrix.ApproximatelyEqual(other.matrix); - case TransformOperation::TRANSFORM_OPERATION_IDENTITY: - return other.matrix.IsIdentity(); - } - NOTREACHED(); - return false; -} - -bool TransformOperation::BlendTransformOperations( - const TransformOperation* from, - const TransformOperation* to, - SkScalar progress, - TransformOperation* result) { - if (IsOperationIdentity(from) && IsOperationIdentity(to)) - return true; - - TransformOperation::Type interpolation_type = - TransformOperation::TRANSFORM_OPERATION_IDENTITY; - if (IsOperationIdentity(to)) - interpolation_type = from->type; - else - interpolation_type = to->type; - result->type = interpolation_type; - - switch (interpolation_type) { - case TransformOperation::TRANSFORM_OPERATION_TRANSLATE: { - SkScalar from_x = IsOperationIdentity(from) ? 0 : from->translate.x; - SkScalar from_y = IsOperationIdentity(from) ? 0 : from->translate.y; - SkScalar from_z = IsOperationIdentity(from) ? 0 : from->translate.z; - SkScalar to_x = IsOperationIdentity(to) ? 0 : to->translate.x; - SkScalar to_y = IsOperationIdentity(to) ? 0 : to->translate.y; - SkScalar to_z = IsOperationIdentity(to) ? 0 : to->translate.z; - result->translate.x = BlendSkScalars(from_x, to_x, progress), - result->translate.y = BlendSkScalars(from_y, to_y, progress), - result->translate.z = BlendSkScalars(from_z, to_z, progress), - result->Bake(); - break; - } - case TransformOperation::TRANSFORM_OPERATION_ROTATE: { - SkScalar axis_x = 0; - SkScalar axis_y = 0; - SkScalar axis_z = 1; - SkScalar from_angle = 0; - SkScalar to_angle = IsOperationIdentity(to) ? 0 : to->rotate.angle; - if (ShareSameAxis(from, to, &axis_x, &axis_y, &axis_z, &from_angle)) { - result->rotate.axis.x = axis_x; - result->rotate.axis.y = axis_y; - result->rotate.axis.z = axis_z; - result->rotate.angle = BlendSkScalars(from_angle, to_angle, progress); - result->Bake(); - } else { - if (!IsOperationIdentity(to)) - result->matrix = to->matrix; - gfx::Transform from_matrix; - if (!IsOperationIdentity(from)) - from_matrix = from->matrix; - if (!result->matrix.Blend(from_matrix, progress)) - return false; - } - break; - } - case TransformOperation::TRANSFORM_OPERATION_SCALE: { - SkScalar from_x = IsOperationIdentity(from) ? 1 : from->scale.x; - SkScalar from_y = IsOperationIdentity(from) ? 1 : from->scale.y; - SkScalar from_z = IsOperationIdentity(from) ? 1 : from->scale.z; - SkScalar to_x = IsOperationIdentity(to) ? 1 : to->scale.x; - SkScalar to_y = IsOperationIdentity(to) ? 1 : to->scale.y; - SkScalar to_z = IsOperationIdentity(to) ? 1 : to->scale.z; - result->scale.x = BlendSkScalars(from_x, to_x, progress); - result->scale.y = BlendSkScalars(from_y, to_y, progress); - result->scale.z = BlendSkScalars(from_z, to_z, progress); - result->Bake(); - break; - } - case TransformOperation::TRANSFORM_OPERATION_SKEWX: - case TransformOperation::TRANSFORM_OPERATION_SKEWY: - case TransformOperation::TRANSFORM_OPERATION_SKEW: { - SkScalar from_x = IsOperationIdentity(from) ? 0 : from->skew.x; - SkScalar from_y = IsOperationIdentity(from) ? 0 : from->skew.y; - SkScalar to_x = IsOperationIdentity(to) ? 0 : to->skew.x; - SkScalar to_y = IsOperationIdentity(to) ? 0 : to->skew.y; - result->skew.x = BlendSkScalars(from_x, to_x, progress); - result->skew.y = BlendSkScalars(from_y, to_y, progress); - result->Bake(); - break; - } - case TransformOperation::TRANSFORM_OPERATION_PERSPECTIVE: { - SkScalar from_perspective_depth = - IsOperationIdentity(from) ? std::numeric_limits<SkScalar>::max() - : from->perspective_depth; - SkScalar to_perspective_depth = IsOperationIdentity(to) - ? std::numeric_limits<SkScalar>::max() - : to->perspective_depth; - if (from_perspective_depth == 0.f || to_perspective_depth == 0.f) - return false; - - SkScalar blended_perspective_depth = BlendSkScalars( - 1.f / from_perspective_depth, 1.f / to_perspective_depth, progress); - - if (blended_perspective_depth == 0.f) - return false; - - result->perspective_depth = 1.f / blended_perspective_depth; - result->Bake(); - break; - } - case TransformOperation::TRANSFORM_OPERATION_MATRIX: { - if (!IsOperationIdentity(to)) - result->matrix = to->matrix; - gfx::Transform from_matrix; - if (!IsOperationIdentity(from)) - from_matrix = from->matrix; - if (!result->matrix.Blend(from_matrix, progress)) - return false; - break; - } - case TransformOperation::TRANSFORM_OPERATION_IDENTITY: - // Do nothing. - break; - } - - return true; -} - -// If p = (px, py) is a point in the plane being rotated about (0, 0, nz), this -// function computes the angles we would have to rotate from p to get to -// (length(p), 0), (-length(p), 0), (0, length(p)), (0, -length(p)). If nz is -// negative, these angles will need to be reversed. -static void FindCandidatesInPlane(float px, - float py, - float nz, - double* candidates, - int* num_candidates) { - double phi = atan2(px, py); - *num_candidates = 4; - candidates[0] = phi; - for (int i = 1; i < *num_candidates; ++i) - candidates[i] = candidates[i - 1] + base::kPiDouble / 2; - if (nz < 0.f) { - for (int i = 0; i < *num_candidates; ++i) - candidates[i] *= -1.f; - } -} - -static void BoundingBoxForArc(const gfx::Point3F& point, - const TransformOperation* from, - const TransformOperation* to, - SkScalar min_progress, - SkScalar max_progress, - gfx::BoxF* box) { - const TransformOperation* exemplar = from ? from : to; - gfx::Vector3dF axis(exemplar->rotate.axis.x, - exemplar->rotate.axis.y, - exemplar->rotate.axis.z); - - const bool x_is_zero = axis.x() == 0.f; - const bool y_is_zero = axis.y() == 0.f; - const bool z_is_zero = axis.z() == 0.f; - - // We will have at most 6 angles to test (excluding from->angle and - // to->angle). - static const int kMaxNumCandidates = 6; - double candidates[kMaxNumCandidates]; - int num_candidates = kMaxNumCandidates; - - if (x_is_zero && y_is_zero && z_is_zero) - return; - - SkScalar from_angle = from ? from->rotate.angle : 0.f; - SkScalar to_angle = to ? to->rotate.angle : 0.f; - - // If the axes of rotation are pointing in opposite directions, we need to - // flip one of the angles. Note, if both |from| and |to| exist, then axis will - // correspond to |from|. - if (from && to) { - gfx::Vector3dF other_axis( - to->rotate.axis.x, to->rotate.axis.y, to->rotate.axis.z); - if (gfx::DotProduct(axis, other_axis) < 0.f) - to_angle *= -1.f; - } - - float min_degrees = - SkScalarToFloat(BlendSkScalars(from_angle, to_angle, min_progress)); - float max_degrees = - SkScalarToFloat(BlendSkScalars(from_angle, to_angle, max_progress)); - if (max_degrees < min_degrees) - std::swap(min_degrees, max_degrees); - - gfx::Transform from_transform; - from_transform.RotateAbout(axis, min_degrees); - gfx::Transform to_transform; - to_transform.RotateAbout(axis, max_degrees); - - *box = gfx::BoxF(); - - gfx::Point3F point_rotated_from = point; - from_transform.TransformPoint(&point_rotated_from); - gfx::Point3F point_rotated_to = point; - to_transform.TransformPoint(&point_rotated_to); - - box->set_origin(point_rotated_from); - box->ExpandTo(point_rotated_to); - - if (x_is_zero && y_is_zero) { - FindCandidatesInPlane( - point.x(), point.y(), axis.z(), candidates, &num_candidates); - } else if (x_is_zero && z_is_zero) { - FindCandidatesInPlane( - point.z(), point.x(), axis.y(), candidates, &num_candidates); - } else if (y_is_zero && z_is_zero) { - FindCandidatesInPlane( - point.y(), point.z(), axis.x(), candidates, &num_candidates); - } else { - gfx::Vector3dF normal = axis; - normal.Scale(1.f / normal.Length()); - - // First, find center of rotation. - gfx::Point3F origin; - gfx::Vector3dF to_point = point - origin; - gfx::Point3F center = - origin + gfx::ScaleVector3d(normal, gfx::DotProduct(to_point, normal)); - - // Now we need to find two vectors in the plane of rotation. One pointing - // towards point and another, perpendicular vector in the plane. - gfx::Vector3dF v1 = point - center; - float v1_length = v1.Length(); - if (v1_length == 0.f) - return; - - v1.Scale(1.f / v1_length); - gfx::Vector3dF v2 = gfx::CrossProduct(normal, v1); - // v1 is the basis vector in the direction of the point. - // i.e. with a rotation of 0, v1 is our +x vector. - // v2 is a perpenticular basis vector of our plane (+y). - - // Take the parametric equation of a circle. - // x = r*cos(t); y = r*sin(t); - // We can treat that as a circle on the plane v1xv2. - // From that we get the parametric equations for a circle on the - // plane in 3d space of: - // x(t) = r*cos(t)*v1.x + r*sin(t)*v2.x + cx - // y(t) = r*cos(t)*v1.y + r*sin(t)*v2.y + cy - // z(t) = r*cos(t)*v1.z + r*sin(t)*v2.z + cz - // Taking the derivative of (x, y, z) and solving for 0 gives us our - // maximum/minimum x, y, z values. - // x'(t) = r*cos(t)*v2.x - r*sin(t)*v1.x = 0 - // tan(t) = v2.x/v1.x - // t = atan2(v2.x, v1.x) + n*pi; - candidates[0] = atan2(v2.x(), v1.x()); - candidates[1] = candidates[0] + base::kPiDouble; - candidates[2] = atan2(v2.y(), v1.y()); - candidates[3] = candidates[2] + base::kPiDouble; - candidates[4] = atan2(v2.z(), v1.z()); - candidates[5] = candidates[4] + base::kPiDouble; - } - - double min_radians = gfx::DegToRad(min_degrees); - double max_radians = gfx::DegToRad(max_degrees); - - for (int i = 0; i < num_candidates; ++i) { - double radians = candidates[i]; - while (radians < min_radians) - radians += 2.0 * base::kPiDouble; - while (radians > max_radians) - radians -= 2.0 * base::kPiDouble; - if (radians < min_radians) - continue; - - gfx::Transform rotation; - rotation.RotateAbout(axis, gfx::RadToDeg(radians)); - gfx::Point3F rotated = point; - rotation.TransformPoint(&rotated); - - box->ExpandTo(rotated); - } -} - -bool TransformOperation::BlendedBoundsForBox(const gfx::BoxF& box, - const TransformOperation* from, - const TransformOperation* to, - SkScalar min_progress, - SkScalar max_progress, - gfx::BoxF* bounds) { - bool is_identity_from = IsOperationIdentity(from); - bool is_identity_to = IsOperationIdentity(to); - if (is_identity_from && is_identity_to) { - *bounds = box; - return true; - } - - TransformOperation::Type interpolation_type = - TransformOperation::TRANSFORM_OPERATION_IDENTITY; - if (is_identity_to) - interpolation_type = from->type; - else - interpolation_type = to->type; - - switch (interpolation_type) { - case TransformOperation::TRANSFORM_OPERATION_IDENTITY: - *bounds = box; - return true; - case TransformOperation::TRANSFORM_OPERATION_TRANSLATE: - case TransformOperation::TRANSFORM_OPERATION_SKEWX: - case TransformOperation::TRANSFORM_OPERATION_SKEWY: - case TransformOperation::TRANSFORM_OPERATION_SKEW: - case TransformOperation::TRANSFORM_OPERATION_PERSPECTIVE: - case TransformOperation::TRANSFORM_OPERATION_SCALE: { - TransformOperation from_operation; - TransformOperation to_operation; - if (!BlendTransformOperations(from, to, min_progress, &from_operation) || - !BlendTransformOperations(from, to, max_progress, &to_operation)) - return false; - - *bounds = box; - from_operation.matrix.TransformBox(bounds); - - gfx::BoxF to_box = box; - to_operation.matrix.TransformBox(&to_box); - bounds->ExpandTo(to_box); - - return true; - } - case TransformOperation::TRANSFORM_OPERATION_ROTATE: { - SkScalar axis_x = 0; - SkScalar axis_y = 0; - SkScalar axis_z = 1; - SkScalar from_angle = 0; - if (!ShareSameAxis(from, to, &axis_x, &axis_y, &axis_z, &from_angle)) - return false; - - bool first_point = true; - for (int i = 0; i < 8; ++i) { - gfx::Point3F corner = box.origin(); - corner += gfx::Vector3dF(i & 1 ? box.width() : 0.f, - i & 2 ? box.height() : 0.f, - i & 4 ? box.depth() : 0.f); - gfx::BoxF box_for_arc; - BoundingBoxForArc( - corner, from, to, min_progress, max_progress, &box_for_arc); - if (first_point) - *bounds = box_for_arc; - else - bounds->Union(box_for_arc); - first_point = false; - } - return true; - } - case TransformOperation::TRANSFORM_OPERATION_MATRIX: - return false; - } - NOTREACHED(); - return false; -} - -} // namespace cc diff --git a/chromium/cc/animation/transform_operation.h b/chromium/cc/animation/transform_operation.h deleted file mode 100644 index b09accd6320..00000000000 --- a/chromium/cc/animation/transform_operation.h +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CC_ANIMATION_TRANSFORM_OPERATION_H_ -#define CC_ANIMATION_TRANSFORM_OPERATION_H_ - -#include "cc/animation/animation_export.h" -#include "ui/gfx/transform.h" - -namespace gfx { -class BoxF; -} - -namespace cc { - -struct CC_ANIMATION_EXPORT TransformOperation { - enum Type { - TRANSFORM_OPERATION_TRANSLATE, - TRANSFORM_OPERATION_ROTATE, - TRANSFORM_OPERATION_SCALE, - TRANSFORM_OPERATION_SKEWX, - TRANSFORM_OPERATION_SKEWY, - TRANSFORM_OPERATION_SKEW, - TRANSFORM_OPERATION_PERSPECTIVE, - TRANSFORM_OPERATION_MATRIX, - TRANSFORM_OPERATION_IDENTITY - }; - - TransformOperation() : type(TRANSFORM_OPERATION_IDENTITY) {} - - Type type; - gfx::Transform matrix; - - union { - SkScalar perspective_depth; - - struct { - SkScalar x, y; - } skew; - - struct { - SkScalar x, y, z; - } scale; - - struct { - SkScalar x, y, z; - } translate; - - struct { - struct { - SkScalar x, y, z; - } axis; - - SkScalar angle; - } rotate; - }; - - bool IsIdentity() const; - - // Sets |matrix| based on type and the union values. - void Bake(); - - bool ApproximatelyEqual(const TransformOperation& other, - SkScalar tolerance) const; - - static bool BlendTransformOperations(const TransformOperation* from, - const TransformOperation* to, - SkScalar progress, - TransformOperation* result); - - static bool BlendedBoundsForBox(const gfx::BoxF& box, - const TransformOperation* from, - const TransformOperation* to, - SkScalar min_progress, - SkScalar max_progress, - gfx::BoxF* bounds); -}; - -} // namespace cc - -#endif // CC_ANIMATION_TRANSFORM_OPERATION_H_ diff --git a/chromium/cc/animation/transform_operations.cc b/chromium/cc/animation/transform_operations.cc deleted file mode 100644 index 2f627f5af10..00000000000 --- a/chromium/cc/animation/transform_operations.cc +++ /dev/null @@ -1,387 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "cc/animation/transform_operations.h" - -#include <stddef.h> - -#include <algorithm> -#include <utility> - -#include "cc/base/math_util.h" -#include "ui/gfx/animation/tween.h" -#include "ui/gfx/geometry/angle_conversions.h" -#include "ui/gfx/geometry/box_f.h" -#include "ui/gfx/geometry/vector3d_f.h" -#include "ui/gfx/transform_util.h" - -namespace cc { - -TransformOperations::TransformOperations() {} - -TransformOperations::TransformOperations(const TransformOperations& other) { - operations_ = other.operations_; -} - -TransformOperations::~TransformOperations() = default; - -TransformOperations& TransformOperations::operator=( - const TransformOperations& other) { - operations_ = other.operations_; - return *this; -} - -gfx::Transform TransformOperations::Apply() const { - return ApplyRemaining(0); -} - -gfx::Transform TransformOperations::ApplyRemaining(size_t start) const { - gfx::Transform to_return; - for (size_t i = start; i < operations_.size(); i++) { - to_return.PreconcatTransform(operations_[i].matrix); - } - return to_return; -} - -// TODO(crbug.com/914397): Consolidate blink and cc implementations of transform -// interpolation. -TransformOperations TransformOperations::Blend(const TransformOperations& from, - SkScalar progress) const { - TransformOperations to_return; - if (!BlendInternal(from, progress, &to_return)) { - // If the matrices cannot be blended, fallback to discrete animation logic. - // See https://drafts.csswg.org/css-transforms/#matrix-interpolation - to_return = progress < 0.5 ? from : *this; - } - return to_return; -} - -bool TransformOperations::BlendedBoundsForBox(const gfx::BoxF& box, - const TransformOperations& from, - SkScalar min_progress, - SkScalar max_progress, - gfx::BoxF* bounds) const { - *bounds = box; - - bool from_identity = from.IsIdentity(); - bool to_identity = IsIdentity(); - if (from_identity && to_identity) - return true; - - if (!MatchesTypes(from)) - return false; - - size_t num_operations = std::max(from_identity ? 0 : from.operations_.size(), - to_identity ? 0 : operations_.size()); - - // Because we are squashing all of the matrices together when applying - // them to the animation, we must apply them in reverse order when - // not squashing them. - for (size_t i = 0; i < num_operations; ++i) { - size_t operation_index = num_operations - 1 - i; - gfx::BoxF bounds_for_operation; - const TransformOperation* from_op = - from_identity ? nullptr : &from.operations_[operation_index]; - const TransformOperation* to_op = - to_identity ? nullptr : &operations_[operation_index]; - if (!TransformOperation::BlendedBoundsForBox(*bounds, from_op, to_op, - min_progress, max_progress, - &bounds_for_operation)) { - return false; - } - *bounds = bounds_for_operation; - } - - return true; -} - -bool TransformOperations::PreservesAxisAlignment() const { - for (auto& operation : operations_) { - switch (operation.type) { - case TransformOperation::TRANSFORM_OPERATION_IDENTITY: - case TransformOperation::TRANSFORM_OPERATION_TRANSLATE: - case TransformOperation::TRANSFORM_OPERATION_SCALE: - continue; - case TransformOperation::TRANSFORM_OPERATION_MATRIX: - if (!operation.matrix.IsIdentity() && - !operation.matrix.IsScaleOrTranslation()) - return false; - continue; - case TransformOperation::TRANSFORM_OPERATION_ROTATE: - case TransformOperation::TRANSFORM_OPERATION_SKEWX: - case TransformOperation::TRANSFORM_OPERATION_SKEWY: - case TransformOperation::TRANSFORM_OPERATION_SKEW: - case TransformOperation::TRANSFORM_OPERATION_PERSPECTIVE: - return false; - } - } - return true; -} - -bool TransformOperations::IsTranslation() const { - for (auto& operation : operations_) { - switch (operation.type) { - case TransformOperation::TRANSFORM_OPERATION_IDENTITY: - case TransformOperation::TRANSFORM_OPERATION_TRANSLATE: - continue; - case TransformOperation::TRANSFORM_OPERATION_MATRIX: - if (!operation.matrix.IsIdentityOrTranslation()) - return false; - continue; - case TransformOperation::TRANSFORM_OPERATION_ROTATE: - case TransformOperation::TRANSFORM_OPERATION_SCALE: - case TransformOperation::TRANSFORM_OPERATION_SKEWX: - case TransformOperation::TRANSFORM_OPERATION_SKEWY: - case TransformOperation::TRANSFORM_OPERATION_SKEW: - case TransformOperation::TRANSFORM_OPERATION_PERSPECTIVE: - return false; - } - } - return true; -} - -static SkScalar TanDegrees(double degrees) { - return SkDoubleToScalar(std::tan(gfx::DegToRad(degrees))); -} - -bool TransformOperations::ScaleComponent(SkScalar* scale) const { - SkScalar operations_scale = 1.f; - for (auto& operation : operations_) { - switch (operation.type) { - case TransformOperation::TRANSFORM_OPERATION_IDENTITY: - case TransformOperation::TRANSFORM_OPERATION_TRANSLATE: - case TransformOperation::TRANSFORM_OPERATION_ROTATE: - continue; - case TransformOperation::TRANSFORM_OPERATION_MATRIX: { - if (operation.matrix.HasPerspective()) - return false; - gfx::Vector2dF scale_components = - MathUtil::ComputeTransform2dScaleComponents(operation.matrix, 1.f); - operations_scale *= - std::max(scale_components.x(), scale_components.y()); - break; - } - case TransformOperation::TRANSFORM_OPERATION_SKEWX: - case TransformOperation::TRANSFORM_OPERATION_SKEWY: - case TransformOperation::TRANSFORM_OPERATION_SKEW: { - SkScalar x_component = TanDegrees(operation.skew.x); - SkScalar y_component = TanDegrees(operation.skew.y); - SkScalar x_scale = std::sqrt(x_component * x_component + 1); - SkScalar y_scale = std::sqrt(y_component * y_component + 1); - operations_scale *= std::max(x_scale, y_scale); - break; - } - case TransformOperation::TRANSFORM_OPERATION_PERSPECTIVE: - return false; - case TransformOperation::TRANSFORM_OPERATION_SCALE: - operations_scale *= std::max( - std::abs(operation.scale.x), - std::max(std::abs(operation.scale.y), std::abs(operation.scale.z))); - } - } - *scale = operations_scale; - return true; -} - -bool TransformOperations::MatchesTypes(const TransformOperations& other) const { - if (operations_.size() == 0 || other.operations_.size() == 0) - return true; - - if (operations_.size() != other.operations_.size()) - return false; - - for (size_t i = 0; i < operations_.size(); ++i) { - if (operations_[i].type != other.operations_[i].type) - return false; - } - - return true; -} - -size_t TransformOperations::MatchingPrefixLength( - const TransformOperations& other) const { - size_t num_operations = - std::min(operations_.size(), other.operations_.size()); - for (size_t i = 0; i < num_operations; ++i) { - if (operations_[i].type != other.operations_[i].type) { - // Remaining operations in each operations list require matrix/matrix3d - // interpolation. - return i; - } - } - // If the operations match to the length of the shorter list, then pad its - // length with the matching identity operations. - // https://drafts.csswg.org/css-transforms/#transform-function-lists - return std::max(operations_.size(), other.operations_.size()); -} - -bool TransformOperations::CanBlendWith( - const TransformOperations& other) const { - TransformOperations dummy; - return BlendInternal(other, 0.5, &dummy); -} - -void TransformOperations::AppendTranslate(SkScalar x, SkScalar y, SkScalar z) { - TransformOperation to_add; - to_add.matrix.Translate3d(x, y, z); - to_add.type = TransformOperation::TRANSFORM_OPERATION_TRANSLATE; - to_add.translate.x = x; - to_add.translate.y = y; - to_add.translate.z = z; - operations_.push_back(to_add); - decomposed_transforms_.clear(); -} - -void TransformOperations::AppendRotate(SkScalar x, - SkScalar y, - SkScalar z, - SkScalar degrees) { - TransformOperation to_add; - to_add.type = TransformOperation::TRANSFORM_OPERATION_ROTATE; - to_add.rotate.axis.x = x; - to_add.rotate.axis.y = y; - to_add.rotate.axis.z = z; - to_add.rotate.angle = degrees; - to_add.Bake(); - operations_.push_back(to_add); - decomposed_transforms_.clear(); -} - -void TransformOperations::AppendScale(SkScalar x, SkScalar y, SkScalar z) { - TransformOperation to_add; - to_add.type = TransformOperation::TRANSFORM_OPERATION_SCALE; - to_add.scale.x = x; - to_add.scale.y = y; - to_add.scale.z = z; - to_add.Bake(); - operations_.push_back(to_add); - decomposed_transforms_.clear(); -} - -void TransformOperations::AppendSkewX(SkScalar x) { - TransformOperation to_add; - to_add.type = TransformOperation::TRANSFORM_OPERATION_SKEWX; - to_add.skew.x = x; - to_add.skew.y = 0; - to_add.Bake(); - operations_.push_back(to_add); - decomposed_transforms_.clear(); -} - -void TransformOperations::AppendSkewY(SkScalar y) { - TransformOperation to_add; - to_add.type = TransformOperation::TRANSFORM_OPERATION_SKEWY; - to_add.skew.x = 0; - to_add.skew.y = y; - to_add.Bake(); - operations_.push_back(to_add); - decomposed_transforms_.clear(); -} - -void TransformOperations::AppendSkew(SkScalar x, SkScalar y) { - TransformOperation to_add; - to_add.type = TransformOperation::TRANSFORM_OPERATION_SKEW; - to_add.skew.x = x; - to_add.skew.y = y; - to_add.Bake(); - operations_.push_back(to_add); - decomposed_transforms_.clear(); -} - -void TransformOperations::AppendPerspective(SkScalar depth) { - TransformOperation to_add; - to_add.type = TransformOperation::TRANSFORM_OPERATION_PERSPECTIVE; - to_add.perspective_depth = depth; - to_add.Bake(); - operations_.push_back(to_add); - decomposed_transforms_.clear(); -} - -void TransformOperations::AppendMatrix(const gfx::Transform& matrix) { - TransformOperation to_add; - to_add.matrix = matrix; - to_add.type = TransformOperation::TRANSFORM_OPERATION_MATRIX; - operations_.push_back(to_add); - decomposed_transforms_.clear(); -} - -void TransformOperations::AppendIdentity() { - operations_.push_back(TransformOperation()); -} - -void TransformOperations::Append(const TransformOperation& operation) { - operations_.push_back(operation); - decomposed_transforms_.clear(); -} - -bool TransformOperations::IsIdentity() const { - for (auto& operation : operations_) { - if (!operation.IsIdentity()) - return false; - } - return true; -} - -bool TransformOperations::ApproximatelyEqual(const TransformOperations& other, - SkScalar tolerance) const { - if (size() != other.size()) - return false; - for (size_t i = 0; i < operations_.size(); ++i) { - if (!operations_[i].ApproximatelyEqual(other.operations_[i], tolerance)) - return false; - } - return true; -} - -bool TransformOperations::BlendInternal(const TransformOperations& from, - SkScalar progress, - TransformOperations* result) const { - bool from_identity = from.IsIdentity(); - bool to_identity = IsIdentity(); - if (from_identity && to_identity) - return true; - - size_t matching_prefix_length = MatchingPrefixLength(from); - size_t from_size = from_identity ? 0 : from.operations_.size(); - size_t to_size = to_identity ? 0 : operations_.size(); - size_t num_operations = std::max(from_size, to_size); - - for (size_t i = 0; i < matching_prefix_length; ++i) { - TransformOperation blended; - if (!TransformOperation::BlendTransformOperations( - i >= from_size ? nullptr : &from.operations_[i], - i >= to_size ? nullptr : &operations_[i], progress, &blended)) { - return false; - } - result->Append(blended); - } - - if (matching_prefix_length < num_operations) { - if (!ComputeDecomposedTransform(matching_prefix_length) || - !from.ComputeDecomposedTransform(matching_prefix_length)) { - return false; - } - gfx::DecomposedTransform matrix_transform = gfx::BlendDecomposedTransforms( - *decomposed_transforms_[matching_prefix_length].get(), - *from.decomposed_transforms_[matching_prefix_length].get(), progress); - result->AppendMatrix(ComposeTransform(matrix_transform)); - } - return true; -} - -bool TransformOperations::ComputeDecomposedTransform( - size_t start_offset) const { - auto it = decomposed_transforms_.find(start_offset); - if (it == decomposed_transforms_.end()) { - std::unique_ptr<gfx::DecomposedTransform> decomposed_transform = - std::make_unique<gfx::DecomposedTransform>(); - gfx::Transform transform = ApplyRemaining(start_offset); - if (!gfx::DecomposeTransform(decomposed_transform.get(), transform)) - return false; - decomposed_transforms_[start_offset] = std::move(decomposed_transform); - } - return true; -} - -} // namespace cc diff --git a/chromium/cc/animation/transform_operations.h b/chromium/cc/animation/transform_operations.h deleted file mode 100644 index 31d50342f45..00000000000 --- a/chromium/cc/animation/transform_operations.h +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CC_ANIMATION_TRANSFORM_OPERATIONS_H_ -#define CC_ANIMATION_TRANSFORM_OPERATIONS_H_ - -#include <memory> -#include <unordered_map> -#include <vector> - -#include "base/check_op.h" -#include "base/gtest_prod_util.h" -#include "cc/animation/animation_export.h" -#include "cc/animation/transform_operation.h" -#include "ui/gfx/transform.h" - -namespace gfx { -class BoxF; -struct DecomposedTransform; -} - -namespace cc { - -// Transform operations are a decomposed transformation matrix. It can be -// applied to obtain a gfx::Transform at any time, and can be blended -// intelligently with other transform operations, so long as they represent the -// same decomposition. For example, if we have a transform that is made up of -// a rotation followed by skew, it can be blended intelligently with another -// transform made up of a rotation followed by a skew. Blending is possible if -// we have two dissimilar sets of transform operations, but the effect may not -// be what was intended. For more information, see the comments for the blend -// function below. -class CC_ANIMATION_EXPORT TransformOperations { - public: - TransformOperations(); - TransformOperations(const TransformOperations& other); - ~TransformOperations(); - - TransformOperations& operator=(const TransformOperations& other); - - // Returns a transformation matrix representing these transform operations. - gfx::Transform Apply() const; - - // Returns a transformation matrix representing the set of transform - // operations from index |start| to the end of the list. - gfx::Transform ApplyRemaining(size_t start) const; - - // Given another set of transform operations and a progress in the range - // [0, 1], returns a transformation matrix representing the intermediate - // value. If this->MatchesTypes(from), then each of the operations are - // blended separately and then combined. Otherwise, the two sets of - // transforms are baked to matrices (using apply), and the matrices are - // then decomposed and interpolated. For more information, see - // http://www.w3.org/TR/2011/WD-css3-2d-transforms-20111215/#matrix-decomposition. - // - // If either of the matrices are non-decomposable for the blend, Blend applies - // discrete interpolation between them based on the progress value. - TransformOperations Blend(const TransformOperations& from, - SkScalar progress) const; - - // Sets |bounds| be the bounding box for the region within which |box| will - // exist when it is transformed by the result of calling Blend on |from| and - // with progress in the range [min_progress, max_progress]. If this region - // cannot be computed, returns false. - bool BlendedBoundsForBox(const gfx::BoxF& box, - const TransformOperations& from, - SkScalar min_progress, - SkScalar max_progress, - gfx::BoxF* bounds) const; - - // Returns true if these operations are only translations. - bool IsTranslation() const; - - // Returns false if the operations affect 2d axis alignment. - bool PreservesAxisAlignment() const; - - // Returns true if this operation and its descendants have the same types - // as other and its descendants. - bool MatchesTypes(const TransformOperations& other) const; - - // Returns the number of matching transform operations at the start of the - // transform lists. If one list is shorter but pairwise compatible, it will be - // extended with matching identity operators per spec - // (https://drafts.csswg.org/css-transforms/#interpolation-of-transforms). - size_t MatchingPrefixLength(const TransformOperations& other) const; - - // Returns true if these operations can be blended. It will only return - // false if we must resort to matrix interpolation, and matrix interpolation - // fails (this can happen if either matrix cannot be decomposed). - bool CanBlendWith(const TransformOperations& other) const; - - // If none of these operations have a perspective component, sets |scale| to - // be the product of the scale component of every operation. Otherwise, - // returns false. - bool ScaleComponent(SkScalar* scale) const; - - void AppendTranslate(SkScalar x, SkScalar y, SkScalar z); - void AppendRotate(SkScalar x, SkScalar y, SkScalar z, SkScalar degrees); - void AppendScale(SkScalar x, SkScalar y, SkScalar z); - void AppendSkewX(SkScalar x); - void AppendSkewY(SkScalar y); - void AppendSkew(SkScalar x, SkScalar y); - void AppendPerspective(SkScalar depth); - void AppendMatrix(const gfx::Transform& matrix); - void AppendIdentity(); - void Append(const TransformOperation& operation); - bool IsIdentity() const; - - size_t size() const { return operations_.size(); } - - const TransformOperation& at(size_t index) const { - DCHECK_LT(index, size()); - return operations_[index]; - } - TransformOperation& at(size_t index) { - DCHECK_LT(index, size()); - return operations_[index]; - } - - bool ApproximatelyEqual(const TransformOperations& other, - SkScalar tolerance) const; - - private: - FRIEND_TEST_ALL_PREFIXES(TransformOperationsTest, TestDecompositionCache); - - bool BlendInternal(const TransformOperations& from, - SkScalar progress, - TransformOperations* result) const; - - std::vector<TransformOperation> operations_; - - bool ComputeDecomposedTransform(size_t start_offset) const; - - // For efficiency, we cache the decomposed transforms. - mutable std::unordered_map<size_t, std::unique_ptr<gfx::DecomposedTransform>> - decomposed_transforms_; -}; - -} // namespace cc - -#endif // CC_ANIMATION_TRANSFORM_OPERATIONS_H_ diff --git a/chromium/cc/animation/transform_operations_unittest.cc b/chromium/cc/animation/transform_operations_unittest.cc deleted file mode 100644 index bdebd3c4be7..00000000000 --- a/chromium/cc/animation/transform_operations_unittest.cc +++ /dev/null @@ -1,1831 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "cc/animation/transform_operations.h" - -#include <stddef.h> - -#include <limits> -#include <utility> -#include <vector> - -#include "base/stl_util.h" -#include "cc/test/geometry_test_utils.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "ui/gfx/animation/tween.h" -#include "ui/gfx/geometry/box_f.h" -#include "ui/gfx/geometry/rect_conversions.h" -#include "ui/gfx/geometry/vector3d_f.h" - -namespace cc { -namespace { - -void ExpectTransformOperationEqual(const TransformOperation& lhs, - const TransformOperation& rhs) { - EXPECT_EQ(lhs.type, rhs.type); - EXPECT_TRANSFORMATION_MATRIX_EQ(lhs.matrix, rhs.matrix); - switch (lhs.type) { - case TransformOperation::TRANSFORM_OPERATION_TRANSLATE: - EXPECT_FLOAT_EQ(lhs.translate.x, rhs.translate.x); - EXPECT_FLOAT_EQ(lhs.translate.y, rhs.translate.y); - EXPECT_FLOAT_EQ(lhs.translate.z, rhs.translate.z); - break; - case TransformOperation::TRANSFORM_OPERATION_ROTATE: - EXPECT_FLOAT_EQ(lhs.rotate.axis.x, rhs.rotate.axis.x); - EXPECT_FLOAT_EQ(lhs.rotate.axis.y, rhs.rotate.axis.y); - EXPECT_FLOAT_EQ(lhs.rotate.axis.z, rhs.rotate.axis.z); - EXPECT_FLOAT_EQ(lhs.rotate.angle, rhs.rotate.angle); - break; - case TransformOperation::TRANSFORM_OPERATION_SCALE: - EXPECT_FLOAT_EQ(lhs.scale.x, rhs.scale.x); - EXPECT_FLOAT_EQ(lhs.scale.y, rhs.scale.y); - EXPECT_FLOAT_EQ(lhs.scale.z, rhs.scale.z); - break; - case TransformOperation::TRANSFORM_OPERATION_SKEWX: - case TransformOperation::TRANSFORM_OPERATION_SKEWY: - case TransformOperation::TRANSFORM_OPERATION_SKEW: - EXPECT_FLOAT_EQ(lhs.skew.x, rhs.skew.x); - EXPECT_FLOAT_EQ(lhs.skew.y, rhs.skew.y); - break; - case TransformOperation::TRANSFORM_OPERATION_PERSPECTIVE: - EXPECT_FLOAT_EQ(lhs.perspective_depth, rhs.perspective_depth); - break; - case TransformOperation::TRANSFORM_OPERATION_MATRIX: - case TransformOperation::TRANSFORM_OPERATION_IDENTITY: - break; - } -} - -TEST(TransformOperationTest, TransformTypesAreUnique) { - std::vector<std::unique_ptr<TransformOperations>> transforms; - - std::unique_ptr<TransformOperations> to_add( - std::make_unique<TransformOperations>()); - to_add->AppendTranslate(1, 0, 0); - transforms.push_back(std::move(to_add)); - - to_add = std::make_unique<TransformOperations>(); - to_add->AppendRotate(0, 0, 1, 2); - transforms.push_back(std::move(to_add)); - - to_add = std::make_unique<TransformOperations>(); - to_add->AppendScale(2, 2, 2); - transforms.push_back(std::move(to_add)); - - to_add = std::make_unique<TransformOperations>(); - to_add->AppendSkew(1, 0); - transforms.push_back(std::move(to_add)); - - to_add = std::make_unique<TransformOperations>(); - to_add->AppendPerspective(800); - transforms.push_back(std::move(to_add)); - - for (size_t i = 0; i < transforms.size(); ++i) { - for (size_t j = 0; j < transforms.size(); ++j) { - bool matches_type = transforms[i]->MatchesTypes(*transforms[j]); - EXPECT_TRUE((i == j && matches_type) || !matches_type); - } - } -} - -TEST(TransformOperationTest, MatchingPrefixSameLength) { - TransformOperations translates; - translates.AppendTranslate(1, 0, 0); - translates.AppendTranslate(1, 0, 0); - translates.AppendTranslate(1, 0, 0); - - TransformOperations skews; - skews.AppendSkew(0, 2); - skews.AppendSkew(0, 2); - skews.AppendSkew(0, 2); - - TransformOperations translates2; - translates2.AppendTranslate(0, 2, 0); - translates2.AppendTranslate(0, 2, 0); - translates2.AppendTranslate(0, 2, 0); - - TransformOperations mixed; - mixed.AppendTranslate(0, 2, 0); - mixed.AppendScale(2, 1, 1); - mixed.AppendSkew(0, 2); - - TransformOperations translates3 = translates2; - - EXPECT_EQ(0UL, translates.MatchingPrefixLength(skews)); - EXPECT_EQ(3UL, translates.MatchingPrefixLength(translates2)); - EXPECT_EQ(3UL, translates.MatchingPrefixLength(translates3)); - EXPECT_EQ(1UL, translates.MatchingPrefixLength(mixed)); -} - -TEST(TransformOperationTest, MatchingPrefixDifferentLength) { - TransformOperations translates; - translates.AppendTranslate(1, 0, 0); - translates.AppendTranslate(1, 0, 0); - translates.AppendTranslate(1, 0, 0); - - TransformOperations skews; - skews.AppendSkew(2, 0); - skews.AppendSkew(2, 0); - - TransformOperations translates2; - translates2.AppendTranslate(0, 2, 0); - translates2.AppendTranslate(0, 2, 0); - - TransformOperations none; - - EXPECT_EQ(0UL, translates.MatchingPrefixLength(skews)); - // Pad the length of the shorter list provided all previous operation- - // pairs match per spec - // (https://drafts.csswg.org/css-transforms/#interpolation-of-transforms). - EXPECT_EQ(3UL, translates.MatchingPrefixLength(translates2)); - EXPECT_EQ(3UL, translates.MatchingPrefixLength(none)); -} - -std::vector<std::unique_ptr<TransformOperations>> GetIdentityOperations() { - std::vector<std::unique_ptr<TransformOperations>> operations; - std::unique_ptr<TransformOperations> to_add( - std::make_unique<TransformOperations>()); - operations.push_back(std::move(to_add)); - - to_add = std::make_unique<TransformOperations>(); - to_add->AppendTranslate(0, 0, 0); - operations.push_back(std::move(to_add)); - - to_add = std::make_unique<TransformOperations>(); - to_add->AppendTranslate(0, 0, 0); - to_add->AppendTranslate(0, 0, 0); - operations.push_back(std::move(to_add)); - - to_add = std::make_unique<TransformOperations>(); - to_add->AppendScale(1, 1, 1); - operations.push_back(std::move(to_add)); - - to_add = std::make_unique<TransformOperations>(); - to_add->AppendScale(1, 1, 1); - to_add->AppendScale(1, 1, 1); - operations.push_back(std::move(to_add)); - - to_add = std::make_unique<TransformOperations>(); - to_add->AppendSkew(0, 0); - operations.push_back(std::move(to_add)); - - to_add = std::make_unique<TransformOperations>(); - to_add->AppendSkew(0, 0); - to_add->AppendSkew(0, 0); - operations.push_back(std::move(to_add)); - - to_add = std::make_unique<TransformOperations>(); - to_add->AppendRotate(0, 0, 1, 0); - operations.push_back(std::move(to_add)); - - to_add = std::make_unique<TransformOperations>(); - to_add->AppendRotate(0, 0, 1, 0); - to_add->AppendRotate(0, 0, 1, 0); - operations.push_back(std::move(to_add)); - - to_add = std::make_unique<TransformOperations>(); - to_add->AppendMatrix(gfx::Transform()); - operations.push_back(std::move(to_add)); - - to_add = std::make_unique<TransformOperations>(); - to_add->AppendMatrix(gfx::Transform()); - to_add->AppendMatrix(gfx::Transform()); - operations.push_back(std::move(to_add)); - - return operations; -} - -TEST(TransformOperationTest, MatchingPrefixLengthOrder) { - TransformOperations mix_order_identity; - mix_order_identity.AppendTranslate(0, 0, 0); - mix_order_identity.AppendScale(1, 1, 1); - mix_order_identity.AppendTranslate(0, 0, 0); - - TransformOperations mix_order_one; - mix_order_one.AppendTranslate(0, 1, 0); - mix_order_one.AppendScale(2, 1, 3); - mix_order_one.AppendTranslate(1, 0, 0); - - TransformOperations mix_order_two; - mix_order_two.AppendTranslate(0, 1, 0); - mix_order_two.AppendTranslate(1, 0, 0); - mix_order_two.AppendScale(2, 1, 3); - - EXPECT_EQ(3UL, mix_order_identity.MatchingPrefixLength(mix_order_one)); - EXPECT_EQ(1UL, mix_order_identity.MatchingPrefixLength(mix_order_two)); - EXPECT_EQ(1UL, mix_order_one.MatchingPrefixLength(mix_order_two)); -} - -TEST(TransformOperationTest, NoneAlwaysMatches) { - std::vector<std::unique_ptr<TransformOperations>> operations = - GetIdentityOperations(); - - TransformOperations none_operation; - for (size_t i = 0; i < operations.size(); ++i) - EXPECT_EQ(operations[i]->size(), - operations[i]->MatchingPrefixLength(none_operation)); -} - -TEST(TransformOperationTest, ApplyTranslate) { - SkScalar x = 1; - SkScalar y = 2; - SkScalar z = 3; - TransformOperations operations; - operations.AppendTranslate(x, y, z); - gfx::Transform expected; - expected.Translate3d(x, y, z); - EXPECT_TRANSFORMATION_MATRIX_EQ(expected, operations.Apply()); -} - -TEST(TransformOperationTest, ApplyRotate) { - SkScalar x = 1; - SkScalar y = 2; - SkScalar z = 3; - SkScalar degrees = 80; - TransformOperations operations; - operations.AppendRotate(x, y, z, degrees); - gfx::Transform expected; - expected.RotateAbout(gfx::Vector3dF(x, y, z), degrees); - EXPECT_TRANSFORMATION_MATRIX_EQ(expected, operations.Apply()); -} - -TEST(TransformOperationTest, ApplyScale) { - SkScalar x = 1; - SkScalar y = 2; - SkScalar z = 3; - TransformOperations operations; - operations.AppendScale(x, y, z); - gfx::Transform expected; - expected.Scale3d(x, y, z); - EXPECT_TRANSFORMATION_MATRIX_EQ(expected, operations.Apply()); -} - -TEST(TransformOperationTest, ApplySkew) { - SkScalar x = 1; - SkScalar y = 2; - TransformOperations operations; - operations.AppendSkew(x, y); - gfx::Transform expected; - expected.Skew(x, y); - EXPECT_TRANSFORMATION_MATRIX_EQ(expected, operations.Apply()); -} - -TEST(TransformOperationTest, ApplyPerspective) { - SkScalar depth = 800; - TransformOperations operations; - operations.AppendPerspective(depth); - gfx::Transform expected; - expected.ApplyPerspectiveDepth(depth); - EXPECT_TRANSFORMATION_MATRIX_EQ(expected, operations.Apply()); -} - -TEST(TransformOperationTest, ApplyMatrix) { - SkScalar dx = 1; - SkScalar dy = 2; - SkScalar dz = 3; - gfx::Transform expected_matrix; - expected_matrix.Translate3d(dx, dy, dz); - TransformOperations matrix_transform; - matrix_transform.AppendMatrix(expected_matrix); - EXPECT_TRANSFORMATION_MATRIX_EQ(expected_matrix, matrix_transform.Apply()); -} - -TEST(TransformOperationTest, ApplyOrder) { - SkScalar sx = 2; - SkScalar sy = 4; - SkScalar sz = 8; - - SkScalar dx = 1; - SkScalar dy = 2; - SkScalar dz = 3; - - TransformOperations operations; - operations.AppendScale(sx, sy, sz); - operations.AppendTranslate(dx, dy, dz); - - gfx::Transform expected_scale_matrix; - expected_scale_matrix.Scale3d(sx, sy, sz); - - gfx::Transform expected_translate_matrix; - expected_translate_matrix.Translate3d(dx, dy, dz); - - gfx::Transform expected_combined_matrix = expected_scale_matrix; - expected_combined_matrix.PreconcatTransform(expected_translate_matrix); - - EXPECT_TRANSFORMATION_MATRIX_EQ(expected_combined_matrix, operations.Apply()); -} - -TEST(TransformOperationTest, BlendOrder) { - SkScalar sx1 = 2; - SkScalar sy1 = 4; - SkScalar sz1 = 8; - - SkScalar dx1 = 1; - SkScalar dy1 = 2; - SkScalar dz1 = 3; - - SkScalar sx2 = 4; - SkScalar sy2 = 8; - SkScalar sz2 = 16; - - SkScalar dx2 = 10; - SkScalar dy2 = 20; - SkScalar dz2 = 30; - - SkScalar sx3 = 2; - SkScalar sy3 = 1; - SkScalar sz3 = 1; - - TransformOperations operations_from; - operations_from.AppendScale(sx1, sy1, sz1); - operations_from.AppendTranslate(dx1, dy1, dz1); - - TransformOperations operations_to; - operations_to.AppendScale(sx2, sy2, sz2); - operations_to.AppendTranslate(dx2, dy2, dz2); - - gfx::Transform scale_from; - scale_from.Scale3d(sx1, sy1, sz1); - gfx::Transform translate_from; - translate_from.Translate3d(dx1, dy1, dz1); - - gfx::Transform scale_to; - scale_to.Scale3d(sx2, sy2, sz2); - gfx::Transform translate_to; - translate_to.Translate3d(dx2, dy2, dz2); - - SkScalar progress = 0.25f; - - TransformOperations operations_expected; - operations_expected.AppendScale( - gfx::Tween::FloatValueBetween(progress, sx1, sx2), - gfx::Tween::FloatValueBetween(progress, sy1, sy2), - gfx::Tween::FloatValueBetween(progress, sz1, sz2)); - - operations_expected.AppendTranslate( - gfx::Tween::FloatValueBetween(progress, dx1, dx2), - gfx::Tween::FloatValueBetween(progress, dy1, dy2), - gfx::Tween::FloatValueBetween(progress, dz1, dz2)); - - gfx::Transform blended_scale = scale_to; - blended_scale.Blend(scale_from, progress); - - gfx::Transform blended_translate = translate_to; - blended_translate.Blend(translate_from, progress); - - gfx::Transform expected = blended_scale; - expected.PreconcatTransform(blended_translate); - - TransformOperations blended = operations_to.Blend(operations_from, progress); - - EXPECT_TRANSFORMATION_MATRIX_EQ(expected, blended.Apply()); - EXPECT_TRANSFORMATION_MATRIX_EQ(operations_expected.Apply(), blended.Apply()); - EXPECT_EQ(operations_expected.size(), blended.size()); - for (size_t i = 0; i < operations_expected.size(); ++i) { - TransformOperation expected_op = operations_expected.at(i); - TransformOperation blended_op = blended.at(i); - SCOPED_TRACE(i); - ExpectTransformOperationEqual(expected_op, blended_op); - } - - TransformOperations base_operations_expected = operations_expected; - - // Create a mismatch in number of operations. Pairwise interpolation is still - // used when the operations match up to the length of the shorter list. - operations_to.AppendScale(sx3, sy3, sz3); - - gfx::Transform appended_scale; - appended_scale.Scale3d(sx3, sy3, sz3); - - gfx::Transform blended_append_scale = appended_scale; - blended_append_scale.Blend(gfx::Transform(), progress); - expected.PreconcatTransform(blended_append_scale); - - operations_expected.AppendScale( - gfx::Tween::FloatValueBetween(progress, 1, sx3), - gfx::Tween::FloatValueBetween(progress, 1, sy3), - gfx::Tween::FloatValueBetween(progress, 1, sz3)); - - blended = operations_to.Blend(operations_from, progress); - - EXPECT_TRANSFORMATION_MATRIX_EQ(expected, blended.Apply()); - EXPECT_TRANSFORMATION_MATRIX_EQ(operations_expected.Apply(), blended.Apply()); - EXPECT_EQ(operations_expected.size(), blended.size()); - for (size_t i = 0; i < operations_expected.size(); ++i) { - TransformOperation expected_op = operations_expected.at(i); - TransformOperation blended_op = blended.at(i); - SCOPED_TRACE(i); - ExpectTransformOperationEqual(expected_op, blended_op); - } - - // Create a mismatch, forcing matrix interpolation for the last operator pair. - operations_from.AppendRotate(0, 0, 1, 90); - - blended = operations_to.Blend(operations_from, progress); - - gfx::Transform transform_from; - transform_from.RotateAboutZAxis(90); - gfx::Transform transform_to; - transform_to.Scale3d(sx3, sy3, sz3); - gfx::Transform blended_matrix = transform_to; - blended_matrix.Blend(transform_from, progress); - - expected = blended_scale; - expected.PreconcatTransform(blended_translate); - expected.PreconcatTransform(blended_matrix); - - operations_expected = base_operations_expected; - operations_expected.AppendMatrix(blended_matrix); - - EXPECT_TRANSFORMATION_MATRIX_EQ(expected, blended.Apply()); - EXPECT_TRANSFORMATION_MATRIX_EQ(operations_expected.Apply(), blended.Apply()); - EXPECT_EQ(operations_expected.size(), blended.size()); - for (size_t i = 0; i < operations_expected.size(); ++i) { - TransformOperation expected_op = operations_expected.at(i); - TransformOperation blended_op = blended.at(i); - SCOPED_TRACE(i); - ExpectTransformOperationEqual(expected_op, blended_op); - } -} - -static void CheckProgress(SkScalar progress, - const gfx::Transform& from_matrix, - const gfx::Transform& to_matrix, - const TransformOperations& from_transform, - const TransformOperations& to_transform) { - gfx::Transform expected_matrix = to_matrix; - expected_matrix.Blend(from_matrix, progress); - EXPECT_TRANSFORMATION_MATRIX_EQ( - expected_matrix, to_transform.Blend(from_transform, progress).Apply()); -} - -TEST(TransformOperationTest, BlendProgress) { - SkScalar sx = 2; - SkScalar sy = 4; - SkScalar sz = 8; - TransformOperations operations_from; - operations_from.AppendScale(sx, sy, sz); - - gfx::Transform matrix_from; - matrix_from.Scale3d(sx, sy, sz); - - sx = 4; - sy = 8; - sz = 16; - TransformOperations operations_to; - operations_to.AppendScale(sx, sy, sz); - - gfx::Transform matrix_to; - matrix_to.Scale3d(sx, sy, sz); - - CheckProgress(-1, matrix_from, matrix_to, operations_from, operations_to); - CheckProgress(0, matrix_from, matrix_to, operations_from, operations_to); - CheckProgress(0.25f, matrix_from, matrix_to, operations_from, operations_to); - CheckProgress(0.5f, matrix_from, matrix_to, operations_from, operations_to); - CheckProgress(1, matrix_from, matrix_to, operations_from, operations_to); - CheckProgress(2, matrix_from, matrix_to, operations_from, operations_to); -} - -TEST(TransformOperationTest, BlendWhenTypesDoNotMatch) { - SkScalar sx1 = 2; - SkScalar sy1 = 4; - SkScalar sz1 = 8; - - SkScalar dx1 = 1; - SkScalar dy1 = 2; - SkScalar dz1 = 3; - - SkScalar sx2 = 4; - SkScalar sy2 = 8; - SkScalar sz2 = 16; - - SkScalar dx2 = 10; - SkScalar dy2 = 20; - SkScalar dz2 = 30; - - TransformOperations operations_from; - operations_from.AppendScale(sx1, sy1, sz1); - operations_from.AppendTranslate(dx1, dy1, dz1); - - TransformOperations operations_to; - operations_to.AppendTranslate(dx2, dy2, dz2); - operations_to.AppendScale(sx2, sy2, sz2); - - gfx::Transform from; - from.Scale3d(sx1, sy1, sz1); - from.Translate3d(dx1, dy1, dz1); - - gfx::Transform to; - to.Translate3d(dx2, dy2, dz2); - to.Scale3d(sx2, sy2, sz2); - - SkScalar progress = 0.25f; - - gfx::Transform expected = to; - expected.Blend(from, progress); - - EXPECT_TRANSFORMATION_MATRIX_EQ( - expected, operations_to.Blend(operations_from, progress).Apply()); -} - -TEST(TransformOperationTest, LargeRotationsWithSameAxis) { - TransformOperations operations_from; - operations_from.AppendRotate(0, 0, 1, 0); - - TransformOperations operations_to; - operations_to.AppendRotate(0, 0, 2, 360); - - SkScalar progress = 0.5f; - - gfx::Transform expected; - expected.RotateAbout(gfx::Vector3dF(0, 0, 1), 180); - - EXPECT_TRANSFORMATION_MATRIX_EQ( - expected, operations_to.Blend(operations_from, progress).Apply()); -} - -TEST(TransformOperationTest, LargeRotationsWithSameAxisInDifferentDirection) { - TransformOperations operations_from; - operations_from.AppendRotate(0, 0, 1, 180); - - TransformOperations operations_to; - operations_to.AppendRotate(0, 0, -1, 180); - - SkScalar progress = 0.5f; - - gfx::Transform expected; - - EXPECT_TRANSFORMATION_MATRIX_EQ( - expected, operations_to.Blend(operations_from, progress).Apply()); -} - -TEST(TransformOperationTest, LargeRotationsWithDifferentAxes) { - TransformOperations operations_from; - operations_from.AppendRotate(0, 0, 1, 175); - - TransformOperations operations_to; - operations_to.AppendRotate(0, 1, 0, 175); - - SkScalar progress = 0.5f; - gfx::Transform matrix_from; - matrix_from.RotateAbout(gfx::Vector3dF(0, 0, 1), 175); - - gfx::Transform matrix_to; - matrix_to.RotateAbout(gfx::Vector3dF(0, 1, 0), 175); - - gfx::Transform expected = matrix_to; - expected.Blend(matrix_from, progress); - - EXPECT_TRANSFORMATION_MATRIX_EQ( - expected, operations_to.Blend(operations_from, progress).Apply()); -} - -TEST(TransformOperationTest, RotationFromZeroDegDifferentAxes) { - TransformOperations operations_from; - operations_from.AppendRotate(0, 0, 1, 0); - - TransformOperations operations_to; - operations_to.AppendRotate(0, 1, 0, 450); - - SkScalar progress = 0.5f; - gfx::Transform expected; - expected.RotateAbout(gfx::Vector3dF(0, 1, 0), 225); - EXPECT_TRANSFORMATION_MATRIX_EQ( - expected, operations_to.Blend(operations_from, progress).Apply()); -} - -TEST(TransformOperationTest, RotationFromZeroDegSameAxes) { - TransformOperations operations_from; - operations_from.AppendRotate(0, 0, 1, 0); - - TransformOperations operations_to; - operations_to.AppendRotate(0, 0, 1, 450); - - SkScalar progress = 0.5f; - gfx::Transform expected; - expected.RotateAbout(gfx::Vector3dF(0, 0, 1), 225); - EXPECT_TRANSFORMATION_MATRIX_EQ( - expected, operations_to.Blend(operations_from, progress).Apply()); -} - -TEST(TransformOperationTest, RotationToZeroDegDifferentAxes) { - TransformOperations operations_from; - operations_from.AppendRotate(0, 1, 0, 450); - - TransformOperations operations_to; - operations_to.AppendRotate(0, 0, 1, 0); - - SkScalar progress = 0.5f; - gfx::Transform expected; - expected.RotateAbout(gfx::Vector3dF(0, 1, 0), 225); - EXPECT_TRANSFORMATION_MATRIX_EQ( - expected, operations_to.Blend(operations_from, progress).Apply()); -} - -TEST(TransformOperationTest, RotationToZeroDegSameAxes) { - TransformOperations operations_from; - operations_from.AppendRotate(0, 0, 1, 450); - - TransformOperations operations_to; - operations_to.AppendRotate(0, 0, 1, 0); - - SkScalar progress = 0.5f; - gfx::Transform expected; - expected.RotateAbout(gfx::Vector3dF(0, 0, 1), 225); - EXPECT_TRANSFORMATION_MATRIX_EQ( - expected, operations_to.Blend(operations_from, progress).Apply()); -} - -TEST(TransformOperationTest, BlendRotationFromIdentity) { - std::vector<std::unique_ptr<TransformOperations>> identity_operations = - GetIdentityOperations(); - - for (size_t i = 0; i < identity_operations.size(); ++i) { - TransformOperations operations; - operations.AppendRotate(0, 0, 1, 90); - - SkScalar progress = 0.5f; - - gfx::Transform expected; - expected.RotateAbout(gfx::Vector3dF(0, 0, 1), 45); - - EXPECT_TRANSFORMATION_MATRIX_EQ( - expected, operations.Blend(*identity_operations[i], progress).Apply()); - - progress = -0.5f; - - expected.MakeIdentity(); - expected.RotateAbout(gfx::Vector3dF(0, 0, 1), -45); - - EXPECT_TRANSFORMATION_MATRIX_EQ( - expected, operations.Blend(*identity_operations[i], progress).Apply()); - - progress = 1.5f; - - expected.MakeIdentity(); - expected.RotateAbout(gfx::Vector3dF(0, 0, 1), 135); - - EXPECT_TRANSFORMATION_MATRIX_EQ( - expected, operations.Blend(*identity_operations[i], progress).Apply()); - } -} - -TEST(TransformOperationTest, BlendTranslationFromIdentity) { - std::vector<std::unique_ptr<TransformOperations>> identity_operations = - GetIdentityOperations(); - - for (size_t i = 0; i < identity_operations.size(); ++i) { - TransformOperations operations; - operations.AppendTranslate(2, 2, 2); - - SkScalar progress = 0.5f; - - gfx::Transform expected; - expected.Translate3d(1, 1, 1); - - EXPECT_TRANSFORMATION_MATRIX_EQ( - expected, operations.Blend(*identity_operations[i], progress).Apply()); - - progress = -0.5f; - - expected.MakeIdentity(); - expected.Translate3d(-1, -1, -1); - - EXPECT_TRANSFORMATION_MATRIX_EQ( - expected, operations.Blend(*identity_operations[i], progress).Apply()); - - progress = 1.5f; - - expected.MakeIdentity(); - expected.Translate3d(3, 3, 3); - - EXPECT_TRANSFORMATION_MATRIX_EQ( - expected, operations.Blend(*identity_operations[i], progress).Apply()); - } -} - -TEST(TransformOperationTest, BlendScaleFromIdentity) { - std::vector<std::unique_ptr<TransformOperations>> identity_operations = - GetIdentityOperations(); - - for (size_t i = 0; i < identity_operations.size(); ++i) { - TransformOperations operations; - operations.AppendScale(3, 3, 3); - - SkScalar progress = 0.5f; - - gfx::Transform expected; - expected.Scale3d(2, 2, 2); - - EXPECT_TRANSFORMATION_MATRIX_EQ( - expected, operations.Blend(*identity_operations[i], progress).Apply()); - - progress = -0.5f; - - expected.MakeIdentity(); - expected.Scale3d(0, 0, 0); - - EXPECT_TRANSFORMATION_MATRIX_EQ( - expected, operations.Blend(*identity_operations[i], progress).Apply()); - - progress = 1.5f; - - expected.MakeIdentity(); - expected.Scale3d(4, 4, 4); - - EXPECT_TRANSFORMATION_MATRIX_EQ( - expected, operations.Blend(*identity_operations[i], progress).Apply()); - } -} - -TEST(TransformOperationTest, BlendSkewFromEmpty) { - TransformOperations empty_operation; - - TransformOperations operations; - operations.AppendSkew(2, 2); - - SkScalar progress = 0.5f; - - gfx::Transform expected; - expected.Skew(1, 1); - - EXPECT_TRANSFORMATION_MATRIX_EQ( - expected, operations.Blend(empty_operation, progress).Apply()); - - progress = -0.5f; - - expected.MakeIdentity(); - expected.Skew(-1, -1); - - EXPECT_TRANSFORMATION_MATRIX_EQ( - expected, operations.Blend(empty_operation, progress).Apply()); - - progress = 1.5f; - - expected.MakeIdentity(); - expected.Skew(3, 3); - - EXPECT_TRANSFORMATION_MATRIX_EQ( - expected, operations.Blend(empty_operation, progress).Apply()); -} - -TEST(TransformOperationTest, BlendPerspectiveFromIdentity) { - std::vector<std::unique_ptr<TransformOperations>> identity_operations = - GetIdentityOperations(); - - for (size_t i = 0; i < identity_operations.size(); ++i) { - TransformOperations operations; - operations.AppendPerspective(1000); - - SkScalar progress = 0.5f; - - gfx::Transform expected; - expected.ApplyPerspectiveDepth(2000); - - EXPECT_TRANSFORMATION_MATRIX_EQ( - expected, operations.Blend(*identity_operations[i], progress).Apply()); - } -} - -TEST(TransformOperationTest, BlendRotationToIdentity) { - std::vector<std::unique_ptr<TransformOperations>> identity_operations = - GetIdentityOperations(); - - for (size_t i = 0; i < identity_operations.size(); ++i) { - TransformOperations operations; - operations.AppendRotate(0, 0, 1, 90); - - SkScalar progress = 0.5f; - - gfx::Transform expected; - expected.RotateAbout(gfx::Vector3dF(0, 0, 1), 45); - - EXPECT_TRANSFORMATION_MATRIX_EQ( - expected, identity_operations[i]->Blend(operations, progress).Apply()); - } -} - -TEST(TransformOperationTest, BlendTranslationToIdentity) { - std::vector<std::unique_ptr<TransformOperations>> identity_operations = - GetIdentityOperations(); - - for (size_t i = 0; i < identity_operations.size(); ++i) { - TransformOperations operations; - operations.AppendTranslate(2, 2, 2); - - SkScalar progress = 0.5f; - - gfx::Transform expected; - expected.Translate3d(1, 1, 1); - - EXPECT_TRANSFORMATION_MATRIX_EQ( - expected, identity_operations[i]->Blend(operations, progress).Apply()); - } -} - -TEST(TransformOperationTest, BlendScaleToIdentity) { - std::vector<std::unique_ptr<TransformOperations>> identity_operations = - GetIdentityOperations(); - - for (size_t i = 0; i < identity_operations.size(); ++i) { - TransformOperations operations; - operations.AppendScale(3, 3, 3); - - SkScalar progress = 0.5f; - - gfx::Transform expected; - expected.Scale3d(2, 2, 2); - - EXPECT_TRANSFORMATION_MATRIX_EQ( - expected, identity_operations[i]->Blend(operations, progress).Apply()); - } -} - -TEST(TransformOperationTest, BlendSkewToEmpty) { - TransformOperations empty_operation; - - TransformOperations operations; - operations.AppendSkew(2, 2); - - SkScalar progress = 0.5f; - - gfx::Transform expected; - expected.Skew(1, 1); - - EXPECT_TRANSFORMATION_MATRIX_EQ( - expected, empty_operation.Blend(operations, progress).Apply()); -} - -TEST(TransformOperationTest, BlendPerspectiveToIdentity) { - std::vector<std::unique_ptr<TransformOperations>> identity_operations = - GetIdentityOperations(); - - for (size_t i = 0; i < identity_operations.size(); ++i) { - TransformOperations operations; - operations.AppendPerspective(1000); - - SkScalar progress = 0.5f; - - gfx::Transform expected; - expected.ApplyPerspectiveDepth(2000); - - EXPECT_TRANSFORMATION_MATRIX_EQ( - expected, identity_operations[i]->Blend(operations, progress).Apply()); - } -} - -TEST(TransformOperationTest, ExtrapolatePerspectiveBlending) { - TransformOperations operations1; - operations1.AppendPerspective(1000); - - TransformOperations operations2; - operations2.AppendPerspective(500); - - gfx::Transform expected; - expected.ApplyPerspectiveDepth(400); - - EXPECT_TRANSFORMATION_MATRIX_EQ(expected, - operations1.Blend(operations2, -0.5).Apply()); - - expected.MakeIdentity(); - expected.ApplyPerspectiveDepth(2000); - - EXPECT_TRANSFORMATION_MATRIX_EQ(expected, - operations1.Blend(operations2, 1.5).Apply()); -} - -TEST(TransformOperationTest, ExtrapolateMatrixBlending) { - gfx::Transform transform1; - transform1.Translate3d(1, 1, 1); - TransformOperations operations1; - operations1.AppendMatrix(transform1); - - gfx::Transform transform2; - transform2.Translate3d(3, 3, 3); - TransformOperations operations2; - operations2.AppendMatrix(transform2); - - gfx::Transform expected; - EXPECT_TRANSFORMATION_MATRIX_EQ(expected, - operations1.Blend(operations2, 1.5).Apply()); - - expected.Translate3d(4, 4, 4); - EXPECT_TRANSFORMATION_MATRIX_EQ(expected, - operations1.Blend(operations2, -0.5).Apply()); -} - -TEST(TransformOperationTest, NonDecomposableBlend) { - TransformOperations non_decomposible_transform; - gfx::Transform non_decomposible_matrix(0, 0, 0, 0, 0, 0); - non_decomposible_transform.AppendMatrix(non_decomposible_matrix); - - TransformOperations identity_transform; - gfx::Transform identity_matrix; - identity_transform.AppendMatrix(identity_matrix); - - // Before the half-way point, we should return the 'from' matrix. - EXPECT_TRANSFORMATION_MATRIX_EQ( - non_decomposible_matrix, - identity_transform.Blend(non_decomposible_transform, 0.0f).Apply()); - EXPECT_TRANSFORMATION_MATRIX_EQ( - non_decomposible_matrix, - identity_transform.Blend(non_decomposible_transform, 0.49f).Apply()); - - // After the half-way point, we should return the 'to' matrix. - EXPECT_TRANSFORMATION_MATRIX_EQ( - identity_matrix, - identity_transform.Blend(non_decomposible_transform, 0.5f).Apply()); - EXPECT_TRANSFORMATION_MATRIX_EQ( - identity_matrix, - identity_transform.Blend(non_decomposible_transform, 1.0f).Apply()); -} - -TEST(TransformOperationTest, BlendedBoundsWhenTypesDoNotMatch) { - TransformOperations operations_from; - operations_from.AppendScale(2.0, 4.0, 8.0); - operations_from.AppendTranslate(1.0, 2.0, 3.0); - - TransformOperations operations_to; - operations_to.AppendTranslate(10.0, 20.0, 30.0); - operations_to.AppendScale(4.0, 8.0, 16.0); - - gfx::BoxF box(1.f, 1.f, 1.f); - gfx::BoxF bounds; - - SkScalar min_progress = 0.f; - SkScalar max_progress = 1.f; - - EXPECT_FALSE(operations_to.BlendedBoundsForBox( - box, operations_from, min_progress, max_progress, &bounds)); -} - -TEST(TransformOperationTest, BlendedBoundsForIdentity) { - TransformOperations operations_from; - operations_from.AppendIdentity(); - TransformOperations operations_to; - operations_to.AppendIdentity(); - - gfx::BoxF box(1.f, 2.f, 3.f); - gfx::BoxF bounds; - - SkScalar min_progress = 0.f; - SkScalar max_progress = 1.f; - - EXPECT_TRUE(operations_to.BlendedBoundsForBox( - box, operations_from, min_progress, max_progress, &bounds)); - EXPECT_EQ(box.ToString(), bounds.ToString()); -} - -TEST(TransformOperationTest, BlendedBoundsForTranslate) { - TransformOperations operations_from; - operations_from.AppendTranslate(3.0, -4.0, 2.0); - TransformOperations operations_to; - operations_to.AppendTranslate(7.0, 4.0, -2.0); - - gfx::BoxF box(1.f, 2.f, 3.f, 4.f, 4.f, 4.f); - gfx::BoxF bounds; - - SkScalar min_progress = -0.5f; - SkScalar max_progress = 1.5f; - EXPECT_TRUE(operations_to.BlendedBoundsForBox( - box, operations_from, min_progress, max_progress, &bounds)); - EXPECT_EQ(gfx::BoxF(2.f, -6.f, -1.f, 12.f, 20.f, 12.f).ToString(), - bounds.ToString()); - - min_progress = 0.f; - max_progress = 1.f; - EXPECT_TRUE(operations_to.BlendedBoundsForBox( - box, operations_from, min_progress, max_progress, &bounds)); - EXPECT_EQ(gfx::BoxF(4.f, -2.f, 1.f, 8.f, 12.f, 8.f).ToString(), - bounds.ToString()); - - TransformOperations identity; - EXPECT_TRUE(operations_to.BlendedBoundsForBox( - box, identity, min_progress, max_progress, &bounds)); - EXPECT_EQ(gfx::BoxF(1.f, 2.f, 1.f, 11.f, 8.f, 6.f).ToString(), - bounds.ToString()); - - EXPECT_TRUE(identity.BlendedBoundsForBox( - box, operations_from, min_progress, max_progress, &bounds)); - EXPECT_EQ(gfx::BoxF(1.f, -2.f, 3.f, 7.f, 8.f, 6.f).ToString(), - bounds.ToString()); -} - -TEST(TransformOperationTest, BlendedBoundsForScale) { - TransformOperations operations_from; - operations_from.AppendScale(3.0, 0.5, 2.0); - TransformOperations operations_to; - operations_to.AppendScale(7.0, 4.0, -2.0); - - gfx::BoxF box(1.f, 2.f, 3.f, 4.f, 4.f, 4.f); - gfx::BoxF bounds; - - SkScalar min_progress = -0.5f; - SkScalar max_progress = 1.5f; - EXPECT_TRUE(operations_to.BlendedBoundsForBox( - box, operations_from, min_progress, max_progress, &bounds)); - EXPECT_EQ(gfx::BoxF(1.f, -7.5f, -28.f, 44.f, 42.f, 56.f).ToString(), - bounds.ToString()); - - min_progress = 0.f; - max_progress = 1.f; - EXPECT_TRUE(operations_to.BlendedBoundsForBox( - box, operations_from, min_progress, max_progress, &bounds)); - EXPECT_EQ(gfx::BoxF(3.f, 1.f, -14.f, 32.f, 23.f, 28.f).ToString(), - bounds.ToString()); - - TransformOperations identity; - EXPECT_TRUE(operations_to.BlendedBoundsForBox( - box, identity, min_progress, max_progress, &bounds)); - EXPECT_EQ(gfx::BoxF(1.f, 2.f, -14.f, 34.f, 22.f, 21.f).ToString(), - bounds.ToString()); - - EXPECT_TRUE(identity.BlendedBoundsForBox( - box, operations_from, min_progress, max_progress, &bounds)); - EXPECT_EQ(gfx::BoxF(1.f, 1.f, 3.f, 14.f, 5.f, 11.f).ToString(), - bounds.ToString()); -} - -TEST(TransformOperationTest, BlendedBoundsWithZeroScale) { - TransformOperations zero_scale; - zero_scale.AppendScale(0.0, 0.0, 0.0); - TransformOperations non_zero_scale; - non_zero_scale.AppendScale(2.0, -4.0, 5.0); - - gfx::BoxF box(1.f, 2.f, 3.f, 4.f, 4.f, 4.f); - gfx::BoxF bounds; - - SkScalar min_progress = 0.f; - SkScalar max_progress = 1.f; - EXPECT_TRUE(zero_scale.BlendedBoundsForBox( - box, non_zero_scale, min_progress, max_progress, &bounds)); - EXPECT_EQ(gfx::BoxF(0.f, -24.f, 0.f, 10.f, 24.f, 35.f).ToString(), - bounds.ToString()); - - EXPECT_TRUE(non_zero_scale.BlendedBoundsForBox( - box, zero_scale, min_progress, max_progress, &bounds)); - EXPECT_EQ(gfx::BoxF(0.f, -24.f, 0.f, 10.f, 24.f, 35.f).ToString(), - bounds.ToString()); - - EXPECT_TRUE(zero_scale.BlendedBoundsForBox( - box, zero_scale, min_progress, max_progress, &bounds)); - EXPECT_EQ(gfx::BoxF().ToString(), bounds.ToString()); -} - -TEST(TransformOperationTest, BlendedBoundsForRotationTrivial) { - TransformOperations operations_from; - operations_from.AppendRotate(0.f, 0.f, 1.f, 0.f); - TransformOperations operations_to; - operations_to.AppendRotate(0.f, 0.f, 1.f, 360.f); - - float sqrt_2 = sqrt(2.f); - gfx::BoxF box( - -sqrt_2, -sqrt_2, 0.f, sqrt_2, sqrt_2, 0.f); - gfx::BoxF bounds; - - // Since we're rotating 360 degrees, any box with dimensions between 0 and - // 2 * sqrt(2) should give the same result. - float sizes[] = { 0.f, 0.1f, sqrt_2, 2.f * sqrt_2 }; - for (size_t i = 0; i < base::size(sizes); ++i) { - box.set_size(sizes[i], sizes[i], 0.f); - SkScalar min_progress = 0.f; - SkScalar max_progress = 1.f; - EXPECT_TRUE(operations_to.BlendedBoundsForBox( - box, operations_from, min_progress, max_progress, &bounds)); - EXPECT_EQ(gfx::BoxF(-2.f, -2.f, 0.f, 4.f, 4.f, 0.f).ToString(), - bounds.ToString()); - } -} - -TEST(TransformOperationTest, BlendedBoundsForRotationAllExtrema) { - // If the normal is out of the plane, we can have up to 6 extrema (a min/max - // in each dimension) between the endpoints of the arc. This test ensures that - // we consider all 6. - TransformOperations operations_from; - operations_from.AppendRotate(1.f, 1.f, 1.f, 30.f); - TransformOperations operations_to; - operations_to.AppendRotate(1.f, 1.f, 1.f, 390.f); - - gfx::BoxF box(1.f, 0.f, 0.f, 0.f, 0.f, 0.f); - gfx::BoxF bounds; - - float min = -1.f / 3.f; - float max = 1.f; - float size = max - min; - EXPECT_TRUE(operations_to.BlendedBoundsForBox( - box, operations_from, 0.f, 1.f, &bounds)); - EXPECT_EQ(gfx::BoxF(min, min, min, size, size, size).ToString(), - bounds.ToString()); -} - -TEST(TransformOperationTest, BlendedBoundsForRotationDifferentAxes) { - // We can handle rotations about a single axis. If the axes are different, - // we revert to matrix interpolation for which inflated bounds cannot be - // computed. - TransformOperations operations_from; - operations_from.AppendRotate(1.f, 1.f, 1.f, 30.f); - TransformOperations operations_to_same; - operations_to_same.AppendRotate(1.f, 1.f, 1.f, 390.f); - TransformOperations operations_to_opposite; - operations_to_opposite.AppendRotate(-1.f, -1.f, -1.f, 390.f); - TransformOperations operations_to_different; - operations_to_different.AppendRotate(1.f, 3.f, 1.f, 390.f); - - gfx::BoxF box(1.f, 0.f, 0.f, 0.f, 0.f, 0.f); - gfx::BoxF bounds; - - EXPECT_TRUE(operations_to_same.BlendedBoundsForBox( - box, operations_from, 0.f, 1.f, &bounds)); - EXPECT_TRUE(operations_to_opposite.BlendedBoundsForBox( - box, operations_from, 0.f, 1.f, &bounds)); - EXPECT_FALSE(operations_to_different.BlendedBoundsForBox( - box, operations_from, 0.f, 1.f, &bounds)); -} - -TEST(TransformOperationTest, BlendedBoundsForRotationPointOnAxis) { - // Checks that if the point to rotate is sitting on the axis of rotation, that - // it does not get affected. - TransformOperations operations_from; - operations_from.AppendRotate(1.f, 1.f, 1.f, 30.f); - TransformOperations operations_to; - operations_to.AppendRotate(1.f, 1.f, 1.f, 390.f); - - gfx::BoxF box(1.f, 1.f, 1.f, 0.f, 0.f, 0.f); - gfx::BoxF bounds; - - EXPECT_TRUE(operations_to.BlendedBoundsForBox( - box, operations_from, 0.f, 1.f, &bounds)); - EXPECT_EQ(box.ToString(), bounds.ToString()); -} - -TEST(TransformOperationTest, BlendedBoundsForRotationProblematicAxes) { - // Zeros in the components of the axis of rotation turned out to be tricky to - // deal with in practice. This function tests some potentially problematic - // axes to ensure sane behavior. - - // Some common values used in the expected boxes. - float dim1 = 0.292893f; - float dim2 = sqrt(2.f); - float dim3 = 2.f * dim2; - - struct { - float x; - float y; - float z; - gfx::BoxF expected; - } tests[] = {{0.f, 0.f, 0.f, gfx::BoxF(1.f, 1.f, 1.f, 0.f, 0.f, 0.f)}, - {1.f, 0.f, 0.f, gfx::BoxF(1.f, -dim2, -dim2, 0.f, dim3, dim3)}, - {0.f, 1.f, 0.f, gfx::BoxF(-dim2, 1.f, -dim2, dim3, 0.f, dim3)}, - {0.f, 0.f, 1.f, gfx::BoxF(-dim2, -dim2, 1.f, dim3, dim3, 0.f)}, - {1.f, 1.f, 0.f, gfx::BoxF(dim1, dim1, -1.f, dim2, dim2, 2.f)}, - {0.f, 1.f, 1.f, gfx::BoxF(-1.f, dim1, dim1, 2.f, dim2, dim2)}, - {1.f, 0.f, 1.f, gfx::BoxF(dim1, -1.f, dim1, dim2, 2.f, dim2)}}; - - for (size_t i = 0; i < base::size(tests); ++i) { - float x = tests[i].x; - float y = tests[i].y; - float z = tests[i].z; - TransformOperations operations_from; - operations_from.AppendRotate(x, y, z, 0.f); - TransformOperations operations_to; - operations_to.AppendRotate(x, y, z, 360.f); - gfx::BoxF box(1.f, 1.f, 1.f, 0.f, 0.f, 0.f); - gfx::BoxF bounds; - - EXPECT_TRUE(operations_to.BlendedBoundsForBox( - box, operations_from, 0.f, 1.f, &bounds)); - EXPECT_EQ(tests[i].expected.ToString(), bounds.ToString()); - } -} - -static void ExpectBoxesApproximatelyEqual(const gfx::BoxF& lhs, - const gfx::BoxF& rhs, - float tolerance) { - EXPECT_NEAR(lhs.x(), rhs.x(), tolerance); - EXPECT_NEAR(lhs.y(), rhs.y(), tolerance); - EXPECT_NEAR(lhs.z(), rhs.z(), tolerance); - EXPECT_NEAR(lhs.width(), rhs.width(), tolerance); - EXPECT_NEAR(lhs.height(), rhs.height(), tolerance); - EXPECT_NEAR(lhs.depth(), rhs.depth(), tolerance); -} - -static void EmpiricallyTestBounds(const TransformOperations& from, - const TransformOperations& to, - SkScalar min_progress, - SkScalar max_progress, - bool test_containment_only) { - gfx::BoxF box(200.f, 500.f, 100.f, 100.f, 300.f, 200.f); - gfx::BoxF bounds; - EXPECT_TRUE( - to.BlendedBoundsForBox(box, from, min_progress, max_progress, &bounds)); - - bool first_time = true; - gfx::BoxF empirical_bounds; - static const size_t kNumSteps = 10; - for (size_t step = 0; step < kNumSteps; ++step) { - float t = step / (kNumSteps - 1.f); - t = gfx::Tween::FloatValueBetween(t, min_progress, max_progress); - gfx::Transform partial_transform = to.Blend(from, t).Apply(); - gfx::BoxF transformed = box; - partial_transform.TransformBox(&transformed); - - if (first_time) { - empirical_bounds = transformed; - first_time = false; - } else { - empirical_bounds.Union(transformed); - } - } - - if (test_containment_only) { - gfx::BoxF unified_bounds = bounds; - unified_bounds.Union(empirical_bounds); - // Convert to the screen space rects these boxes represent. - gfx::Rect bounds_rect = ToEnclosingRect( - gfx::RectF(bounds.x(), bounds.y(), bounds.width(), bounds.height())); - gfx::Rect unified_bounds_rect = - ToEnclosingRect(gfx::RectF(unified_bounds.x(), - unified_bounds.y(), - unified_bounds.width(), - unified_bounds.height())); - EXPECT_EQ(bounds_rect.ToString(), unified_bounds_rect.ToString()); - } else { - // Our empirical estimate will be a little rough since we're only doing - // 100 samples. - static const float kTolerance = 1e-2f; - ExpectBoxesApproximatelyEqual(empirical_bounds, bounds, kTolerance); - } -} - -static void EmpiricallyTestBoundsEquality(const TransformOperations& from, - const TransformOperations& to, - SkScalar min_progress, - SkScalar max_progress) { - EmpiricallyTestBounds(from, to, min_progress, max_progress, false); -} - -static void EmpiricallyTestBoundsContainment(const TransformOperations& from, - const TransformOperations& to, - SkScalar min_progress, - SkScalar max_progress) { - EmpiricallyTestBounds(from, to, min_progress, max_progress, true); -} - -TEST(TransformOperationTest, BlendedBoundsForRotationEmpiricalTests) { - // Sets up various axis angle combinations, computes the bounding box and - // empirically tests that the transformed bounds are indeed contained by the - // computed bounding box. - - struct { - float x; - float y; - float z; - } axes[] = {{1.f, 1.f, 1.f}, - {-1.f, -1.f, -1.f}, - {-1.f, 2.f, 3.f}, - {1.f, -2.f, 3.f}, - {1.f, 2.f, -3.f}, - {0.f, 0.f, 0.f}, - {1.f, 0.f, 0.f}, - {0.f, 1.f, 0.f}, - {0.f, 0.f, 1.f}, - {1.f, 1.f, 0.f}, - {0.f, 1.f, 1.f}, - {1.f, 0.f, 1.f}, - {-1.f, 0.f, 0.f}, - {0.f, -1.f, 0.f}, - {0.f, 0.f, -1.f}, - {-1.f, -1.f, 0.f}, - {0.f, -1.f, -1.f}, - {-1.f, 0.f, -1.f}}; - - struct { - float theta_from; - float theta_to; - } angles[] = {{5.f, 10.f}, - {10.f, 5.f}, - {0.f, 360.f}, - {20.f, 180.f}, - {-20.f, -180.f}, - {180.f, -220.f}, - {220.f, 320.f}}; - - // We can go beyond the range [0, 1] (the bezier might slide out of this range - // at either end), but since the first and last knots are at (0, 0) and (1, 1) - // we will never go within it, so these tests are sufficient. - struct { - float min_progress; - float max_progress; - } progress[] = { - {0.f, 1.f}, {-.25f, 1.25f}, - }; - - for (size_t i = 0; i < base::size(axes); ++i) { - for (size_t j = 0; j < base::size(angles); ++j) { - for (size_t k = 0; k < base::size(progress); ++k) { - float x = axes[i].x; - float y = axes[i].y; - float z = axes[i].z; - TransformOperations operations_from; - operations_from.AppendRotate(x, y, z, angles[j].theta_from); - TransformOperations operations_to; - operations_to.AppendRotate(x, y, z, angles[j].theta_to); - EmpiricallyTestBoundsContainment(operations_from, - operations_to, - progress[k].min_progress, - progress[k].max_progress); - } - } - } -} - -TEST(TransformOperationTest, PerspectiveMatrixAndTransformBlendingEquivalency) { - TransformOperations from_operations; - from_operations.AppendPerspective(200); - - TransformOperations to_operations; - to_operations.AppendPerspective(1000); - - gfx::Transform from_transform; - from_transform.ApplyPerspectiveDepth(200); - - gfx::Transform to_transform; - to_transform.ApplyPerspectiveDepth(1000); - - static const int steps = 20; - for (int i = 0; i < steps; ++i) { - double progress = static_cast<double>(i) / (steps - 1); - - gfx::Transform blended_matrix = to_transform; - EXPECT_TRUE(blended_matrix.Blend(from_transform, progress)); - - gfx::Transform blended_transform = - to_operations.Blend(from_operations, progress).Apply(); - - EXPECT_TRANSFORMATION_MATRIX_EQ(blended_matrix, blended_transform); - } -} - -TEST(TransformOperationTest, BlendedBoundsForPerspective) { - struct { - float from_depth; - float to_depth; - } perspective_depths[] = { - {600.f, 400.f}, - {800.f, 1000.f}, - {800.f, std::numeric_limits<float>::infinity()}, - }; - - struct { - float min_progress; - float max_progress; - } progress[] = { - {0.f, 1.f}, {-0.1f, 1.1f}, - }; - - for (size_t i = 0; i < base::size(perspective_depths); ++i) { - for (size_t j = 0; j < base::size(progress); ++j) { - TransformOperations operations_from; - operations_from.AppendPerspective(perspective_depths[i].from_depth); - TransformOperations operations_to; - operations_to.AppendPerspective(perspective_depths[i].to_depth); - EmpiricallyTestBoundsEquality(operations_from, - operations_to, - progress[j].min_progress, - progress[j].max_progress); - } - } -} - -TEST(TransformOperationTest, BlendedBoundsForSkew) { - struct { - float from_x; - float from_y; - float to_x; - float to_y; - } skews[] = { - {1.f, 0.5f, 0.5f, 1.f}, {2.f, 1.f, 0.5f, 0.5f}, - }; - - struct { - float min_progress; - float max_progress; - } progress[] = { - {0.f, 1.f}, {-0.1f, 1.1f}, - }; - - for (size_t i = 0; i < base::size(skews); ++i) { - for (size_t j = 0; j < base::size(progress); ++j) { - TransformOperations operations_from; - operations_from.AppendSkew(skews[i].from_x, skews[i].from_y); - TransformOperations operations_to; - operations_to.AppendSkew(skews[i].to_x, skews[i].to_y); - EmpiricallyTestBoundsEquality(operations_from, - operations_to, - progress[j].min_progress, - progress[j].max_progress); - } - } -} - -TEST(TransformOperationTest, NonCommutativeRotations) { - TransformOperations operations_from; - operations_from.AppendRotate(1.0, 0.0, 0.0, 0.0); - operations_from.AppendRotate(0.0, 1.0, 0.0, 0.0); - TransformOperations operations_to; - operations_to.AppendRotate(1.0, 0.0, 0.0, 45.0); - operations_to.AppendRotate(0.0, 1.0, 0.0, 135.0); - - gfx::BoxF box(0, 0, 0, 1, 1, 1); - gfx::BoxF bounds; - - SkScalar min_progress = 0.0f; - SkScalar max_progress = 1.0f; - EXPECT_TRUE(operations_to.BlendedBoundsForBox( - box, operations_from, min_progress, max_progress, &bounds)); - gfx::Transform blended_transform = - operations_to.Blend(operations_from, max_progress).Apply(); - gfx::Point3F blended_point(0.9f, 0.9f, 0.0f); - blended_transform.TransformPoint(&blended_point); - gfx::BoxF expanded_bounds = bounds; - expanded_bounds.ExpandTo(blended_point); - EXPECT_EQ(bounds.ToString(), expanded_bounds.ToString()); -} - -TEST(TransformOperationTest, BlendedBoundsForSequence) { - TransformOperations operations_from; - operations_from.AppendTranslate(1.0, -5.0, 1.0); - operations_from.AppendScale(-1.0, 2.0, 3.0); - operations_from.AppendTranslate(2.0, 4.0, -1.0); - TransformOperations operations_to; - operations_to.AppendTranslate(13.0, -1.0, 5.0); - operations_to.AppendScale(-3.0, -2.0, 5.0); - operations_to.AppendTranslate(6.0, -2.0, 3.0); - - gfx::BoxF box(1.f, 2.f, 3.f, 4.f, 4.f, 4.f); - gfx::BoxF bounds; - - SkScalar min_progress = -0.5f; - SkScalar max_progress = 1.5f; - EXPECT_TRUE(operations_to.BlendedBoundsForBox( - box, operations_from, min_progress, max_progress, &bounds)); - EXPECT_EQ(gfx::BoxF(-57.f, -59.f, -1.f, 76.f, 112.f, 80.f).ToString(), - bounds.ToString()); - - min_progress = 0.f; - max_progress = 1.f; - EXPECT_TRUE(operations_to.BlendedBoundsForBox( - box, operations_from, min_progress, max_progress, &bounds)); - EXPECT_EQ(gfx::BoxF(-32.f, -25.f, 7.f, 42.f, 44.f, 48.f).ToString(), - bounds.ToString()); - - TransformOperations identity; - EXPECT_TRUE(operations_to.BlendedBoundsForBox( - box, identity, min_progress, max_progress, &bounds)); - EXPECT_EQ(gfx::BoxF(-33.f, -13.f, 3.f, 57.f, 19.f, 52.f).ToString(), - bounds.ToString()); - - EXPECT_TRUE(identity.BlendedBoundsForBox( - box, operations_from, min_progress, max_progress, &bounds)); - EXPECT_EQ(gfx::BoxF(-7.f, -3.f, 2.f, 15.f, 23.f, 20.f).ToString(), - bounds.ToString()); -} - -TEST(TransformOperationTest, IsTranslationWithSingleOperation) { - TransformOperations empty_operations; - EXPECT_TRUE(empty_operations.IsTranslation()); - - TransformOperations identity; - identity.AppendIdentity(); - EXPECT_TRUE(identity.IsTranslation()); - - TransformOperations translate; - translate.AppendTranslate(1.f, 2.f, 3.f); - EXPECT_TRUE(translate.IsTranslation()); - - TransformOperations rotate; - rotate.AppendRotate(1.f, 2.f, 3.f, 4.f); - EXPECT_FALSE(rotate.IsTranslation()); - - TransformOperations scale; - scale.AppendScale(1.f, 2.f, 3.f); - EXPECT_FALSE(scale.IsTranslation()); - - TransformOperations skew; - skew.AppendSkew(1.f, 2.f); - EXPECT_FALSE(skew.IsTranslation()); - - TransformOperations perspective; - perspective.AppendPerspective(1.f); - EXPECT_FALSE(perspective.IsTranslation()); - - TransformOperations identity_matrix; - identity_matrix.AppendMatrix(gfx::Transform()); - EXPECT_TRUE(identity_matrix.IsTranslation()); - - TransformOperations translation_matrix; - gfx::Transform translation_transform; - translation_transform.Translate3d(1.f, 2.f, 3.f); - translation_matrix.AppendMatrix(translation_transform); - EXPECT_TRUE(translation_matrix.IsTranslation()); - - TransformOperations scaling_matrix; - gfx::Transform scaling_transform; - scaling_transform.Scale(2.f, 2.f); - scaling_matrix.AppendMatrix(scaling_transform); - EXPECT_FALSE(scaling_matrix.IsTranslation()); -} - -TEST(TransformOperationTest, IsTranslationWithMultipleOperations) { - TransformOperations operations1; - operations1.AppendSkew(1.f, 2.f); - operations1.AppendTranslate(1.f, 2.f, 3.f); - operations1.AppendIdentity(); - EXPECT_FALSE(operations1.IsTranslation()); - - TransformOperations operations2; - operations2.AppendIdentity(); - operations2.AppendTranslate(3.f, 2.f, 1.f); - gfx::Transform translation_transform; - translation_transform.Translate3d(1.f, 2.f, 3.f); - operations2.AppendMatrix(translation_transform); - EXPECT_TRUE(operations2.IsTranslation()); -} - -TEST(TransformOperationTest, ScaleComponent) { - SkScalar scale; - - // Scale. - TransformOperations operations1; - operations1.AppendScale(-3.f, 2.f, 5.f); - EXPECT_TRUE(operations1.ScaleComponent(&scale)); - EXPECT_EQ(5.f, scale); - - // Translate. - TransformOperations operations2; - operations2.AppendTranslate(1.f, 2.f, 3.f); - EXPECT_TRUE(operations2.ScaleComponent(&scale)); - EXPECT_EQ(1.f, scale); - - // Rotate. - TransformOperations operations3; - operations3.AppendRotate(1.f, 2.f, 3.f, 4.f); - EXPECT_TRUE(operations3.ScaleComponent(&scale)); - EXPECT_EQ(1.f, scale); - - // Matrix that's only a translation. - TransformOperations operations4; - gfx::Transform translation_transform; - translation_transform.Translate3d(1.f, 2.f, 3.f); - operations4.AppendMatrix(translation_transform); - EXPECT_TRUE(operations4.ScaleComponent(&scale)); - EXPECT_EQ(1.f, scale); - - // Matrix that includes scale. - TransformOperations operations5; - gfx::Transform matrix; - matrix.RotateAboutZAxis(30.0); - matrix.Scale(-7.f, 6.f); - matrix.Translate3d(gfx::Vector3dF(3.f, 7.f, 1.f)); - operations5.AppendMatrix(matrix); - EXPECT_TRUE(operations5.ScaleComponent(&scale)); - EXPECT_EQ(7.f, scale); - - // Matrix with perspective. - TransformOperations operations6; - matrix.ApplyPerspectiveDepth(2000.f); - operations6.AppendMatrix(matrix); - EXPECT_FALSE(operations6.ScaleComponent(&scale)); - - // Skew. - TransformOperations operations7; - operations7.AppendSkew(30.f, 60.f); - EXPECT_TRUE(operations7.ScaleComponent(&scale)); - EXPECT_EQ(2.f, scale); - - // Perspective. - TransformOperations operations8; - operations8.AppendPerspective(500.f); - EXPECT_FALSE(operations8.ScaleComponent(&scale)); - - // Translate + Scale. - TransformOperations operations9; - operations9.AppendTranslate(1.f, 2.f, 3.f); - operations9.AppendScale(2.f, 5.f, 4.f); - EXPECT_TRUE(operations9.ScaleComponent(&scale)); - EXPECT_EQ(5.f, scale); - - // Translate + Scale + Matrix with translate. - operations9.AppendMatrix(translation_transform); - EXPECT_TRUE(operations9.ScaleComponent(&scale)); - EXPECT_EQ(5.f, scale); - - // Scale + translate. - TransformOperations operations10; - operations10.AppendScale(2.f, 3.f, 2.f); - operations10.AppendTranslate(1.f, 2.f, 3.f); - EXPECT_TRUE(operations10.ScaleComponent(&scale)); - EXPECT_EQ(3.f, scale); - - // Two Scales. - TransformOperations operations11; - operations11.AppendScale(2.f, 3.f, 2.f); - operations11.AppendScale(-3.f, -2.f, -3.f); - EXPECT_TRUE(operations11.ScaleComponent(&scale)); - EXPECT_EQ(9.f, scale); - - // Scale + Matrix. - TransformOperations operations12; - operations12.AppendScale(2.f, 2.f, 2.f); - gfx::Transform scaling_transform; - scaling_transform.Scale(2.f, 2.f); - operations12.AppendMatrix(scaling_transform); - EXPECT_TRUE(operations12.ScaleComponent(&scale)); - EXPECT_EQ(4.f, scale); - - // Scale + Rotate. - TransformOperations operations13; - operations13.AppendScale(2.f, 2.f, 2.f); - operations13.AppendRotate(1.f, 2.f, 3.f, 4.f); - EXPECT_TRUE(operations13.ScaleComponent(&scale)); - EXPECT_EQ(2.f, scale); - - // Scale + Skew. - TransformOperations operations14; - operations14.AppendScale(2.f, 2.f, 2.f); - operations14.AppendSkew(60.f, 45.f); - EXPECT_TRUE(operations14.ScaleComponent(&scale)); - EXPECT_EQ(4.f, scale); - - // Scale + Perspective. - TransformOperations operations15; - operations15.AppendScale(2.f, 2.f, 2.f); - operations15.AppendPerspective(1.f); - EXPECT_FALSE(operations15.ScaleComponent(&scale)); - - // Matrix with skew. - TransformOperations operations16; - gfx::Transform skew_transform; - skew_transform.Skew(50.f, 60.f); - operations16.AppendMatrix(skew_transform); - EXPECT_TRUE(operations16.ScaleComponent(&scale)); - EXPECT_EQ(2.f, scale); -} - -TEST(TransformOperationsTest, ApproximateEquality) { - float noise = 1e-7f; - float tolerance = 1e-5f; - TransformOperations lhs; - TransformOperations rhs; - - // Empty lists of operations are trivially equal. - EXPECT_TRUE(lhs.ApproximatelyEqual(rhs, tolerance)); - - rhs.AppendIdentity(); - rhs.AppendTranslate(0, 0, 0); - rhs.AppendRotate(1, 0, 0, 0); - rhs.AppendScale(1, 1, 1); - rhs.AppendSkew(0, 0); - rhs.AppendMatrix(gfx::Transform()); - - // Even though both lists operations are effectively the identity matrix, rhs - // has a different number of operations and is therefore different. - EXPECT_FALSE(lhs.ApproximatelyEqual(rhs, tolerance)); - - rhs.AppendPerspective(800); - - // Assignment should produce equal lists of operations. - lhs = rhs; - EXPECT_TRUE(lhs.ApproximatelyEqual(rhs, tolerance)); - - // Cannot affect identity operations. - lhs.at(0).translate.x = 1; - EXPECT_TRUE(lhs.ApproximatelyEqual(rhs, tolerance)); - - lhs.at(1).translate.x += noise; - EXPECT_TRUE(lhs.ApproximatelyEqual(rhs, tolerance)); - lhs.at(1).translate.x += 1; - EXPECT_FALSE(lhs.ApproximatelyEqual(rhs, tolerance)); - - lhs = rhs; - lhs.at(2).rotate.angle += noise; - EXPECT_TRUE(lhs.ApproximatelyEqual(rhs, tolerance)); - lhs.at(2).rotate.angle = 1; - EXPECT_FALSE(lhs.ApproximatelyEqual(rhs, tolerance)); - - lhs = rhs; - lhs.at(3).scale.x += noise; - EXPECT_TRUE(lhs.ApproximatelyEqual(rhs, tolerance)); - lhs.at(3).scale.x += 1; - EXPECT_FALSE(lhs.ApproximatelyEqual(rhs, tolerance)); - - lhs = rhs; - lhs.at(4).skew.x += noise; - EXPECT_TRUE(lhs.ApproximatelyEqual(rhs, tolerance)); - lhs.at(4).skew.x = 2; - EXPECT_FALSE(lhs.ApproximatelyEqual(rhs, tolerance)); - - lhs = rhs; - lhs.at(5).matrix.Translate3d(noise, 0, 0); - EXPECT_TRUE(lhs.ApproximatelyEqual(rhs, tolerance)); - lhs.at(5).matrix.Translate3d(1, 1, 1); - EXPECT_FALSE(lhs.ApproximatelyEqual(rhs, tolerance)); - - lhs = rhs; - lhs.at(6).perspective_depth += noise; - EXPECT_TRUE(lhs.ApproximatelyEqual(rhs, tolerance)); - lhs.at(6).perspective_depth = 801; - EXPECT_FALSE(lhs.ApproximatelyEqual(rhs, tolerance)); -} - -} // namespace - -// This test is intentionally outside the anonymous namespace for visibility as -// it needs to be friend of TransformOperations. -TEST(TransformOperationsTest, TestDecompositionCache) { - TransformOperations transforms; - EXPECT_EQ(0UL, transforms.decomposed_transforms_.size()); - EXPECT_TRUE(transforms.ComputeDecomposedTransform(0)); - EXPECT_EQ(1UL, transforms.decomposed_transforms_.size()); - - // Reset cache when appending a scale transform. - transforms.AppendScale(2.f, 2.f, 2.f); - EXPECT_EQ(0UL, transforms.decomposed_transforms_.size()); - EXPECT_TRUE(transforms.ComputeDecomposedTransform(1)); - EXPECT_EQ(1UL, transforms.decomposed_transforms_.size()); - EXPECT_TRUE(transforms.ComputeDecomposedTransform(1)); - EXPECT_EQ(1UL, transforms.decomposed_transforms_.size()); - EXPECT_TRUE(transforms.ComputeDecomposedTransform(0)); - EXPECT_EQ(2UL, transforms.decomposed_transforms_.size()); - - // Reset cache when appending a rotation transform. - transforms.AppendRotate(1, 0, 0, 45); - EXPECT_EQ(0UL, transforms.decomposed_transforms_.size()); - EXPECT_TRUE(transforms.ComputeDecomposedTransform(0)); - EXPECT_EQ(1UL, transforms.decomposed_transforms_.size()); - - // Reset cache when appending a translation transform. - transforms.AppendTranslate(1, 1, 1); - EXPECT_EQ(0UL, transforms.decomposed_transforms_.size()); - EXPECT_TRUE(transforms.ComputeDecomposedTransform(0)); - EXPECT_EQ(1UL, transforms.decomposed_transforms_.size()); - - // Reset cache when appending a skew transform. - transforms.AppendSkew(1, 0); - EXPECT_EQ(0UL, transforms.decomposed_transforms_.size()); - EXPECT_TRUE(transforms.ComputeDecomposedTransform(0)); - EXPECT_EQ(1UL, transforms.decomposed_transforms_.size()); - - // Reset cache when appending a perspective transform. - transforms.AppendPerspective(800); - EXPECT_EQ(0UL, transforms.decomposed_transforms_.size()); - EXPECT_TRUE(transforms.ComputeDecomposedTransform(0)); - EXPECT_EQ(1UL, transforms.decomposed_transforms_.size()); - - // Reset cache when appending a matrix transform. - transforms.AppendMatrix(gfx::Transform()); - EXPECT_EQ(0UL, transforms.decomposed_transforms_.size()); - EXPECT_TRUE(transforms.ComputeDecomposedTransform(0)); - EXPECT_EQ(1UL, transforms.decomposed_transforms_.size()); - - // Reset cache when appending a generic transform operation. - transforms.Append(TransformOperation()); - EXPECT_EQ(0UL, transforms.decomposed_transforms_.size()); - EXPECT_TRUE(transforms.ComputeDecomposedTransform(0)); - EXPECT_EQ(1UL, transforms.decomposed_transforms_.size()); -} - -TEST(TransformOperationTest, BlendSkewMismatch) { - TransformOperations from_ops, to_ops, expected_ops; - from_ops.AppendSkewX(0); - from_ops.AppendRotate(0, 0, 1, 0); - to_ops.AppendSkewY(0); - to_ops.AppendRotate(0, 0, 1, 360); - - // Skew types do not match so use matrix interpolation - expected_ops.AppendMatrix(gfx::Transform()); - - TransformOperations blended_ops = to_ops.Blend(from_ops, 0.5); - ASSERT_EQ(blended_ops.size(), 1u); - ExpectTransformOperationEqual(blended_ops.at(0), expected_ops.at(0)); -} - -TEST(TransformOperationTest, BlendSkewMatch) { - TransformOperations from_ops, to_ops, expected_ops; - from_ops.AppendSkew(30, 0); - from_ops.AppendRotate(0, 0, 1, 0); - to_ops.AppendSkew(0, 30); - to_ops.AppendRotate(0, 0, 1, 360); - - // Skew types match so interpolate as a function. - expected_ops.AppendSkew(15, 15); - expected_ops.AppendRotate(0, 0, 1, 180); - - TransformOperations blended_ops = to_ops.Blend(from_ops, 0.5); - ASSERT_EQ(blended_ops.size(), 2u); - ExpectTransformOperationEqual(blended_ops.at(0), expected_ops.at(0)); - ExpectTransformOperationEqual(blended_ops.at(1), expected_ops.at(1)); -} - -} // namespace cc diff --git a/chromium/cc/base/features.cc b/chromium/cc/base/features.cc index 0e831481045..5e95d7db995 100644 --- a/chromium/cc/base/features.cc +++ b/chromium/cc/base/features.cc @@ -8,9 +8,21 @@ namespace features { +// Uses the Resume method instead of the Catch-up method for animated images. +// - Catch-up behavior tries to keep animated images in pace with wall-clock +// time. This might require decoding several animation frames if the +// animation has fallen behind. +// - Resume behavior presents what would have been the next presented frame. +// This means it might only decode one frame, resuming where it left off. +// However, if the animation updates faster than the display's refresh rate, +// it is possible to decode more than a single frame. +const base::Feature kAnimatedImageResume = {"AnimatedImageResume", + base::FEATURE_DISABLED_BY_DEFAULT}; + // Enables impulse-style scroll animations in place of the default ones. const base::Feature kImpulseScrollAnimations = { - "ImpulseScrollAnimations", base::FEATURE_DISABLED_BY_DEFAULT}; + "ImpulseScrollAnimations", + base::FEATURE_DISABLED_BY_DEFAULT}; // Whether the compositor should attempt to sync with the scroll handlers before // submitting a frame. @@ -60,4 +72,10 @@ const base::Feature kSchedulerSmoothnessForAnimatedScrolls{ const base::Feature kWheelEventRegions{"WheelEventRegions", base::FEATURE_DISABLED_BY_DEFAULT}; + +const base::Feature kHudDisplayForPerformanceMetrics{ + "HudDisplayForPerformanceMetrics", base::FEATURE_DISABLED_BY_DEFAULT}; + +const base::Feature kJankInjectionAblationFeature{ + "JankInjectionAblation", base::FEATURE_DISABLED_BY_DEFAULT}; } // namespace features diff --git a/chromium/cc/base/features.h b/chromium/cc/base/features.h index 6c1e4db7b81..8fef93721ec 100644 --- a/chromium/cc/base/features.h +++ b/chromium/cc/base/features.h @@ -11,6 +11,7 @@ namespace features { +CC_BASE_EXPORT extern const base::Feature kAnimatedImageResume; CC_BASE_EXPORT extern const base::Feature kImpulseScrollAnimations; CC_BASE_EXPORT extern const base::Feature kSynchronizedScrolling; @@ -42,6 +43,13 @@ CC_BASE_EXPORT extern const base::Feature // https://docs.google.com/document/d/1ar4WhVnLA-fmw6atgP-23iq-ys_NfFoGb3LA5AgaylA/edit?usp=sharing CC_BASE_EXPORT extern const base::Feature kWheelEventRegions; +// When enabled, cc will show blink's Web-Vital metrics inside its heads up +// display. +CC_BASE_EXPORT extern const base::Feature kHudDisplayForPerformanceMetrics; + +// When enabled, some jank is injected to the animation/scrolling pipeline. +CC_BASE_EXPORT extern const base::Feature kJankInjectionAblationFeature; + } // namespace features #endif // CC_BASE_FEATURES_H_ diff --git a/chromium/cc/base/math_util.cc b/chromium/cc/base/math_util.cc index 486974d284d..6af60ed1cf4 100644 --- a/chromium/cc/base/math_util.cc +++ b/chromium/cc/base/math_util.cc @@ -577,42 +577,6 @@ gfx::RectF MathUtil::ScaleRectProportional(const gfx::RectF& input_outer_rect, return output_inner_rect; } -static inline bool NearlyZero(double value) { - return std::abs(value) < std::numeric_limits<double>::epsilon(); -} - -static inline float ScaleOnAxis(double a, double b, double c) { - if (NearlyZero(b) && NearlyZero(c)) - return std::abs(a); - if (NearlyZero(a) && NearlyZero(c)) - return std::abs(b); - if (NearlyZero(a) && NearlyZero(b)) - return std::abs(c); - - // Do the sqrt as a double to not lose precision. - return static_cast<float>(std::sqrt(a * a + b * b + c * c)); -} - -gfx::Vector2dF MathUtil::ComputeTransform2dScaleComponents( - const gfx::Transform& transform, - float fallback_value) { - if (transform.HasPerspective()) - return gfx::Vector2dF(fallback_value, fallback_value); - float x_scale = ScaleOnAxis(transform.matrix().getDouble(0, 0), - transform.matrix().getDouble(1, 0), - transform.matrix().getDouble(2, 0)); - float y_scale = ScaleOnAxis(transform.matrix().getDouble(0, 1), - transform.matrix().getDouble(1, 1), - transform.matrix().getDouble(2, 1)); - return gfx::Vector2dF(x_scale, y_scale); -} - -float MathUtil::ComputeApproximateMaxScale(const gfx::Transform& transform) { - gfx::RectF unit(0.f, 0.f, 1.f, 1.f); - transform.TransformRect(&unit); - return std::max(unit.width(), unit.height()); -} - float MathUtil::SmallestAngleBetweenVectors(const gfx::Vector2dF& v1, const gfx::Vector2dF& v2) { double dot_product = gfx::DotProduct(v1, v2) / v1.Length() / v2.Length(); @@ -868,4 +832,39 @@ bool MathUtil::IsNearlyTheSameForTesting(const gfx::Point3F& left, return IsNearlyTheSame(left, right); } +// Equivalent to SkMatrix::HasPerspective +bool MathUtil::SkM44HasPerspective(const SkM44& m) { + return (m.rc(3, 0) != 0 || m.rc(3, 1) != 0 || m.rc(3, 2) != 0 || + m.rc(3, 3) != 1); +} + +// Since some operations assume a 2d transformation, check to make sure that +// is the case by seeing that the z-axis is identity +bool MathUtil::SkM44Is2D(const SkM44& m) { + return (m.rc(0, 2) == 0 && m.rc(1, 2) == 0 && m.rc(2, 2) == 1 && + m.rc(2, 0) == 0 && m.rc(2, 1) == 0 && m.rc(2, 3) == 0 && + m.rc(3, 2) == 0); +} + +// Equivalent to SkMatrix::PreservesAxisAlignment +// Checks if the transformation is a 90 degree rotation or scaling +// See SkMatrix::computeTypeMask +bool MathUtil::SkM44Preserves2DAxisAlignment(const SkM44& m) { + // Conservatively assume that perspective transforms would not preserve + // axis-alignment + if (!SkM44Is2D(m) || SkM44HasPerspective(m)) + return false; + + // Does the matrix have skew components + if (m.rc(0, 1) != 0 || m.rc(1, 0) != 0) { + // Rects only map to rects if both skews are non-zero and both scale + // components are zero (i.e. it's a +/-90-degree rotation) + return (m.rc(0, 0) == 0 && m.rc(1, 1) == 0 && m.rc(0, 1) != 0 && + m.rc(1, 0) != 0); + } + // Since the matrix has no skewing, it maps to a rectangle so long as the + // scale components are non-zero + return (m.rc(0, 0) != 0 && m.rc(1, 1) != 0); +} + } // namespace cc diff --git a/chromium/cc/base/math_util.h b/chromium/cc/base/math_util.h index 8cb8c38628b..e6940430086 100644 --- a/chromium/cc/base/math_util.h +++ b/chromium/cc/base/math_util.h @@ -13,6 +13,7 @@ #include "base/numerics/ranges.h" #include "build/build_config.h" #include "cc/base/base_export.h" +#include "third_party/skia/include/core/SkM44.h" #include "ui/gfx/geometry/box_f.h" #include "ui/gfx/geometry/point3_f.h" #include "ui/gfx/geometry/point_f.h" @@ -234,13 +235,6 @@ class CC_BASE_EXPORT MathUtil { const gfx::PointF& point, bool* clipped); - static gfx::Vector2dF ComputeTransform2dScaleComponents(const gfx::Transform&, - float fallbackValue); - // Returns an approximate max scale value of the transform even if it has - // perspective. Prefer to use ComputeTransform2dScaleComponents if there is no - // perspective, since it can produce more accurate results. - static float ComputeApproximateMaxScale(const gfx::Transform& transform); - // Makes a rect that has the same relationship to input_outer_rect as // scale_inner_rect has to scale_outer_rect. scale_inner_rect should be // contained within scale_outer_rect, and likewise the rectangle that is @@ -325,6 +319,12 @@ class CC_BASE_EXPORT MathUtil { static bool IsNearlyTheSameForTesting(const gfx::Point3F& l, const gfx::Point3F& r); + // Helper functions for migration from SkMatrix->SkM44. It may make sense to + // move these to skia itself at some point. + static bool SkM44HasPerspective(const SkM44& m); + static bool SkM44Is2D(const SkM44& m); + static bool SkM44Preserves2DAxisAlignment(const SkM44& m); + private: template <typename T> static T RoundUpInternal(T n, T mul) { diff --git a/chromium/cc/debug/layer_tree_debug_state.cc b/chromium/cc/debug/layer_tree_debug_state.cc index 0ce673525cf..2a791a688a7 100644 --- a/chromium/cc/debug/layer_tree_debug_state.cc +++ b/chromium/cc/debug/layer_tree_debug_state.cc @@ -4,29 +4,10 @@ #include "cc/debug/layer_tree_debug_state.h" - namespace cc { // IMPORTANT: new fields must be added to Equal() and Unite() -LayerTreeDebugState::LayerTreeDebugState() - : show_fps_counter(false), - show_debug_borders(false), - show_layout_shift_regions(false), - show_paint_rects(false), - show_property_changed_rects(false), - show_surface_damage_rects(false), - show_screen_space_rects(false), - show_touch_event_handler_rects(false), - show_wheel_event_handler_rects(false), - show_scroll_event_handler_rects(false), - show_non_fast_scrollable_rects(false), - show_main_thread_scrolling_reason_rects(false), - show_layer_animation_bounds_rects(false), - slow_down_raster_scale_factor(0), - rasterize_only_visible_content(false), - highlight_non_lcd_text_layers(false), - show_hit_test_borders(false), - record_rendering_stats_(false) {} +LayerTreeDebugState::LayerTreeDebugState() = default; LayerTreeDebugState::LayerTreeDebugState(const LayerTreeDebugState& other) = default; @@ -41,11 +22,11 @@ bool LayerTreeDebugState::RecordRenderingStats() const { return record_rendering_stats_; } -bool LayerTreeDebugState::ShowHudInfo() const { - return show_fps_counter || ShowHudRects(); +bool LayerTreeDebugState::ShouldCreateHudLayer() const { + return ShowDebugRects() || ShouldDrawHudInfo(); } -bool LayerTreeDebugState::ShowHudRects() const { +bool LayerTreeDebugState::ShowDebugRects() const { return show_paint_rects || show_property_changed_rects || show_surface_damage_rects || show_screen_space_rects || show_touch_event_handler_rects || show_wheel_event_handler_rects || @@ -58,6 +39,18 @@ bool LayerTreeDebugState::ShowMemoryStats() const { return show_fps_counter; } +bool LayerTreeDebugState::ShouldDrawHudInfo() const { + return show_fps_counter || show_web_vital_metrics || show_smoothness_metrics; +} + +void LayerTreeDebugState::TurnOffHudInfoDisplay() { + // Turn off all types of HUD info display. ShouldDrawHudInfo() would return + // false after this function. + show_fps_counter = false; + show_web_vital_metrics = false; + show_smoothness_metrics = false; +} + bool LayerTreeDebugState::Equal(const LayerTreeDebugState& a, const LayerTreeDebugState& b) { return ( @@ -80,39 +73,8 @@ bool LayerTreeDebugState::Equal(const LayerTreeDebugState& a, a.rasterize_only_visible_content == b.rasterize_only_visible_content && a.highlight_non_lcd_text_layers == b.highlight_non_lcd_text_layers && a.show_hit_test_borders == b.show_hit_test_borders && + a.show_web_vital_metrics == b.show_web_vital_metrics && a.record_rendering_stats_ == b.record_rendering_stats_); } -LayerTreeDebugState LayerTreeDebugState::Unite(const LayerTreeDebugState& a, - const LayerTreeDebugState& b) { - LayerTreeDebugState r(a); - - r.show_fps_counter |= b.show_fps_counter; - r.show_debug_borders |= b.show_debug_borders; - - r.show_layout_shift_regions |= b.show_layout_shift_regions; - r.show_paint_rects |= b.show_paint_rects; - r.show_property_changed_rects |= b.show_property_changed_rects; - r.show_surface_damage_rects |= b.show_surface_damage_rects; - r.show_screen_space_rects |= b.show_screen_space_rects; - r.show_touch_event_handler_rects |= b.show_touch_event_handler_rects; - r.show_wheel_event_handler_rects |= b.show_wheel_event_handler_rects; - r.show_scroll_event_handler_rects |= b.show_scroll_event_handler_rects; - r.show_non_fast_scrollable_rects |= b.show_non_fast_scrollable_rects; - r.show_main_thread_scrolling_reason_rects |= - b.show_main_thread_scrolling_reason_rects; - r.show_layer_animation_bounds_rects |= b.show_layer_animation_bounds_rects; - - if (b.slow_down_raster_scale_factor) - r.slow_down_raster_scale_factor = b.slow_down_raster_scale_factor; - r.rasterize_only_visible_content |= b.rasterize_only_visible_content; - r.highlight_non_lcd_text_layers |= b.highlight_non_lcd_text_layers; - - r.show_hit_test_borders |= b.show_hit_test_borders; - - r.record_rendering_stats_ |= b.record_rendering_stats_; - - return r; -} - } // namespace cc diff --git a/chromium/cc/debug/layer_tree_debug_state.h b/chromium/cc/debug/layer_tree_debug_state.h index 95b79cda95d..d6618b9e4a8 100644 --- a/chromium/cc/debug/layer_tree_debug_state.h +++ b/chromium/cc/debug/layer_tree_debug_state.h @@ -29,40 +29,48 @@ class CC_DEBUG_EXPORT LayerTreeDebugState { LayerTreeDebugState(const LayerTreeDebugState& other); ~LayerTreeDebugState(); - bool show_fps_counter; - DebugBorderTypes show_debug_borders; - - bool show_layout_shift_regions; - bool show_paint_rects; - bool show_property_changed_rects; - bool show_surface_damage_rects; - bool show_screen_space_rects; - bool show_touch_event_handler_rects; - bool show_wheel_event_handler_rects; - bool show_scroll_event_handler_rects; - bool show_non_fast_scrollable_rects; - bool show_main_thread_scrolling_reason_rects; - bool show_layer_animation_bounds_rects; - - int slow_down_raster_scale_factor; - bool rasterize_only_visible_content; - bool highlight_non_lcd_text_layers; - - bool show_hit_test_borders; + bool show_fps_counter = false; + DebugBorderTypes show_debug_borders = false; + + bool show_layout_shift_regions = false; + bool show_paint_rects = false; + bool show_property_changed_rects = false; + bool show_surface_damage_rects = false; + bool show_screen_space_rects = false; + bool show_touch_event_handler_rects = false; + bool show_wheel_event_handler_rects = false; + bool show_scroll_event_handler_rects = false; + bool show_non_fast_scrollable_rects = false; + bool show_main_thread_scrolling_reason_rects = false; + bool show_layer_animation_bounds_rects = false; + + int slow_down_raster_scale_factor = 0; + bool rasterize_only_visible_content = false; + bool highlight_non_lcd_text_layers = false; + + bool show_hit_test_borders = false; + + // This is part of the feature to show performance metrics on HUD. This + // particular flag is set only in Blink. + bool show_web_vital_metrics = false; + bool show_smoothness_metrics = false; void SetRecordRenderingStats(bool enabled); bool RecordRenderingStats() const; - bool ShowHudInfo() const; - bool ShowHudRects() const; + // HUD layer is responsible for drawing debug rects as well as displaying HUD + // overlay. This function checks if a HUD layer should be created for any of + // these situations. + bool ShouldCreateHudLayer() const; + bool ShowDebugRects() const; bool ShowMemoryStats() const; + bool ShouldDrawHudInfo() const; + void TurnOffHudInfoDisplay(); static bool Equal(const LayerTreeDebugState& a, const LayerTreeDebugState& b); - static LayerTreeDebugState Unite(const LayerTreeDebugState& a, - const LayerTreeDebugState& b); private: - bool record_rendering_stats_; + bool record_rendering_stats_ = false; }; } // namespace cc diff --git a/chromium/cc/document_transition/README.md b/chromium/cc/document_transition/README.md new file mode 100644 index 00000000000..3b764d357b0 --- /dev/null +++ b/chromium/cc/document_transition/README.md @@ -0,0 +1,5 @@ +# cc/document\_transition + +The document transition direction supports the Document Transition and Shared +Element Transition projects in Blink. Please see +//third\_party/blink/renderer/core/document\_transition/README.md for more details. diff --git a/chromium/cc/document_transition/document_transition_request.cc b/chromium/cc/document_transition/document_transition_request.cc new file mode 100644 index 00000000000..220d097f1ff --- /dev/null +++ b/chromium/cc/document_transition/document_transition_request.cc @@ -0,0 +1,114 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/document_transition/document_transition_request.h" + +#include <memory> +#include <sstream> +#include <utility> + +#include "base/callback.h" +#include "base/memory/ptr_util.h" +#include "components/viz/common/quads/compositor_frame_transition_directive.h" + +namespace cc { +namespace { + +std::string TypeToString(viz::CompositorFrameTransitionDirective::Type type) { + switch (type) { + case viz::CompositorFrameTransitionDirective::Type::kSave: + return "kSave"; + case viz::CompositorFrameTransitionDirective::Type::kAnimate: + return "kAnimate"; + } + return "<unknown>"; +} + +std::string EffectToString( + viz::CompositorFrameTransitionDirective::Effect effect) { + switch (effect) { + case viz::CompositorFrameTransitionDirective::Effect::kNone: + return "kNone"; + case viz::CompositorFrameTransitionDirective::Effect::kCoverDown: + return "kCoverDown"; + case viz::CompositorFrameTransitionDirective::Effect::kCoverLeft: + return "kCoverLeft"; + case viz::CompositorFrameTransitionDirective::Effect::kCoverRight: + return "kCoverRight"; + case viz::CompositorFrameTransitionDirective::Effect::kCoverUp: + return "kCoverUp"; + case viz::CompositorFrameTransitionDirective::Effect::kExplode: + return "kExplode"; + case viz::CompositorFrameTransitionDirective::Effect::kFade: + return "kFade"; + case viz::CompositorFrameTransitionDirective::Effect::kImplode: + return "kImplode"; + case viz::CompositorFrameTransitionDirective::Effect::kRevealDown: + return "kRevealDown"; + case viz::CompositorFrameTransitionDirective::Effect::kRevealLeft: + return "kRevealLeft"; + case viz::CompositorFrameTransitionDirective::Effect::kRevealRight: + return "kRevealRight"; + case viz::CompositorFrameTransitionDirective::Effect::kRevealUp: + return "kRevealUp"; + } + return "<unknown>"; +} + +} // namespace + +uint32_t DocumentTransitionRequest::s_next_sequence_id_ = 1; + +// static +std::unique_ptr<DocumentTransitionRequest> +DocumentTransitionRequest::CreatePrepare(Effect effect, + base::TimeDelta duration, + base::OnceClosure commit_callback) { + return base::WrapUnique(new DocumentTransitionRequest( + effect, duration, std::move(commit_callback))); +} + +// static +std::unique_ptr<DocumentTransitionRequest> +DocumentTransitionRequest::CreateStart(base::OnceClosure commit_callback) { + return base::WrapUnique( + new DocumentTransitionRequest(std::move(commit_callback))); +} + +DocumentTransitionRequest::DocumentTransitionRequest( + Effect effect, + base::TimeDelta duration, + base::OnceClosure commit_callback) + : type_(Type::kSave), + effect_(effect), + duration_(duration), + commit_callback_(std::move(commit_callback)) {} + +DocumentTransitionRequest::DocumentTransitionRequest( + base::OnceClosure commit_callback) + : type_(Type::kAnimate), commit_callback_(std::move(commit_callback)) {} + +DocumentTransitionRequest::~DocumentTransitionRequest() = default; + +viz::CompositorFrameTransitionDirective +DocumentTransitionRequest::ConstructDirective() const { + // Note that the clamped_duration is also verified at + // CompositorFrameTransitionDirective deserialization time. + auto clamped_duration = + duration_ < viz::CompositorFrameTransitionDirective::kMaxDuration + ? duration_ + : viz::CompositorFrameTransitionDirective::kMaxDuration; + return viz::CompositorFrameTransitionDirective(s_next_sequence_id_++, type_, + effect_, clamped_duration); +} + +std::string DocumentTransitionRequest::ToString() const { + std::ostringstream str; + str << "[type: " << TypeToString(type_) + << " effect: " << EffectToString(effect_) + << " duration: " << duration_.InMillisecondsF() << "ms]"; + return str.str(); +} + +} // namespace cc diff --git a/chromium/cc/document_transition/document_transition_request.h b/chromium/cc/document_transition/document_transition_request.h new file mode 100644 index 00000000000..dddfd4a9fd9 --- /dev/null +++ b/chromium/cc/document_transition/document_transition_request.h @@ -0,0 +1,73 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CC_DOCUMENT_TRANSITION_DOCUMENT_TRANSITION_REQUEST_H_ +#define CC_DOCUMENT_TRANSITION_DOCUMENT_TRANSITION_REQUEST_H_ + +#include <memory> +#include <string> +#include <utility> + +#include "base/callback.h" +#include "cc/cc_export.h" +#include "components/viz/common/quads/compositor_frame_transition_directive.h" + +namespace cc { + +// This class represents a document transition request. It is constructed in +// Blink with an intent of translating this request into a viz directive for the +// transition to occur. +class CC_EXPORT DocumentTransitionRequest { + public: + using Effect = viz::CompositorFrameTransitionDirective::Effect; + + // Creates a Type::kPrepare type of request. + static std::unique_ptr<DocumentTransitionRequest> CreatePrepare( + Effect effect, + base::TimeDelta duration, + base::OnceClosure commit_callback); + + // Creates a Type::kSave type of request. + static std::unique_ptr<DocumentTransitionRequest> CreateStart( + base::OnceClosure commit_callback); + + DocumentTransitionRequest(DocumentTransitionRequest&) = delete; + ~DocumentTransitionRequest(); + + DocumentTransitionRequest& operator=(DocumentTransitionRequest&) = delete; + + // The callback is run when the request is committed from the main thread onto + // the compositor thread. This is used to indicate that the request has been + // submitted for processing and that script may now change the page in some + // way. In other words, this callback would resolve the prepare promise that + // script may be waiting for. + base::OnceClosure TakeCommitCallback() { return std::move(commit_callback_); } + + // This constructs a viz directive. Note that repeated calls to this function + // would create a new sequence id for the directive, which means it would be + // processed again by viz. + viz::CompositorFrameTransitionDirective ConstructDirective() const; + + // Testing / debugging functionality. + std::string ToString() const; + + private: + using Type = viz::CompositorFrameTransitionDirective::Type; + + DocumentTransitionRequest(Effect effect, + base::TimeDelta duration, + base::OnceClosure commit_callback); + explicit DocumentTransitionRequest(base::OnceClosure commit_callback); + + const Type type_; + const Effect effect_ = Effect::kNone; + const base::TimeDelta duration_; + base::OnceClosure commit_callback_; + + static uint32_t s_next_sequence_id_; +}; + +} // namespace cc + +#endif // CC_DOCUMENT_TRANSITION_DOCUMENT_TRANSITION_REQUEST_H_ diff --git a/chromium/cc/document_transition/document_transition_request_unittest.cc b/chromium/cc/document_transition/document_transition_request_unittest.cc new file mode 100644 index 00000000000..e5001d1f2df --- /dev/null +++ b/chromium/cc/document_transition/document_transition_request_unittest.cc @@ -0,0 +1,73 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/document_transition/document_transition_request.h" + +#include <utility> + +#include "base/test/bind.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace cc { + +TEST(DocumentTransitionRequestTest, PrepareRequest) { + bool called = false; + auto callback = base::BindLambdaForTesting([&called]() { called = true; }); + + auto request = DocumentTransitionRequest::CreatePrepare( + DocumentTransitionRequest::Effect::kRevealLeft, + base::TimeDelta::FromMilliseconds(123), std::move(callback)); + + EXPECT_FALSE(called); + request->TakeCommitCallback().Run(); + EXPECT_TRUE(called); + EXPECT_TRUE(request->TakeCommitCallback().is_null()); + + auto directive = request->ConstructDirective(); + EXPECT_GT(directive.sequence_id(), 0u); + EXPECT_EQ(DocumentTransitionRequest::Effect::kRevealLeft, directive.effect()); + EXPECT_EQ(base::TimeDelta::FromMilliseconds(123), directive.duration()); + EXPECT_EQ(viz::CompositorFrameTransitionDirective::Type::kSave, + directive.type()); + + auto duplicate = request->ConstructDirective(); + EXPECT_GT(duplicate.sequence_id(), directive.sequence_id()); + EXPECT_EQ(duplicate.effect(), directive.effect()); + EXPECT_EQ(duplicate.duration(), directive.duration()); + EXPECT_EQ(duplicate.type(), directive.type()); +} + +TEST(DocumentTransitionRequestTest, PrepareRequestLongDurationIsCapped) { + auto long_duration = base::TimeDelta::FromSeconds(1); + + ASSERT_GT(long_duration, + viz::CompositorFrameTransitionDirective::kMaxDuration); + + auto request = DocumentTransitionRequest::CreatePrepare( + DocumentTransitionRequest::Effect::kRevealLeft, long_duration, + base::OnceCallback<void()>()); + + auto directive = request->ConstructDirective(); + EXPECT_EQ(viz::CompositorFrameTransitionDirective::kMaxDuration, + directive.duration()); +} + +TEST(DocumentTransitionRequestTest, StartRequest) { + bool called = false; + auto callback = base::BindLambdaForTesting([&called]() { called = true; }); + + auto request = DocumentTransitionRequest::CreateStart(std::move(callback)); + + EXPECT_FALSE(called); + request->TakeCommitCallback().Run(); + EXPECT_TRUE(called); + EXPECT_TRUE(request->TakeCommitCallback().is_null()); + + auto directive = request->ConstructDirective(); + EXPECT_GT(directive.sequence_id(), 0u); + EXPECT_EQ(viz::CompositorFrameTransitionDirective::Type::kAnimate, + directive.type()); +} + +} // namespace cc diff --git a/chromium/cc/input/browser_controls_state.h b/chromium/cc/input/browser_controls_state.h index 8b2f93f4171..2e5c537949d 100644 --- a/chromium/cc/input/browser_controls_state.h +++ b/chromium/cc/input/browser_controls_state.h @@ -7,7 +7,14 @@ namespace cc { -enum class BrowserControlsState { kShown = 1, kHidden = 2, kBoth = 3 }; +// A Java counterpart will be generated for this enum. +// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.cc.input +enum class BrowserControlsState { + kShown = 1, + kHidden = 2, + kBoth = 3, + kMaxValue = kBoth +}; } // namespace cc diff --git a/chromium/cc/input/compositor_input_interfaces.h b/chromium/cc/input/compositor_input_interfaces.h index b20b270ae9e..d5bbaededda 100644 --- a/chromium/cc/input/compositor_input_interfaces.h +++ b/chromium/cc/input/compositor_input_interfaces.h @@ -10,6 +10,7 @@ #include "base/time/time.h" #include "cc/input/actively_scrolling_type.h" #include "cc/paint/element_id.h" +#include "ui/gfx/geometry/size.h" namespace viz { struct BeginFrameArgs; @@ -107,6 +108,7 @@ class CompositorDelegateForInput { virtual void DidScrollContent(ElementId element_id, bool animated) = 0; virtual float DeviceScaleFactor() const = 0; virtual float PageScaleFactor() const = 0; + virtual gfx::Size VisualDeviceViewportSize() const = 0; virtual const LayerTreeSettings& GetSettings() const = 0; // TODO(bokan): Temporary escape hatch for code that hasn't yet been diff --git a/chromium/cc/input/input_handler.h b/chromium/cc/input/input_handler.h index aeec0fbc1a7..5460c76c716 100644 --- a/chromium/cc/input/input_handler.h +++ b/chromium/cc/input/input_handler.h @@ -176,6 +176,8 @@ class CC_EXPORT InputHandler { SCROLL_ON_MAIN_THREAD = 0, SCROLL_ON_IMPL_THREAD, SCROLL_IGNORED, + // SCROLL_UNKOWN is not used anymore. However we'll keep this entry as per + // the comment above. SCROLL_UNKNOWN, LAST_SCROLL_STATUS = SCROLL_UNKNOWN }; @@ -195,9 +197,17 @@ class CC_EXPORT InputHandler { main_thread_scrolling_reasons(main_thread_scrolling_reasons), needs_main_thread_hit_test(needs_main_thread_hit_test) {} ScrollThread thread = ScrollThread::SCROLL_ON_IMPL_THREAD; + // TODO(crbug.com/1155663): Make sure to set main_thread_scrolling_reasons + // only when ScrollStatus.thread is set to + // InputHander::ScrollThread::SCROLL_ON_MAIN_THREAD uint32_t main_thread_scrolling_reasons = MainThreadScrollingReason::kNotScrollingOnMain; - bool bubble = false; + // TODO(crbug.com/1155758): This is a temporary workaround for GuestViews + // as they create viewport nodes and want to bubble scroll if the + // viewport cannot scroll in the given delta directions. There should be + // a parameter to ThreadInputHandler to specify whether unused delta is + // consumed by the viewport or bubbles to the parent. + bool viewport_cannot_scroll = false; // Used only in scroll unification. Tells the caller that the input handler // detected a case where it cannot reliably target a scroll node and needs @@ -262,6 +272,7 @@ class CC_EXPORT InputHandler { ScrollBeginThreadState scroll_start_state) = 0; virtual void RecordScrollEnd(ui::ScrollInputType input_type) = 0; + virtual PointerResultType HitTest(const gfx::PointF& mouse_position) = 0; virtual InputHandlerPointerResult MouseMoveAt( const gfx::Point& mouse_position) = 0; // TODO(arakeri): Pass in the modifier instead of a bool once the refactor diff --git a/chromium/cc/input/main_thread_scrolling_reason.cc b/chromium/cc/input/main_thread_scrolling_reason.cc index abd24047855..0d0d0c2a57a 100644 --- a/chromium/cc/input/main_thread_scrolling_reason.cc +++ b/chromium/cc/input/main_thread_scrolling_reason.cc @@ -32,16 +32,10 @@ void MainThreadScrollingReason::AddToTracedValue( if (reasons & kHasBackgroundAttachmentFixedObjects) traced_value.AppendString("Has background-attachment:fixed"); - if (reasons & kHasNonLayerViewportConstrainedObjects) - traced_value.AppendString("Has non-layer viewport-constrained objects"); if (reasons & kThreadedScrollingDisabled) traced_value.AppendString("Threaded scrolling is disabled"); if (reasons & kScrollbarScrolling) traced_value.AppendString("Scrollbar scrolling"); - if (reasons & kFrameOverlay) - traced_value.AppendString("Frame overlay"); - if (reasons & kHandlingScrollFromMainThread) - traced_value.AppendString("Handling scroll from main thread"); if (reasons & kNotOpaqueForTextAndLCDText) traced_value.AppendString("Not opaque for text and LCD text"); if (reasons & kCantPaintScrollingBackgroundAndLCDText) @@ -56,12 +50,8 @@ void MainThreadScrollingReason::AddToTracedValue( traced_value.AppendString("No scrolling layer"); if (reasons & kNotScrollable) traced_value.AppendString("Not scrollable"); - if (reasons & kContinuingMainThreadScroll) - traced_value.AppendString("Continuing main thread scroll"); if (reasons & kNonInvertibleTransform) traced_value.AppendString("Non-invertible transform"); - if (reasons & kPageBasedScrolling) - traced_value.AppendString("Page-based scrolling"); if (reasons & kWheelEventHandlerRegion) traced_value.AppendString("Wheel event handler region"); if (reasons & kTouchEventHandlerRegion) diff --git a/chromium/cc/input/main_thread_scrolling_reason.h b/chromium/cc/input/main_thread_scrolling_reason.h index 4ddd536afcc..0378bdac161 100644 --- a/chromium/cc/input/main_thread_scrolling_reason.h +++ b/chromium/cc/input/main_thread_scrolling_reason.h @@ -17,52 +17,43 @@ class TracedValue; namespace cc { -// Ensure this stays in sync with MainThreadScrollingReason in histograms.xml. -// When adding a new MainThreadScrollingReason, make sure the corresponding -// [MainThread/Compositor]CanSetScrollReasons function is also updated. +// Ensure this stays in sync with MainThreadScrollingReason in +// tools/metrics/enums.xml. When adding a new MainThreadScrollingReason, make +// sure the corresponding [MainThread/Compositor]CanSetScrollReasons function +// is also updated. struct CC_EXPORT MainThreadScrollingReason { enum : uint32_t { kNotScrollingOnMain = 0, + // This enum simultaneously defines actual bitmask values and indices into + // the bitmask, but kNotScrollingMain is recorded in the histograms as + // value 0, so the 0th bit should never be used. + // See also blink::RecordScrollReasonsMetric(). + // Non-transient scrolling reasons. - kHasBackgroundAttachmentFixedObjects = 1 << 0, - kHasNonLayerViewportConstrainedObjects = 1 << 1, - kThreadedScrollingDisabled = 1 << 2, - kScrollbarScrolling = 1 << 3, - kFrameOverlay = 1 << 4, - - // This bit is set when any of the other main thread scrolling reasons cause - // an input event to be handled on the main thread, and the main thread - // blink::ScrollAnimator is in the middle of running a scroll offset - // animation. Note that a scroll handled by the main thread can result in an - // animation running on the main thread or on the compositor thread. - kHandlingScrollFromMainThread = 1 << 13, + kHasBackgroundAttachmentFixedObjects = 1 << 1, + kThreadedScrollingDisabled = 1 << 3, // Style-related scrolling on main reasons. // These *AndLCDText reasons are due to subpixel text rendering which can // only be applied by blending glyphs with the background at a specific // screen position; transparency and transforms break this. - kNonCompositedReasonsFirst = 17, - kNotOpaqueForTextAndLCDText = 1 << 18, - kCantPaintScrollingBackgroundAndLCDText = 1 << 19, - kNonCompositedReasonsLast = 22, + kNonCompositedReasonsFirst = 18, + kNotOpaqueForTextAndLCDText = 1 << 19, + kCantPaintScrollingBackgroundAndLCDText = 1 << 20, + kNonCompositedReasonsLast = 23, // Transient scrolling reasons. These are computed for each scroll begin. - kNonFastScrollableRegion = 1 << 5, - kFailedHitTest = 1 << 7, - kNoScrollingLayer = 1 << 8, - kNotScrollable = 1 << 9, - kContinuingMainThreadScroll = 1 << 10, - kNonInvertibleTransform = 1 << 11, - kPageBasedScrolling = 1 << 12, - kWheelEventHandlerRegion = 1 << 23, - kTouchEventHandlerRegion = 1 << 24, - - // The maximum number of flags in this struct (excluding itself). - // New flags should increment this number but it should never be decremented - // because the values are used in UMA histograms. It should also be noted - // that it excludes the kNotScrollingOnMain value. - kMainThreadScrollingReasonCount = 25, + kScrollbarScrolling = 1 << 4, + kNonFastScrollableRegion = 1 << 6, + kFailedHitTest = 1 << 8, + kNoScrollingLayer = 1 << 9, + kNotScrollable = 1 << 10, + kNonInvertibleTransform = 1 << 12, + kWheelEventHandlerRegion = 1 << 24, + kTouchEventHandlerRegion = 1 << 25, + + kMainThreadScrollingReasonLast = 25, }; static const uint32_t kNonCompositedReasons = @@ -71,20 +62,17 @@ struct CC_EXPORT MainThreadScrollingReason { // Returns true if the given MainThreadScrollingReason can be set by the main // thread. static bool MainThreadCanSetScrollReasons(uint32_t reasons) { - uint32_t reasons_set_by_main_thread = - kNotScrollingOnMain | kHasBackgroundAttachmentFixedObjects | - kHasNonLayerViewportConstrainedObjects | kThreadedScrollingDisabled | - kScrollbarScrolling | kFrameOverlay | kHandlingScrollFromMainThread; + constexpr uint32_t reasons_set_by_main_thread = + kHasBackgroundAttachmentFixedObjects | kThreadedScrollingDisabled; return (reasons & reasons_set_by_main_thread) == reasons; } // Returns true if the given MainThreadScrollingReason can be set by the // compositor. static bool CompositorCanSetScrollReasons(uint32_t reasons) { - uint32_t reasons_set_by_compositor = + constexpr uint32_t reasons_set_by_compositor = kNonFastScrollableRegion | kFailedHitTest | kNoScrollingLayer | - kNotScrollable | kContinuingMainThreadScroll | kNonInvertibleTransform | - kPageBasedScrolling | kWheelEventHandlerRegion | + kNotScrollable | kNonInvertibleTransform | kWheelEventHandlerRegion | kTouchEventHandlerRegion; return (reasons & reasons_set_by_compositor) == reasons; } diff --git a/chromium/cc/input/main_thread_scrolling_reason_unittest.cc b/chromium/cc/input/main_thread_scrolling_reason_unittest.cc index becbfe296db..282ef9ac64a 100644 --- a/chromium/cc/input/main_thread_scrolling_reason_unittest.cc +++ b/chromium/cc/input/main_thread_scrolling_reason_unittest.cc @@ -14,20 +14,15 @@ TEST_F(MainThreadScrollingReasonTest, AsText) { EXPECT_EQ("", MainThreadScrollingReason::AsText(0)); EXPECT_EQ( "Has background-attachment:fixed, " - "Has non-layer viewport-constrained objects, " "Threaded scrolling is disabled, " "Scrollbar scrolling, " - "Frame overlay, " - "Handling scroll from main thread, " "Not opaque for text and LCD text, " "Can't paint scrolling background and LCD text, " "Non fast scrollable region, " "Failed hit test, " "No scrolling layer, " "Not scrollable, " - "Continuing main thread scroll, " "Non-invertible transform, " - "Page-based scrolling, " "Wheel event handler region, " "Touch event handler region", MainThreadScrollingReason::AsText(0xffffffffu)); diff --git a/chromium/cc/input/scrollbar_controller.cc b/chromium/cc/input/scrollbar_controller.cc index 75f786b67c1..7cd1f44f02a 100644 --- a/chromium/cc/input/scrollbar_controller.cc +++ b/chromium/cc/input/scrollbar_controller.cc @@ -50,26 +50,22 @@ ScrollbarLayerImplBase* ScrollbarController::ScrollbarLayer() const { return nullptr; } -// Performs hit test and prepares scroll deltas that will be used by GSB and -// GSU. -InputHandlerPointerResult ScrollbarController::HandlePointerDown( - const gfx::PointF position_in_widget, - bool jump_key_modifier) { - LayerImpl* layer_impl = GetLayerHitByPoint(position_in_widget); - +PointerResultType ScrollbarController::HitTest( + const gfx::PointF position_in_widget) const { // If a non-custom scrollbar layer was not found, we return early as there is // no point in setting additional state in the ScrollbarController. Return an // empty InputHandlerPointerResult in this case so that when it is bubbled up // to InputHandlerProxy::RouteToTypeSpecificHandler, the pointer event gets // passed on to the main thread. + const LayerImpl* layer_impl = GetLayerHitByPoint(position_in_widget); if (!(layer_impl && layer_impl->IsScrollbarLayer())) - return InputHandlerPointerResult(); + return PointerResultType::kUnhandled; // If the scrollbar layer has faded out (eg: Overlay scrollbars), don't // initiate a scroll. const ScrollbarLayerImplBase* scrollbar = ToScrollbarLayer(layer_impl); if (scrollbar->OverlayScrollbarOpacity() == 0.f) - return InputHandlerPointerResult(); + return PointerResultType::kUnhandled; // If the scroll_node has a main_thread_scrolling_reason, don't initiate a // scroll. @@ -78,8 +74,23 @@ InputHandlerPointerResult ScrollbarController::HandlePointerDown( ->property_trees() ->scroll_tree.FindNodeFromElementId(scrollbar->scroll_element_id()); if (target_node->main_thread_scrolling_reasons) + return PointerResultType::kUnhandled; + + return PointerResultType::kScrollbarScroll; +} + +// Performs hit test and prepares scroll deltas that will be used by GSB and +// GSU. +InputHandlerPointerResult ScrollbarController::HandlePointerDown( + const gfx::PointF position_in_widget, + bool jump_key_modifier) { + if (HitTest(position_in_widget) != PointerResultType::kScrollbarScroll) return InputHandlerPointerResult(); + // TODO(arakeri): GetLayerHitByPoint should ideally be called only once per + // pointerdown. This needs to be optimized. See crbug.com/1156922. + const ScrollbarLayerImplBase* scrollbar = + ToScrollbarLayer(GetLayerHitByPoint(position_in_widget)); captured_scrollbar_metadata_ = CapturedScrollbarMetadata(); captured_scrollbar_metadata_->scroll_element_id = scrollbar->scroll_element_id(); @@ -255,7 +266,7 @@ float ScrollbarController::GetScrollDeltaForAbsoluteJump() const { return delta * GetScrollerToScrollbarRatio() * GetPageScaleFactorForScroll(); } -int ScrollbarController::GetScrollDeltaForDragPosition( +float ScrollbarController::GetScrollDeltaForDragPosition( const gfx::PointF pointer_position_in_widget) const { const ScrollbarLayerImplBase* scrollbar = ScrollbarLayer(); // Convert the move position to scrollbar layer relative for comparison with @@ -278,9 +289,7 @@ int ScrollbarController::GetScrollDeltaForDragPosition( // correct amount, we have to convert the delta to be unscaled (i.e. multiply // by the page scale factor), as GSU deltas are always unscaled. scroll_delta *= GetPageScaleFactorForScroll(); - - // Scroll delta floored to match main thread per pixel behavior - return floorf(scroll_delta); + return scroll_delta; } // Performs hit test and prepares scroll deltas that will be used by GSU. @@ -333,7 +342,7 @@ InputHandlerPointerResult ScrollbarController::HandlePointerMove( // valid ScrollNode. DCHECK(target_node); - int delta = GetScrollDeltaForDragPosition(position_in_widget); + float delta = GetScrollDeltaForDragPosition(position_in_widget); if (drag_state_->scroller_length_at_previous_move != scrollbar->scroll_layer_length()) { drag_state_->scroller_displacement = delta; @@ -436,7 +445,7 @@ float ScrollbarController::GetScrollerToScrollbarRatio() const { scrollbar->orientation() == ScrollbarOrientation::VERTICAL ? thumb_rect.height() : thumb_rect.width(); - int viewport_length = GetViewportLength(); + float viewport_length = GetViewportLength(); return (scroll_layer_length - viewport_length) / (scrollbar_track_length - scrollbar_thumb_length); @@ -590,6 +599,7 @@ void ScrollbarController::StartAutoScrollAnimation( ? AutoScrollDirection::AUTOSCROLL_BACKWARD : AutoScrollDirection::AUTOSCROLL_FORWARD; + layer_tree_host_impl_->mutator_host()->ScrollAnimationAbort(); layer_tree_host_impl_->AutoScrollAnimationCreate( *scroll_node, target_offset_vector, std::abs(velocity)); } @@ -627,7 +637,7 @@ LayerImpl* ScrollbarController::GetLayerHitByPoint( return layer_impl; } -int ScrollbarController::GetViewportLength() const { +float ScrollbarController::GetViewportLength() const { const ScrollbarLayerImplBase* scrollbar = ScrollbarLayer(); const ScrollNode* scroll_node = layer_tree_host_impl_->active_tree() @@ -649,7 +659,7 @@ int ScrollbarController::GetViewportLength() const { return length / GetPageScaleFactorForScroll(); } -int ScrollbarController::GetScrollDeltaForPercentBasedScroll() const { +float ScrollbarController::GetScrollDeltaForPercentBasedScroll() const { const ScrollbarLayerImplBase* scrollbar = ScrollbarLayer(); const ScrollNode* scroll_node = @@ -677,10 +687,10 @@ float ScrollbarController::GetPageScaleFactorForScroll() const { return layer_tree_host_impl_->active_tree()->page_scale_factor_for_scroll(); } -int ScrollbarController::GetScrollDeltaForScrollbarPart( +float ScrollbarController::GetScrollDeltaForScrollbarPart( const ScrollbarPart scrollbar_part, const bool jump_key_modifier) const { - int scroll_delta = 0; + float scroll_delta = 0; switch (scrollbar_part) { case ScrollbarPart::BACK_BUTTON: diff --git a/chromium/cc/input/scrollbar_controller.h b/chromium/cc/input/scrollbar_controller.h index fb827f06a48..6d15cbcd295 100644 --- a/chromium/cc/input/scrollbar_controller.h +++ b/chromium/cc/input/scrollbar_controller.h @@ -148,10 +148,13 @@ class CC_EXPORT ScrollbarController { ScrollbarLayerImplBase* ScrollbarLayer() const; void WillBeginImplFrame(); void ResetState(); + PointerResultType HitTest(const gfx::PointF position_in_widget) const; private: FRIEND_TEST_ALL_PREFIXES(ScrollUnifiedLayerTreeHostImplTest, ThumbDragAfterJumpClick); + FRIEND_TEST_ALL_PREFIXES(ScrollUnifiedLayerTreeHostImplTest, + AbortAnimatedScrollBeforeStartingAutoscroll); // "Autoscroll" here means the continuous scrolling that occurs when the // pointer is held down on a hit-testable area of the scrollbar such as an @@ -232,8 +235,8 @@ class CC_EXPORT ScrollbarController { gfx::Rect GetRectForScrollbarPart(const ScrollbarPart scrollbar_part) const; LayerImpl* GetLayerHitByPoint(const gfx::PointF position_in_widget) const; - int GetScrollDeltaForScrollbarPart(const ScrollbarPart scrollbar_part, - const bool jump_key_modifier) const; + float GetScrollDeltaForScrollbarPart(const ScrollbarPart scrollbar_part, + const bool jump_key_modifier) const; // Makes position_in_widget relative to the scrollbar. gfx::PointF GetScrollbarRelativePosition(const gfx::PointF position_in_widget, @@ -257,17 +260,17 @@ class CC_EXPORT ScrollbarController { bool jump_key_modifier) const; // Calculates the delta based on position_in_widget and drag_origin. - int GetScrollDeltaForDragPosition( + float GetScrollDeltaForDragPosition( const gfx::PointF pointer_position_in_widget) const; // Returns the ratio of the scroller length to the scrollbar length. This is // needed to scale the scroll delta for thumb drag. float GetScrollerToScrollbarRatio() const; - int GetViewportLength() const; + float GetViewportLength() const; // Returns the pixel delta for a percent-based scroll of the scrollbar - int GetScrollDeltaForPercentBasedScroll() const; + float GetScrollDeltaForPercentBasedScroll() const; // Returns the page scale factor (i.e. pinch zoom factor). This is relevant // for root viewport scrollbar scrolling. diff --git a/chromium/cc/input/snap_selection_strategy.cc b/chromium/cc/input/snap_selection_strategy.cc index 704e07d384b..b99b3de4938 100644 --- a/chromium/cc/input/snap_selection_strategy.cc +++ b/chromium/cc/input/snap_selection_strategy.cc @@ -124,18 +124,18 @@ bool DirectionStrategy::IsValidSnapPosition(SearchAxis axis, float position) const { // If not using fractional offsets then it is possible for the currently // snapped area's offset, which is fractional, to not be equal to the current - // scroll offset, which is not fractional. Therefore we round the offsets so - // that any position within 0.5 of the current position is ignored. + // scroll offset, which is not fractional. Therefore we truncate the offsets + // so that any position within 1 of the current position is ignored. if (axis == SearchAxis::kX) { float delta = position - current_position_.x(); if (!use_fractional_offsets_) - delta = std::round(delta); + delta = delta > 0 ? std::floor(delta) : std::ceil(delta); return (step_.x() > 0 && delta > 0) || // "Right" arrow (step_.x() < 0 && delta < 0); // "Left" arrow } else { float delta = position - current_position_.y(); if (!use_fractional_offsets_) - delta = std::round(delta); + delta = delta > 0 ? std::floor(delta) : std::ceil(delta); return (step_.y() > 0 && delta > 0) || // "Down" arrow (step_.y() < 0 && delta < 0); // "Up" arrow } diff --git a/chromium/cc/input/threaded_input_handler.cc b/chromium/cc/input/threaded_input_handler.cc index f1510a7281e..a22f04db5f7 100644 --- a/chromium/cc/input/threaded_input_handler.cc +++ b/chromium/cc/input/threaded_input_handler.cc @@ -249,7 +249,8 @@ InputHandler::ScrollStatus ThreadedInputHandler::ScrollBegin( layer_impl, first_scrolling_layer_or_scrollbar)) { TRACE_EVENT_INSTANT0("cc", "Failed Hit Test", TRACE_EVENT_SCOPE_THREAD); - scroll_status.thread = InputHandler::ScrollThread::SCROLL_UNKNOWN; + scroll_status.thread = + InputHandler::ScrollThread::SCROLL_ON_MAIN_THREAD; scroll_status.main_thread_scrolling_reasons = MainThreadScrollingReason::kFailedHitTest; return scroll_status; @@ -277,15 +278,17 @@ InputHandler::ScrollStatus ThreadedInputHandler::ScrollBegin( scroll_status.thread = InputHandler::ScrollThread::SCROLL_ON_MAIN_THREAD; return scroll_status; } else if (!scrolling_node) { + // TODO(crbug.com/1155663): Make sure to set main_thread_scrolling_reasons + // only when ScrollStatus.thread is set to + // InputHander::ScrollThread::SCROLL_ON_MAIN_THREAD scroll_status.main_thread_scrolling_reasons = MainThreadScrollingReason::kNoScrollingLayer; if (compositor_delegate_.GetSettings().is_layer_tree_for_subframe) { // OOPIFs never have a viewport scroll node so if we can't scroll // we need to be bubble up to the parent frame. This happens by - // returning SCROLL_UNKNOWN. + // returning SCROLL_IGNORED. TRACE_EVENT_INSTANT0("cc", "Ignored - No ScrollNode (OOPIF)", TRACE_EVENT_SCOPE_THREAD); - scroll_status.thread = InputHandler::ScrollThread::SCROLL_UNKNOWN; } else { // If we didn't hit a layer above we'd usually fallback to the // viewport scroll node. However, there may not be one if a scroll @@ -295,8 +298,8 @@ InputHandler::ScrollStatus ThreadedInputHandler::ScrollBegin( // configurations where input is allowed prior to a commit. TRACE_EVENT_INSTANT0("cc", "Ignored - No ScrollNode", TRACE_EVENT_SCOPE_THREAD); - scroll_status.thread = InputHandler::ScrollThread::SCROLL_IGNORED; } + scroll_status.thread = InputHandler::ScrollThread::SCROLL_IGNORED; return scroll_status; } @@ -314,7 +317,12 @@ InputHandler::ScrollStatus ThreadedInputHandler::ScrollBegin( // oopif. if (GetViewport().ShouldScroll(*CurrentlyScrollingNode()) && !GetViewport().CanScroll(*CurrentlyScrollingNode(), *scroll_state)) { - scroll_status.bubble = true; + // TODO(crbug.com/1155758): This is a temporary workaround for GuestViews + // as they create viewport nodes and want to bubble scroll if the + // viewport cannot scroll in the given delta directions. There should be + // a parameter to ThreadInputHandler to specify whether unused delta is + // consumed by the viewport or bubbles to the parent. + scroll_status.viewport_cannot_scroll = true; } return scroll_status; @@ -575,6 +583,14 @@ InputHandlerPointerResult ThreadedInputHandler::MouseMoveAt( return result; } +PointerResultType ThreadedInputHandler::HitTest( + const gfx::PointF& viewport_point) { + return compositor_delegate_.GetSettings() + .compositor_threaded_scrollbar_scrolling + ? scrollbar_controller_->HitTest(viewport_point) + : PointerResultType::kUnhandled; +} + InputHandlerPointerResult ThreadedInputHandler::MouseDown( const gfx::PointF& viewport_point, bool shift_modifier) { @@ -951,11 +967,14 @@ void ThreadedInputHandler::ProcessCommitDeltas( commit_data->manipulation_info |= kManipulationInfoPrecisionTouchPad; if (has_pinch_zoomed_) commit_data->manipulation_info |= kManipulationInfoPinchZoom; + if (has_scrolled_by_scrollbar_) + commit_data->manipulation_info |= kManipulationInfoScrollbar; has_scrolled_by_wheel_ = false; has_scrolled_by_touch_ = false; has_scrolled_by_precisiontouchpad_ = false; has_pinch_zoomed_ = false; + has_scrolled_by_scrollbar_ = false; commit_data->scroll_gesture_did_end = scroll_gesture_did_end_; scroll_gesture_did_end_ = false; @@ -1170,16 +1189,16 @@ gfx::Vector2dF ThreadedInputHandler::ResolveScrollGranularityToPixels( if (granularity == ui::ScrollGranularity::kScrollByPercentage) { gfx::SizeF scroller_size = gfx::SizeF(scroll_node.container_bounds); - - gfx::SizeF viewport_size = - InnerViewportScrollNode() - ? gfx::SizeF(InnerViewportScrollNode()->container_bounds) - : gfx::SizeF(ActiveTree().GetDeviceViewport().size()); + gfx::SizeF viewport_size(compositor_delegate_.VisualDeviceViewportSize()); // Convert from rootframe coordinates to screen coordinates (physical - // pixels). + // pixels if --use-zoom-for-dsf enabled, DIPs otherwise). scroller_size.Scale(compositor_delegate_.PageScaleFactor()); + // Convert from physical pixels to screen coordinates (if --use-zoom-for-dsf + // enabled, `DeviceScaleFactor()` returns 1). + viewport_size.Scale(1 / compositor_delegate_.DeviceScaleFactor()); + pixel_delta = ScrollUtils::ResolveScrollPercentageToPixels( pixel_delta, scroller_size, viewport_size); } @@ -1823,6 +1842,7 @@ ScrollNode* ThreadedInputHandler::FindNodeToLatch(ScrollState* scroll_state, ui::ScrollInputType type) { ScrollTree& scroll_tree = GetScrollTree(); ScrollNode* scroll_node = nullptr; + ScrollNode* first_scrollable_node = nullptr; for (ScrollNode* cur_node = starting_node; cur_node; cur_node = scroll_tree.parent(cur_node)) { if (GetViewport().ShouldScroll(*cur_node)) { @@ -1836,10 +1856,11 @@ ScrollNode* ThreadedInputHandler::FindNodeToLatch(ScrollState* scroll_state, if (!cur_node->scrollable) continue; - // For UX reasons, autoscrolling should always latch to the top-most - // scroller, even if it can't scroll in the initial direction. - if (type == ui::ScrollInputType::kAutoscroll || - CanConsumeDelta(*scroll_state, *cur_node)) { + if (!first_scrollable_node) { + first_scrollable_node = cur_node; + } + + if (CanConsumeDelta(*scroll_state, *cur_node)) { scroll_node = cur_node; break; } @@ -1861,6 +1882,14 @@ ScrollNode* ThreadedInputHandler::FindNodeToLatch(ScrollState* scroll_state, } } + // If the root scroller can not consume delta in an autoscroll, latch on + // to the top most autoscrollable scroller. See https://crbug.com/969150 + if ((type == ui::ScrollInputType::kAutoscroll) && first_scrollable_node) { + // If scroll_node is nullptr or delta can not be consumed + if (!(scroll_node && CanConsumeDelta(*scroll_state, *scroll_node))) + scroll_node = first_scrollable_node; + } + return scroll_node; } @@ -2093,6 +2122,8 @@ void ThreadedInputHandler::UpdateScrollSourceInfo( has_scrolled_by_wheel_ = true; } else if (type == ui::ScrollInputType::kTouchscreen) { has_scrolled_by_touch_ = true; + } else if (type == ui::ScrollInputType::kScrollbar) { + has_scrolled_by_scrollbar_ = true; } } diff --git a/chromium/cc/input/threaded_input_handler.h b/chromium/cc/input/threaded_input_handler.h index d84d2ebd8ce..80b33189b2f 100644 --- a/chromium/cc/input/threaded_input_handler.h +++ b/chromium/cc/input/threaded_input_handler.h @@ -56,6 +56,7 @@ class CC_EXPORT ThreadedInputHandler : public InputHandler, ScrollState* scroll_state, base::TimeDelta delayed_by = base::TimeDelta()) override; void ScrollEnd(bool should_snap = false) override; + PointerResultType HitTest(const gfx::PointF& viewport_point) override; void RecordScrollBegin(ui::ScrollInputType input_type, ScrollBeginThreadState scroll_start_state) override; void RecordScrollEnd(ui::ScrollInputType input_type) override; @@ -168,6 +169,8 @@ class CC_EXPORT ThreadedInputHandler : public InputHandler, private: FRIEND_TEST_ALL_PREFIXES(ScrollUnifiedLayerTreeHostImplTest, + AbortAnimatedScrollBeforeStartingAutoscroll); + FRIEND_TEST_ALL_PREFIXES(ScrollUnifiedLayerTreeHostImplTest, AnimatedScrollYielding); FRIEND_TEST_ALL_PREFIXES(ScrollUnifiedLayerTreeHostImplTest, AutoscrollOnDeletedScrollbar); @@ -434,6 +437,7 @@ class CC_EXPORT ThreadedInputHandler : public InputHandler, bool has_scrolled_by_wheel_ = false; bool has_scrolled_by_touch_ = false; bool has_scrolled_by_precisiontouchpad_ = false; + bool has_scrolled_by_scrollbar_ = false; // Must be the last member to ensure this is destroyed first in the // destruction order and invalidates all weak pointers. diff --git a/chromium/cc/ipc/cc_param_traits_macros.h b/chromium/cc/ipc/cc_param_traits_macros.h index af561bb6feb..146444cde30 100644 --- a/chromium/cc/ipc/cc_param_traits_macros.h +++ b/chromium/cc/ipc/cc_param_traits_macros.h @@ -6,6 +6,7 @@ #define CC_IPC_CC_PARAM_TRAITS_MACROS_H_ #include "base/component_export.h" +#include "cc/input/browser_controls_state.h" #include "cc/input/overscroll_behavior.h" #include "cc/input/touch_action.h" #include "cc/trees/browser_controls_params.h" @@ -34,4 +35,7 @@ IPC_STRUCT_TRAITS_BEGIN(cc::BrowserControlsParams) IPC_STRUCT_TRAITS_MEMBER(only_expand_top_controls_at_page_top) IPC_STRUCT_TRAITS_END() +IPC_ENUM_TRAITS_MAX_VALUE(cc::BrowserControlsState, + cc::BrowserControlsState::kMaxValue) + #endif // CC_IPC_CC_PARAM_TRAITS_MACROS_H_ diff --git a/chromium/cc/layers/content_layer_client.h b/chromium/cc/layers/content_layer_client.h index e7643fc5cf4..25d1a33757c 100644 --- a/chromium/cc/layers/content_layer_client.h +++ b/chromium/cc/layers/content_layer_client.h @@ -22,7 +22,7 @@ class CC_EXPORT ContentLayerClient { // layer this client paints, that the client is capable of painting via // paintContents(). Calling paintContents() will return a DisplayItemList // that is guaranteed valid only within this region. - virtual gfx::Rect PaintableRegion() = 0; + virtual gfx::Rect PaintableRegion() const = 0; // Paints the content area for the layer, typically dirty rects submitted // to the layer itself, into a DisplayItemList that it returns. The diff --git a/chromium/cc/layers/heads_up_display_layer.cc b/chromium/cc/layers/heads_up_display_layer.cc index 0c56668a44f..09dbbd9e4d2 100644 --- a/chromium/cc/layers/heads_up_display_layer.cc +++ b/chromium/cc/layers/heads_up_display_layer.cc @@ -5,6 +5,8 @@ #include "cc/layers/heads_up_display_layer.h" #include <algorithm> +#include <utility> +#include <vector> #include "base/trace_event/trace_event.h" #include "cc/layers/heads_up_display_layer_impl.h" @@ -37,13 +39,21 @@ void HeadsUpDisplayLayer::UpdateLocationAndSize( gfx::Size bounds; - if (layer_tree_host()->GetDebugState().ShowHudRects()) { + // If the HUD is not displaying full-viewport rects (e.g., it is showing the + // Frame Rendering Stats), use a fixed size. + constexpr int kDefaultHUDSize = 256; + bounds.SetSize(kDefaultHUDSize, kDefaultHUDSize); + + if (layer_tree_host()->GetDebugState().ShowDebugRects()) { bounds = device_viewport_in_layout_pixels; - } else { - // If the HUD is not displaying full-viewport rects (e.g., it is showing the - // Frame Rendering Stats), use a fixed size. - constexpr int kDefaultHUDSize = 256; - bounds.SetSize(kDefaultHUDSize, kDefaultHUDSize); + } else if (layer_tree_host()->GetDebugState().show_web_vital_metrics || + layer_tree_host()->GetDebugState().show_smoothness_metrics) { + // If the HUD is used to display performance metrics (which is on the right + // hand side_, make sure the bounds has the correct width, with a fixed + // height. + bounds.set_width(device_viewport_in_layout_pixels.width()); + // Increase HUD layer height to make sure all the metrics are showing. + bounds.set_height(kDefaultHUDSize * 2); } SetBounds(bounds); @@ -67,6 +77,11 @@ void HeadsUpDisplayLayer::SetLayoutShiftRects( layout_shift_rects_ = rects; } +void HeadsUpDisplayLayer::UpdateWebVitalMetrics( + std::unique_ptr<WebVitalMetrics> web_vital_metrics) { + web_vital_metrics_ = std::move(web_vital_metrics); +} + void HeadsUpDisplayLayer::PushPropertiesTo(LayerImpl* layer) { Layer::PushPropertiesTo(layer); TRACE_EVENT0("cc", "HeadsUpDisplayLayer::PushPropertiesTo"); @@ -76,6 +91,8 @@ void HeadsUpDisplayLayer::PushPropertiesTo(LayerImpl* layer) { layer_impl->SetHUDTypeface(typeface_); layer_impl->SetLayoutShiftRects(layout_shift_rects_); layout_shift_rects_.clear(); + if (web_vital_metrics_ && web_vital_metrics_->HasValue()) + layer_impl->SetWebVitalMetrics(std::move(web_vital_metrics_)); } } // namespace cc diff --git a/chromium/cc/layers/heads_up_display_layer.h b/chromium/cc/layers/heads_up_display_layer.h index 1dea4e99e44..9d497439259 100644 --- a/chromium/cc/layers/heads_up_display_layer.h +++ b/chromium/cc/layers/heads_up_display_layer.h @@ -7,9 +7,11 @@ #include <memory> #include <string> +#include <vector> #include "cc/cc_export.h" #include "cc/layers/layer.h" +#include "cc/metrics/web_vital_metrics.h" #include "third_party/skia/include/core/SkRefCnt.h" #include "third_party/skia/include/core/SkTypeface.h" #include "ui/gfx/geometry/rect.h" @@ -25,6 +27,8 @@ class CC_EXPORT HeadsUpDisplayLayer : public Layer { void UpdateLocationAndSize(const gfx::Size& device_viewport, float device_scale_factor); + void UpdateWebVitalMetrics( + std::unique_ptr<WebVitalMetrics> web_vital_metrics); const std::vector<gfx::Rect>& LayoutShiftRects() const; void SetLayoutShiftRects(const std::vector<gfx::Rect>& rects); @@ -43,6 +47,8 @@ class CC_EXPORT HeadsUpDisplayLayer : public Layer { sk_sp<SkTypeface> typeface_; std::vector<gfx::Rect> layout_shift_rects_; + + std::unique_ptr<WebVitalMetrics> web_vital_metrics_; }; } // namespace cc diff --git a/chromium/cc/layers/heads_up_display_layer_impl.cc b/chromium/cc/layers/heads_up_display_layer_impl.cc index 3e5cc2b7319..de7936aad42 100644 --- a/chromium/cc/layers/heads_up_display_layer_impl.cc +++ b/chromium/cc/layers/heads_up_display_layer_impl.cc @@ -8,6 +8,7 @@ #include <stdint.h> #include <algorithm> +#include <iomanip> #include <utility> #include <vector> @@ -20,6 +21,7 @@ #include "base/trace_event/process_memory_dump.h" #include "base/trace_event/trace_event.h" #include "base/trace_event/traced_value.h" +#include "build/build_config.h" #include "cc/debug/debug_colors.h" #include "cc/metrics/dropped_frame_counter.h" #include "cc/paint/display_item_list.h" @@ -90,6 +92,35 @@ class DummyImageProvider : public ImageProvider { } }; +std::string ToStringTwoDecimalPrecision(double input) { + std::stringstream stream; + stream << std::fixed << std::setprecision(2) << input; + return stream.str(); +} + +#if defined(OS_ANDROID) +struct MetricsDrawSizes { + const int kTopPadding = 35; + const int kPadding = 15; + const int kFontHeight = 32; + const int kWidth = 500; + const int kSidePadding = 20; +} constexpr metrics_sizes; +#else +struct MetricsDrawSizes { + const int kTopPadding = 35; + const int kPadding = 15; + const int kFontHeight = 22; + const int kWidth = 400; + const int kSidePadding = 20; +} constexpr metrics_sizes; +#endif + +constexpr int ComputeTotalHeight(int num_of_lines) { + int num_of_spaces = std::max(0, num_of_lines - 1); + return num_of_lines * metrics_sizes.kFontHeight + + num_of_spaces * metrics_sizes.kPadding + 2 * metrics_sizes.kTopPadding; +} } // namespace HeadsUpDisplayLayerImpl::HeadsUpDisplayLayerImpl(LayerTreeImpl* tree_impl, @@ -522,6 +553,11 @@ void HeadsUpDisplayLayerImpl::SetLayoutShiftRects( layout_shift_rects_ = rects; } +void HeadsUpDisplayLayerImpl::SetWebVitalMetrics( + std::unique_ptr<WebVitalMetrics> web_vital_metrics) { + web_vital_metrics_ = std::move(web_vital_metrics); +} + void HeadsUpDisplayLayerImpl::PushPropertiesTo(LayerImpl* layer) { LayerImpl::PushPropertiesTo(layer); @@ -531,6 +567,8 @@ void HeadsUpDisplayLayerImpl::PushPropertiesTo(LayerImpl* layer) { layer_impl->SetHUDTypeface(typeface_); layer_impl->SetLayoutShiftRects(layout_shift_rects_); layout_shift_rects_.clear(); + if (web_vital_metrics_ && web_vital_metrics_->HasValue()) + layer_impl->SetWebVitalMetrics(std::move(web_vital_metrics_)); } void HeadsUpDisplayLayerImpl::UpdateHudContents() { @@ -544,6 +582,9 @@ void HeadsUpDisplayLayerImpl::UpdateHudContents() { if (debug_state.show_fps_counter) { throughput_value_ = layer_tree_impl()->dropped_frame_counter()->GetAverageThroughput(); + const auto& args = layer_tree_impl()->CurrentBeginFrameArgs(); + if (args.IsValid()) + frame_interval_ = args.interval; } if (debug_state.ShowMemoryStats()) { @@ -564,26 +605,46 @@ void HeadsUpDisplayLayerImpl::DrawHudContents(PaintCanvas* canvas) { canvas->save(); canvas->scale(internal_contents_scale_, internal_contents_scale_); - if (debug_state.ShowHudRects()) { + if (debug_state.ShowDebugRects()) { DrawDebugRects(canvas, layer_tree_impl()->debug_rect_history()); if (IsAnimatingHUDContents()) { layer_tree_impl()->SetNeedsRedraw(); } } - if (!debug_state.show_fps_counter) { + if (!debug_state.ShouldDrawHudInfo()) { canvas->restore(); return; } - SkRect area = DrawFrameThroughputDisplay( - canvas, layer_tree_impl()->dropped_frame_counter(), 0, 0); - area = DrawGpuRasterizationStatus(canvas, 0, area.bottom(), - std::max<SkScalar>(area.width(), 150)); + SkRect area = SkRect::MakeXYWH(0, 0, 0, 0); - if (debug_state.ShowMemoryStats() && memory_entry_.total_bytes_used) - DrawMemoryDisplay(canvas, 0, area.bottom(), - std::max<SkScalar>(area.width(), 150)); + if (debug_state.show_fps_counter) { + area = DrawFrameThroughputDisplay( + canvas, layer_tree_impl()->dropped_frame_counter(), 0, 0); + area = DrawGpuRasterizationStatus(canvas, 0, area.bottom(), + std::max<SkScalar>(area.width(), 150)); + } + + if (debug_state.ShowMemoryStats() && memory_entry_.total_bytes_used) { + area = DrawMemoryDisplay(canvas, 0, area.bottom(), + std::max<SkScalar>(area.width(), 150)); + } + + SkRect metrics_area = SkRect::MakeXYWH( + std::max<SkScalar>(0, bounds().width() - metrics_sizes.kWidth), 0, + metrics_sizes.kWidth, 0); + if (debug_state.show_web_vital_metrics) { + metrics_area = DrawWebVitalMetrics( + canvas, metrics_area.left(), metrics_area.bottom(), + std::max<SkScalar>(metrics_area.width(), metrics_sizes.kWidth)); + } + + if (debug_state.show_smoothness_metrics) { + metrics_area = DrawSmoothnessMetrics( + canvas, metrics_area.left(), metrics_area.bottom(), + std::max<SkScalar>(metrics_area.width(), metrics_sizes.kWidth)); + } canvas->restore(); } @@ -640,6 +701,16 @@ void HeadsUpDisplayLayerImpl::DrawGraphLines(PaintCanvas* canvas, bounds.bottom(), *flags); } +void HeadsUpDisplayLayerImpl::DrawSeparatorLine(PaintCanvas* canvas, + PaintFlags* flags, + const SkRect& bounds) const { + // Draw separator line as transparent white. + constexpr auto kSeparatorLineColor = SkColorSetARGB(64, 255, 255, 255); + flags->setColor(kSeparatorLineColor); + canvas->drawLine(bounds.left(), bounds.top(), bounds.right(), bounds.top(), + *flags); +} + SkRect HeadsUpDisplayLayerImpl::DrawFrameThroughputDisplay( PaintCanvas* canvas, const DroppedFrameCounter* dropped_frame_counter, @@ -674,14 +745,15 @@ SkRect HeadsUpDisplayLayerImpl::DrawFrameThroughputDisplay( kGraphWidth, kGraphHeight); // Draw the frame rendering stats. - const std::string title("Frames"); - const std::string value_text = base::StringPrintf("%d%%", throughput_value_); - const std::string dropped_frames_text = - base::StringPrintf("%zu (%zu m) dropped of %zu", - dropped_frame_counter->total_compositor_dropped(), - dropped_frame_counter->total_main_dropped(), - dropped_frame_counter->total_frames()); - + const std::string title("Frame Rate"); + std::string value_text = "n/a"; + if (frame_interval_.has_value()) { + // This assumes a constant frame rate. If the frame rate changed throughout + // the sequence, then maybe we should average over the sequence. + double frame_rate = static_cast<double>(throughput_value_) / + (100 * frame_interval_.value().InSecondsF()); + value_text = base::StringPrintf("%5.1f fps", frame_rate); + } VLOG(1) << value_text; flags.setColor(DebugColors::HUDTitleColor()); @@ -689,9 +761,7 @@ SkRect HeadsUpDisplayLayerImpl::DrawFrameThroughputDisplay( title_bounds.left(), title_bounds.bottom()); flags.setColor(DebugColors::FPSDisplayTextAndGraphColor()); - DrawText(canvas, flags, value_text, TextAlign::kLeft, kFontHeight, - text_bounds.left(), text_bounds.bottom()); - DrawText(canvas, flags, dropped_frames_text, TextAlign::kRight, kFontHeight, + DrawText(canvas, flags, value_text, TextAlign::kRight, kFontHeight, text_bounds.right(), text_bounds.bottom()); DrawGraphLines(canvas, &flags, graph_bounds); @@ -1022,6 +1092,134 @@ void HeadsUpDisplayLayerImpl::DrawDebugRects( } } +int HeadsUpDisplayLayerImpl::DrawSingleMetric( + PaintCanvas* canvas, + int left, + int right, + int top, + std::string name, + const WebVitalMetrics::MetricsInfo& info, + bool has_value, + double value) const { + std::string value_str = "-"; + SkColor metrics_color = DebugColors::HUDTitleColor(); + if (has_value) { + value_str = ToStringTwoDecimalPrecision(value) + info.UnitToString(); + if (value < info.green_threshold) + metrics_color = SK_ColorGREEN; + else if (value < info.yellow_threshold) + metrics_color = SK_ColorYELLOW; + else + metrics_color = SK_ColorRED; + } + + PaintFlags flags; + flags.setColor(DebugColors::HUDTitleColor()); + DrawText(canvas, flags, name, TextAlign::kLeft, metrics_sizes.kFontHeight, + left + metrics_sizes.kSidePadding, top); + flags.setColor(metrics_color); + DrawText(canvas, flags, value_str, TextAlign::kRight, + metrics_sizes.kFontHeight, right - metrics_sizes.kSidePadding, top); + + return top + metrics_sizes.kFontHeight + metrics_sizes.kPadding; +} + +SkRect HeadsUpDisplayLayerImpl::DrawWebVitalMetrics(PaintCanvas* canvas, + int left, + int top, + int width) const { + const int height = ComputeTotalHeight(3); + const SkRect area = SkRect::MakeXYWH(left, top, width, height); + + PaintFlags flags; + DrawGraphBackground(canvas, &flags, area); + + int current_top = top + metrics_sizes.kTopPadding + metrics_sizes.kFontHeight; + double metric_value = 0.f; + bool has_lcp = web_vital_metrics_ && web_vital_metrics_->has_lcp; + if (has_lcp) + metric_value = web_vital_metrics_->largest_contentful_paint.InSecondsF(); + current_top = DrawSingleMetric( + canvas, left, left + width, current_top, "Largest Contentful Paint", + WebVitalMetrics::lcp_info, has_lcp, metric_value); + + bool has_fid = web_vital_metrics_ && web_vital_metrics_->has_fid; + if (has_fid) + metric_value = web_vital_metrics_->first_input_delay.InMillisecondsF(); + current_top = DrawSingleMetric(canvas, left, left + width, current_top, + "First Input Delay", WebVitalMetrics::fid_info, + has_fid, metric_value); + + bool has_layout_shift = web_vital_metrics_ && web_vital_metrics_->has_cls; + if (has_layout_shift) + metric_value = web_vital_metrics_->layout_shift; + current_top = DrawSingleMetric( + canvas, left, left + width, current_top, "Cumulative Layout Shift", + WebVitalMetrics::cls_info, has_layout_shift, metric_value); + + return area; +} + +int HeadsUpDisplayLayerImpl::DrawSinglePercentageMetric(PaintCanvas* canvas, + int left, + int right, + int top, + std::string name, + double value) const { + std::string value_str = "-"; + SkColor metrics_color = DebugColors::HUDTitleColor(); + value_str = ToStringTwoDecimalPrecision(value) + "%"; + + PaintFlags flags; + flags.setColor(DebugColors::HUDTitleColor()); + DrawText(canvas, flags, name, TextAlign::kLeft, metrics_sizes.kFontHeight, + left + metrics_sizes.kSidePadding, top); + flags.setColor(metrics_color); + DrawText(canvas, flags, value_str, TextAlign::kRight, + metrics_sizes.kFontHeight, right - metrics_sizes.kSidePadding, top); + + return top + metrics_sizes.kFontHeight + metrics_sizes.kPadding; +} + +SkRect HeadsUpDisplayLayerImpl::DrawSmoothnessMetrics(PaintCanvas* canvas, + int left, + int top, + int width) const { + const int height = ComputeTotalHeight(3); + const SkRect area = SkRect::MakeXYWH(left, top, width, height); + + PaintFlags flags; + DrawGraphBackground(canvas, &flags, area); + if (top != 0) { + // There are metrics drawn before this. + SkRect separator = + SkRect::MakeXYWH(area.x(), area.y(), area.width(), area.height()); + DrawSeparatorLine(canvas, &flags, separator); + } + + int current_top = top + metrics_sizes.kTopPadding + metrics_sizes.kFontHeight; + double avg_smoothness = layer_tree_impl() + ->dropped_frame_counter() + ->GetMostRecentAverageSmoothness(); + current_top = + DrawSinglePercentageMetric(canvas, left, left + width, current_top, + "Average Dropped Frame", avg_smoothness); + double worst_smoothness = layer_tree_impl() + ->dropped_frame_counter() + ->sliding_window_max_percent_dropped(); + current_top = + DrawSinglePercentageMetric(canvas, left, left + width, current_top, + "Max Dropped Frame", worst_smoothness); + double percentile_smoothness = layer_tree_impl() + ->dropped_frame_counter() + ->GetMostRecent95PercentileSmoothness(); + current_top = + DrawSinglePercentageMetric(canvas, left, left + width, current_top, + "95th Percentile DF", percentile_smoothness); + + return area; +} + const char* HeadsUpDisplayLayerImpl::LayerTypeAsString() const { return "cc::HeadsUpDisplayLayerImpl"; } diff --git a/chromium/cc/layers/heads_up_display_layer_impl.h b/chromium/cc/layers/heads_up_display_layer_impl.h index 056bfb006f2..804201da7d9 100644 --- a/chromium/cc/layers/heads_up_display_layer_impl.h +++ b/chromium/cc/layers/heads_up_display_layer_impl.h @@ -13,6 +13,7 @@ #include "base/time/time.h" #include "cc/cc_export.h" #include "cc/layers/layer_impl.h" +#include "cc/metrics/web_vital_metrics.h" #include "cc/resources/memory_history.h" #include "cc/resources/resource_pool.h" #include "cc/trees/debug_rect_history.h" @@ -68,6 +69,7 @@ class CC_EXPORT HeadsUpDisplayLayerImpl : public LayerImpl { void SetHUDTypeface(sk_sp<SkTypeface> typeface); void SetLayoutShiftRects(const std::vector<gfx::Rect>& rects); const std::vector<gfx::Rect>& LayoutShiftRects() const; + void SetWebVitalMetrics(std::unique_ptr<WebVitalMetrics> web_vital_metrics); // This evicts hud quad appended during render pass preparation. void EvictHudQuad(const viz::CompositorRenderPassList& list); @@ -103,6 +105,10 @@ class CC_EXPORT HeadsUpDisplayLayerImpl : public LayerImpl { void DrawGraphLines(PaintCanvas* canvas, PaintFlags* flags, const SkRect& bounds) const; + // Draw a separator line at top of bounds. + void DrawSeparatorLine(PaintCanvas* canvas, + PaintFlags* flags, + const SkRect& bounds) const; SkRect DrawFrameThroughputDisplay( PaintCanvas* canvas, @@ -127,6 +133,35 @@ class CC_EXPORT HeadsUpDisplayLayerImpl : public LayerImpl { void DrawDebugRects(PaintCanvas* canvas, DebugRectHistory* debug_rect_history); + // This function draws a single web vital metric. If the metrics doesn't have + // a valid value, the value is set to -1. This function returns the height + // of the current draw so it can be used to calculate the top of the next + // draw. + int DrawSingleMetric(PaintCanvas* canvas, + int left, + int right, + int top, + std::string name, + const WebVitalMetrics::MetricsInfo& info, + bool has_value, + double value) const; + SkRect DrawWebVitalMetrics(PaintCanvas* canvas, + int left, + int top, + int width) const; + + // This function draws a single smoothness related metric. + int DrawSinglePercentageMetric(PaintCanvas* canvas, + int left, + int right, + int top, + std::string name, + double value) const; + SkRect DrawSmoothnessMetrics(PaintCanvas* canvas, + int left, + int top, + int width) const; + ResourcePool::InUsePoolResource in_flight_resource_; std::unique_ptr<ResourcePool> pool_; viz::DrawQuad* current_quad_ = nullptr; @@ -140,12 +175,16 @@ class CC_EXPORT HeadsUpDisplayLayerImpl : public LayerImpl { gfx::Size internal_content_bounds_; uint32_t throughput_value_ = 0.0f; + // Obtained from the current BeginFrameArgs. + base::Optional<base::TimeDelta> frame_interval_; MemoryHistory::Entry memory_entry_; int paint_rects_fade_step_ = 0; int layout_shift_rects_fade_step_ = 0; std::vector<DebugRect> paint_rects_; std::vector<DebugRect> layout_shift_debug_rects_; + std::unique_ptr<WebVitalMetrics> web_vital_metrics_; + base::TimeTicks time_of_last_graph_update_; }; diff --git a/chromium/cc/layers/heads_up_display_unittest.cc b/chromium/cc/layers/heads_up_display_unittest.cc index 688a510cbb6..20b6f21c510 100644 --- a/chromium/cc/layers/heads_up_display_unittest.cc +++ b/chromium/cc/layers/heads_up_display_unittest.cc @@ -97,5 +97,25 @@ class HeadsUpDisplaySizeWithFPS : public LayerTreeTest { SINGLE_AND_MULTI_THREAD_TEST_F(HeadsUpDisplaySizeWithFPS); +class HeadsUpDisplaySizeWithMetrics : public LayerTreeTest { + public: + void InitializeSettings(LayerTreeSettings* settings) override { + settings->initial_debug_state.show_web_vital_metrics = true; + } + + void BeginTest() override { PostSetNeedsCommitToMainThread(); } + + void DidCommit() override { + // The metrics should be shown on the right, so the width of the HUD layer + // should be the saem as the root layer bounds. + ASSERT_TRUE(layer_tree_host()->hud_layer()); + EXPECT_EQ(gfx::Size(layer_tree_host()->root_layer()->bounds().width(), 512), + layer_tree_host()->hud_layer()->bounds()); + EndTest(); + } +}; + +SINGLE_AND_MULTI_THREAD_TEST_F(HeadsUpDisplaySizeWithMetrics); + } // namespace } // namespace cc diff --git a/chromium/cc/layers/layer.cc b/chromium/cc/layers/layer.cc index aca2eaf4da0..2a0de3af1dd 100644 --- a/chromium/cc/layers/layer.cc +++ b/chromium/cc/layers/layer.cc @@ -59,7 +59,6 @@ struct SameSizeAsLayer : public base::RefCounted<SameSizeAsLayer> { int int_fields[6]; gfx::Vector2dF offset; unsigned bitfields; - SkColor safe_opaque_background_color; void* debug_info; }; @@ -87,19 +86,13 @@ Layer::Inputs::Inputs(int layer_id) Layer::Inputs::~Inputs() = default; Layer::LayerTreeInputs::LayerTreeInputs() - : mask_layer(nullptr), - opacity(1.f), - blend_mode(SkBlendMode::kSrcOver), - masks_to_bounds(false), + : masks_to_bounds(false), is_fast_rounded_corner(false), user_scrollable_horizontal(true), user_scrollable_vertical(true), trilinear_filtering(false), hide_layer_and_subtree(false), - scrollable(false), - backdrop_filter_quality(1.0f), - mirror_count(0), - corner_radii({0, 0, 0, 0}) {} + scrollable(false) {} Layer::LayerTreeInputs::~LayerTreeInputs() = default; @@ -128,8 +121,7 @@ Layer::Layer() needs_show_scrollbars_(false), has_transform_node_(false), has_clip_node_(false), - subtree_has_copy_request_(false), - safe_opaque_background_color_(0) {} + subtree_has_copy_request_(false) {} Layer::~Layer() { // Our parent should be holding a reference to us so there should be no @@ -496,25 +488,41 @@ void Layer::SetBackgroundColor(SkColor background_color) { void Layer::SetSafeOpaqueBackgroundColor(SkColor background_color) { DCHECK(IsPropertyChangeAllowed()); - SkColor opaque_color = SkColorSetA(background_color, 255); - if (safe_opaque_background_color_ == opaque_color) + SkColor opaque_color = SkColorSetA(background_color, SK_AlphaOPAQUE); + auto& inputs = EnsureLayerTreeInputs(); + if (inputs.safe_opaque_background_color == opaque_color) return; - safe_opaque_background_color_ = opaque_color; + inputs.safe_opaque_background_color = opaque_color; SetNeedsPushProperties(); } SkColor Layer::SafeOpaqueBackgroundColor() const { if (contents_opaque()) { - // TODO(936906): We should uncomment this DCHECK, since the - // |safe_opaque_background_color_| could be transparent if it is never set - // (the default is 0). But to do that, one test needs to be fixed. - // DCHECK_EQ(SkColorGetA(safe_opaque_background_color_), SK_AlphaOPAQUE); - return safe_opaque_background_color_; + if (!layer_tree_host_ || !layer_tree_host_->IsUsingLayerLists()) { + // In layer tree mode, PropertyTreeBuilder should have calculated the safe + // opaque background color and called SetSafeOpaqueBackgroundColor(). + DCHECK(layer_tree_inputs()); + DCHECK_EQ(SkColorGetA(layer_tree_inputs()->safe_opaque_background_color), + SK_AlphaOPAQUE); + return layer_tree_inputs()->safe_opaque_background_color; + } + // In layer list mode, the PropertyTreeBuilder algorithm doesn't apply + // because it depends on the layer tree hierarchy. Instead we use + // background_color() if it's not transparent, or layer_tree_host_'s + // background_color(), with the alpha channel forced to be opaque. + SkColor color = background_color() == SK_ColorTRANSPARENT + ? layer_tree_host_->background_color() + : background_color(); + return SkColorSetA(color, SK_AlphaOPAQUE); } - SkColor color = background_color(); - if (SkColorGetA(color) == 255) - color = SK_ColorTRANSPARENT; - return color; + if (SkColorGetA(background_color()) == SK_AlphaOPAQUE) { + // The layer is not opaque while the background color is, meaning that the + // background color doesn't cover the whole layer. Use SK_ColorTRANSPARENT + // to avoid intrusive checkerboard where the layer is not covered by the + // background color. + return SK_ColorTRANSPARENT; + } + return background_color(); } void Layer::SetMasksToBounds(bool masks_to_bounds) { @@ -995,6 +1003,22 @@ void Layer::SetDidScrollCallback( EnsureLayerTreeInputs().did_scroll_callback = std::move(callback); } +void Layer::SetSubtreeCaptureId(viz::SubtreeCaptureId subtree_id) { + DCHECK(IsPropertyChangeAllowed()); + + auto& inputs = EnsureLayerTreeInputs(); + if (inputs.subtree_capture_id == subtree_id) + return; + + DCHECK(!inputs.subtree_capture_id.is_valid() || !subtree_id.is_valid()) + << "Not allowed to change from a valid ID to another valid ID, as it may " + "already be in use."; + + inputs.subtree_capture_id = subtree_id; + SetPropertyTreesNeedRebuild(); + SetNeedsCommit(); +} + void Layer::SetScrollable(const gfx::Size& bounds) { DCHECK(IsPropertyChangeAllowed()); auto& inputs = EnsureLayerTreeInputs(); @@ -1305,7 +1329,7 @@ void Layer::PushPropertiesTo(LayerImpl* layer) { layer->SetElementId(inputs_.element_id); layer->SetHasTransformNode(has_transform_node_); layer->SetBackgroundColor(inputs_.background_color); - layer->SetSafeOpaqueBackgroundColor(safe_opaque_background_color_); + layer->SetSafeOpaqueBackgroundColor(SafeOpaqueBackgroundColor()); layer->SetBounds(inputs_.bounds); layer->SetTransformTreeIndex(transform_tree_index()); layer->SetEffectTreeIndex(effect_tree_index()); diff --git a/chromium/cc/layers/layer.h b/chromium/cc/layers/layer.h index 69915971416..f73be661141 100644 --- a/chromium/cc/layers/layer.h +++ b/chromium/cc/layers/layer.h @@ -31,6 +31,7 @@ #include "cc/trees/effect_node.h" #include "cc/trees/property_tree.h" #include "cc/trees/target_property.h" +#include "components/viz/common/surfaces/subtree_capture_id.h" #include "third_party/skia/include/core/SkColor.h" #include "ui/gfx/geometry/point3_f.h" #include "ui/gfx/geometry/rect.h" @@ -151,22 +152,22 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> { virtual void SetBackgroundColor(SkColor background_color); SkColor background_color() const { return inputs_.background_color; } - // Internal to property tree generation. Sets an opaque background color for - // the layer, to be used in place of the background_color() if the layer says - // contents_opaque() is true. + // For layer tree mode only. In layer list mode, client doesn't need to set + // it. Sets an opaque background color for the layer, to be used in place of + // the background_color() if the layer says contents_opaque() is true. void SetSafeOpaqueBackgroundColor(SkColor background_color); - // Returns a background color with opaque-ness equal to the value of + + // Returns a background color with opaqueness equal to the value of // contents_opaque(). - // If the layer says contents_opaque() is true, this returns the value set by - // SetSafeOpaqueBackgroundColor() which should be an opaque color. Otherwise, - // it returns something non-opaque. It prefers to return the + // If the layer says contents_opaque() is true, in layer tree mode, this + // returns the value set by SetSafeOpaqueBackgroundColor() which should be an + // opaque color, and in layer list mode, returns an opaque color calculated + // from background_color() and layer_tree_host()->background_clor(). + // Otherwise, it returns something non-opaque. It prefers to return the // background_color(), but if the background_color() is opaque (and this layer - // claims to not be), then SK_ColorTRANSPARENT is returned. + // claims to not be), then SK_ColorTRANSPARENT is returned to avoid intrusive + // checkerboard where the layer is not covered by the background_color(). SkColor SafeOpaqueBackgroundColor() const; - // For testing, return the actual stored value. - SkColor ActualSafeOpaqueBackgroundColorForTesting() const { - return safe_opaque_background_color_; - } // For layer tree mode only. // Set and get the position of this layer, relative to its parent. This is @@ -472,6 +473,27 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> { void SetDidScrollCallback(base::RepeatingCallback< void(const gfx::ScrollOffset&, const ElementId&)>); + // For layer tree mode only. + // Sets the given |subtree_id| on this layer, so that the layer subtree rooted + // at this layer can be uniquely identified by a FrameSinkVideoCapturer. + // The existence of a valid SubtreeCaptureId on this layer will force it to be + // drawn into a separate CompositorRenderPass. + // Setting a non-valid (i.e. default-constructed SubtreeCaptureId) will clear + // this property. + // It is not allowed to change this ID from a valid ID to another valid ID, + // since a client might already using the existing valid ID to make this layer + // subtree identifiable by a capturer. + // + // Note that this is useful when it's desired to video record a layer subtree + // of a non-root layer using a FrameSinkVideoCapturer, since non-root layers + // are usually not drawn into their own CompositorRenderPass. + void SetSubtreeCaptureId(viz::SubtreeCaptureId subtree_id); + viz::SubtreeCaptureId subtree_capture_id() const { + if (layer_tree_inputs()) + return layer_tree_inputs()->subtree_capture_id; + return viz::SubtreeCaptureId(); + } + // Set or get if the layer and its subtree should be cached as a texture in // the display compositor. This is used as an optimization when it is known // that the layer will be animated without changing its content, or any of its @@ -862,10 +884,10 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> { // If not null, points to one of child layers which is set as mask layer // by SetMaskLayer(). - PictureLayer* mask_layer; + PictureLayer* mask_layer = nullptr; - float opacity; - SkBlendMode blend_mode; + float opacity = 1.0f; + SkBlendMode blend_mode = SkBlendMode::kSrcOver; bool masks_to_bounds : 1; @@ -889,12 +911,20 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> { gfx::Transform transform; gfx::Point3F transform_origin; + // A unique ID that identifies the layer subtree rooted at this layer, so + // that it can be independently captured by the FrameSinkVideoCapturer. If + // this ID is set (i.e. valid), it would force this subtree into a render + // surface that darws in a render pass. + viz::SubtreeCaptureId subtree_capture_id; + + SkColor safe_opaque_background_color = SK_ColorTRANSPARENT; + FilterOperations filters; FilterOperations backdrop_filters; base::Optional<gfx::RRectF> backdrop_filter_bounds; - float backdrop_filter_quality; + float backdrop_filter_quality = 1.0f; - int mirror_count; + int mirror_count = 0; gfx::ScrollOffset scroll_offset; // Size of the scroll container that this layer scrolls in. @@ -925,6 +955,7 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> { int clip_tree_index_; int scroll_tree_index_; int property_tree_sequence_number_; + gfx::Vector2dF offset_to_transform_parent_; // When true, the layer is about to perform an update. Any commit requests @@ -945,8 +976,6 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> { // This value is valid only when LayerTreeHost::has_copy_request() is true bool subtree_has_copy_request_ : 1; - SkColor safe_opaque_background_color_; - std::unique_ptr<LayerDebugInfo> debug_info_; static constexpr gfx::Transform kIdentityTransform{}; diff --git a/chromium/cc/layers/layer_impl.cc b/chromium/cc/layers/layer_impl.cc index 460019a63e3..2d69deaaddd 100644 --- a/chromium/cc/layers/layer_impl.cc +++ b/chromium/cc/layers/layer_impl.cc @@ -42,6 +42,7 @@ #include "ui/gfx/geometry/rect_conversions.h" #include "ui/gfx/geometry/size_conversions.h" #include "ui/gfx/geometry/vector2d_conversions.h" +#include "ui/gfx/transform_util.h" namespace cc { LayerImpl::LayerImpl(LayerTreeImpl* tree_impl, @@ -288,7 +289,7 @@ void LayerImpl::GetContentsResourceId(viz::ResourceId* resource_id, gfx::Size* resource_size, gfx::SizeF* resource_uv_size) const { NOTREACHED(); - *resource_id = 0; + *resource_id = viz::kInvalidResourceId; } gfx::Vector2dF LayerImpl::ScrollBy(const gfx::Vector2dF& scroll) { @@ -555,20 +556,6 @@ void LayerImpl::SetSafeOpaqueBackgroundColor(SkColor background_color) { safe_opaque_background_color_ = background_color; } -SkColor LayerImpl::SafeOpaqueBackgroundColor() const { - if (contents_opaque()) { - // TODO(936906): We should uncomment this DCHECK, since the - // |safe_opaque_background_color_| could be transparent if it is never set - // (the default is 0). But to do that, one test needs to be fixed. - // DCHECK_EQ(SkColorGetA(safe_opaque_background_color_), SK_AlphaOPAQUE); - return safe_opaque_background_color_; - } - SkColor color = background_color(); - if (SkColorGetA(color) == 255) - color = SK_ColorTRANSPARENT; - return color; -} - void LayerImpl::SetContentsOpaque(bool opaque) { contents_opaque_ = opaque; contents_opaque_for_text_ = opaque; @@ -812,7 +799,7 @@ float LayerImpl::GetIdealContentsScale() const { const auto& transform = ScreenSpaceTransform(); if (transform.HasPerspective()) { - float scale = MathUtil::ComputeApproximateMaxScale(transform); + float scale = gfx::ComputeApproximateMaxScale(transform); const int kMaxTilesToCoverLayerDimension = 5; // Cap the scale in a way that it should be covered by at most @@ -842,7 +829,7 @@ float LayerImpl::GetIdealContentsScale() const { } gfx::Vector2dF transform_scales = - MathUtil::ComputeTransform2dScaleComponents(transform, default_scale); + gfx::ComputeTransform2dScaleComponents(transform, default_scale); return GetPreferredRasterScale(transform_scales); } diff --git a/chromium/cc/layers/layer_impl.h b/chromium/cc/layers/layer_impl.h index 6af8b62c8ba..66c0b8015c8 100644 --- a/chromium/cc/layers/layer_impl.h +++ b/chromium/cc/layers/layer_impl.h @@ -163,9 +163,12 @@ class CC_EXPORT LayerImpl { void SetBackgroundColor(SkColor background_color); SkColor background_color() const { return background_color_; } void SetSafeOpaqueBackgroundColor(SkColor background_color); - // If contents_opaque(), return an opaque color else return a - // non-opaque color. Tries to return background_color(), if possible. - SkColor SafeOpaqueBackgroundColor() const; + SkColor safe_opaque_background_color() const { + // Layer::SafeOpaqueBackgroundColor() should ensure this. + DCHECK_EQ(contents_opaque(), + SkColorGetA(safe_opaque_background_color_) == SK_AlphaOPAQUE); + return safe_opaque_background_color_; + } // See Layer::SetContentsOpaque() and SetContentsOpaqueForText() for the // relationship between the two flags. diff --git a/chromium/cc/layers/layer_impl_unittest.cc b/chromium/cc/layers/layer_impl_unittest.cc index a4924c9f3ba..7ef644a4208 100644 --- a/chromium/cc/layers/layer_impl_unittest.cc +++ b/chromium/cc/layers/layer_impl_unittest.cc @@ -17,7 +17,6 @@ #include "cc/trees/tree_synchronizer.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/skia/include/effects/SkBlurImageFilter.h" namespace cc { namespace { diff --git a/chromium/cc/layers/layer_unittest.cc b/chromium/cc/layers/layer_unittest.cc index 2b7d4d69605..9770b8c6873 100644 --- a/chromium/cc/layers/layer_unittest.cc +++ b/chromium/cc/layers/layer_unittest.cc @@ -9,11 +9,10 @@ #include <utility> #include "base/bind.h" -#include "base/stl_util.h" +#include "base/containers/contains.h" #include "base/threading/thread_task_runner_handle.h" #include "cc/animation/animation_host.h" #include "cc/animation/animation_id_provider.h" -#include "cc/animation/keyframed_animation_curve.h" #include "cc/base/math_util.h" #include "cc/layers/layer_impl.h" #include "cc/layers/picture_layer.h" @@ -38,6 +37,7 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/skia/include/core/SkColor.h" +#include "ui/gfx/animation/keyframe/keyframed_animation_curve.h" #include "ui/gfx/geometry/point3_f.h" #include "ui/gfx/geometry/point_f.h" #include "ui/gfx/geometry/scroll_offset.h" @@ -1719,7 +1719,7 @@ TEST_F(LayerTest, UpdatingClipRect) { // Setting a filter that moves pixels. FilterOperations move_pixel_filters; move_pixel_filters.Append( - FilterOperation::CreateBlurFilter(2, SkBlurImageFilter::kClamp_TileMode)); + FilterOperation::CreateBlurFilter(2, SkTileMode::kClamp)); ASSERT_TRUE(move_pixel_filters.HasFilterThatMovesPixels()); clipped_3->SetFilters(move_pixel_filters); diff --git a/chromium/cc/layers/mirror_layer_impl.cc b/chromium/cc/layers/mirror_layer_impl.cc index b76d41508e9..2343eeb56a9 100644 --- a/chromium/cc/layers/mirror_layer_impl.cc +++ b/chromium/cc/layers/mirror_layer_impl.cc @@ -47,7 +47,7 @@ void MirrorLayerImpl::AppendQuads(viz::CompositorRenderPass* render_pass, AppendDebugBorderQuad(render_pass, content_rect, shared_quad_state, append_quads_data); - viz::ResourceId mask_resource_id = 0; + viz::ResourceId mask_resource_id = viz::kInvalidResourceId; gfx::RectF mask_uv_rect; gfx::Size mask_texture_size; diff --git a/chromium/cc/layers/painted_scrollbar_layer.cc b/chromium/cc/layers/painted_scrollbar_layer.cc index a7348df68a9..3e0b802ba84 100644 --- a/chromium/cc/layers/painted_scrollbar_layer.cc +++ b/chromium/cc/layers/painted_scrollbar_layer.cc @@ -14,6 +14,7 @@ #include "cc/trees/draw_property_utils.h" #include "cc/trees/layer_tree_host.h" #include "third_party/skia/include/core/SkBitmap.h" +#include "ui/gfx/transform_util.h" namespace cc { @@ -85,7 +86,7 @@ void PaintedScrollbarLayer::PushPropertiesTo(LayerImpl* layer) { else scrollbar_layer->set_thumb_ui_resource_id(0); - scrollbar_layer->set_scrollbar_painted_opacity(painted_opacity_); + scrollbar_layer->SetScrollbarPaintedOpacity(painted_opacity_); scrollbar_layer->set_is_overlay_scrollbar(is_overlay_); } @@ -141,7 +142,7 @@ bool PaintedScrollbarLayer::UpdateInternalContentScale() { transform = draw_property_utils::ScreenSpaceTransform( this, layer_tree_host()->property_trees()->transform_tree); - gfx::Vector2dF transform_scales = MathUtil::ComputeTransform2dScaleComponents( + gfx::Vector2dF transform_scales = gfx::ComputeTransform2dScaleComponents( transform, layer_tree_host()->device_scale_factor()); float scale = std::max(transform_scales.x(), transform_scales.y()); // Clamp minimum scale to 1 to avoid too low scale during scale animation. diff --git a/chromium/cc/layers/painted_scrollbar_layer_impl.cc b/chromium/cc/layers/painted_scrollbar_layer_impl.cc index 0eda954cd9e..5ccb0727ab8 100644 --- a/chromium/cc/layers/painted_scrollbar_layer_impl.cc +++ b/chromium/cc/layers/painted_scrollbar_layer_impl.cc @@ -77,7 +77,7 @@ void PaintedScrollbarLayerImpl::PushPropertiesTo(LayerImpl* layer) { scrollbar_layer->set_track_ui_resource_id(track_ui_resource_id_); scrollbar_layer->set_thumb_ui_resource_id(thumb_ui_resource_id_); - scrollbar_layer->set_scrollbar_painted_opacity(painted_opacity_); + scrollbar_layer->SetScrollbarPaintedOpacity(painted_opacity_); } float PaintedScrollbarLayerImpl::OverlayScrollbarOpacity() const { @@ -277,6 +277,13 @@ void PaintedScrollbarLayerImpl::SetTrackRect(gfx::Rect track_rect) { NoteLayerPropertyChanged(); } +void PaintedScrollbarLayerImpl::SetScrollbarPaintedOpacity(float opacity) { + if (painted_opacity_ == opacity) + return; + painted_opacity_ = opacity; + NoteLayerPropertyChanged(); +} + float PaintedScrollbarLayerImpl::TrackLength() const { if (orientation() == ScrollbarOrientation::VERTICAL) return track_rect_.height() + vertical_adjust(); diff --git a/chromium/cc/layers/painted_scrollbar_layer_impl.h b/chromium/cc/layers/painted_scrollbar_layer_impl.h index 4e72b0112ab..9e7859264d5 100644 --- a/chromium/cc/layers/painted_scrollbar_layer_impl.h +++ b/chromium/cc/layers/painted_scrollbar_layer_impl.h @@ -45,6 +45,7 @@ class CC_EXPORT PaintedScrollbarLayerImpl : public ScrollbarLayerImplBase { void SetThumbThickness(int thumb_thickness); void SetThumbLength(int thumb_length); void SetTrackRect(gfx::Rect track_rect); + void SetScrollbarPaintedOpacity(float opacity); void set_track_ui_resource_id(UIResourceId uid) { track_ui_resource_id_ = uid; @@ -52,10 +53,6 @@ class CC_EXPORT PaintedScrollbarLayerImpl : public ScrollbarLayerImplBase { void set_thumb_ui_resource_id(UIResourceId uid) { thumb_ui_resource_id_ = uid; } - - void set_scrollbar_painted_opacity(float opacity) { - painted_opacity_ = opacity; - } float OverlayScrollbarOpacity() const override; void set_internal_contents_scale_and_bounds(float content_scale, diff --git a/chromium/cc/layers/painted_scrollbar_layer_impl_unittest.cc b/chromium/cc/layers/painted_scrollbar_layer_impl_unittest.cc index 11f47b40e2b..1669534136f 100644 --- a/chromium/cc/layers/painted_scrollbar_layer_impl_unittest.cc +++ b/chromium/cc/layers/painted_scrollbar_layer_impl_unittest.cc @@ -55,7 +55,7 @@ TEST(PaintedScrollbarLayerImplTest, Occlusion) { scrollbar_layer_impl->SetScrollLayerLength(200.f); scrollbar_layer_impl->set_track_ui_resource_id(track_uid); scrollbar_layer_impl->set_thumb_ui_resource_id(thumb_uid); - scrollbar_layer_impl->set_scrollbar_painted_opacity(painted_opacity_); + scrollbar_layer_impl->SetScrollbarPaintedOpacity(painted_opacity_); CopyProperties(impl.root_layer(), scrollbar_layer_impl); impl.CalcDrawProps(viewport_size); @@ -134,5 +134,16 @@ TEST(PaintedScrollbarLayerImplTest, Occlusion) { } } +TEST(PaintedScrollbarLayerImplTest, PaintedOpacityChangesInvalidate) { + LayerTreeImplTestBase impl; + ScrollbarOrientation orientation = ScrollbarOrientation::VERTICAL; + PaintedScrollbarLayerImpl* scrollbar_layer_impl = + impl.AddLayer<PaintedScrollbarLayerImpl>(orientation, false, false); + EXPECT_FALSE( + scrollbar_layer_impl->LayerPropertyChangedNotFromPropertyTrees()); + scrollbar_layer_impl->SetScrollbarPaintedOpacity(0.3f); + EXPECT_TRUE(scrollbar_layer_impl->LayerPropertyChangedNotFromPropertyTrees()); +} + } // namespace } // namespace cc diff --git a/chromium/cc/layers/picture_layer_impl.cc b/chromium/cc/layers/picture_layer_impl.cc index a89ec6638fe..e49acdc47ec 100644 --- a/chromium/cc/layers/picture_layer_impl.cc +++ b/chromium/cc/layers/picture_layer_impl.cc @@ -66,6 +66,15 @@ const float kMaxIdealContentsScale = 10000.f; // the native scale. const float kMinScaleRatioForWillChangeTransform = 0.25f; +// Used to avoid raster scale adjustment during a transform animation by +// using the maximum animation scale, but sometimes the maximum animation scale +// can't be accurately calculated (e.g. with nested scale transforms). We'll +// adjust raster scale if it is not affected by invalid scale and is smaller +// than the ideal scale divided by this ratio. The situation is rare. +// See PropertyTrees::MaximumAnimationToScreenScale() and +// AnimationAffectedByInvalidScale(). +const float kRatioToAdjustRasterScaleForTransformAnimation = 1.5f; + // Intersect rects which may have right() and bottom() that overflow integer // boundaries. This code is similar to gfx::Rect::Intersect with the exception // that the types are promoted to int64_t when there is a chance of overflow. @@ -512,7 +521,7 @@ void PictureLayerImpl::AppendQuads(viz::CompositorRenderPass* render_pass, if (!has_draw_quad) { // Checkerboard. - SkColor color = SafeOpaqueBackgroundColor(); + SkColor color = safe_opaque_background_color(); if (ShowDebugBorders(DebugBorderType::LAYER)) { // Fill the whole tile with the missing tile color. color = DebugColors::DefaultCheckerboardColor(); @@ -582,6 +591,7 @@ void PictureLayerImpl::AppendQuads(viz::CompositorRenderPass* render_pass, // better scheme would be to maintain a tighter visible_layer_rect for the // finer tilings. CleanUpTilingsOnActiveLayer(last_append_quads_tilings_); + SanityCheckTilingState(); } bool PictureLayerImpl::UpdateTiles() { @@ -658,6 +668,8 @@ bool PictureLayerImpl::UpdateTiles() { viewport_rect_for_tile_priority_in_content_space_, ideal_contents_scale_, current_frame_time_in_seconds, occlusion_in_content_space, can_require_tiles_for_activation); + DCHECK_GT(tilings_->num_tilings(), 0u); + SanityCheckTilingState(); return updated; } @@ -1037,7 +1049,7 @@ void PictureLayerImpl::GetContentsResourceId( gfx::SizeF* resource_uv_size) const { // We need contents resource for backdrop filter masks only. if (!is_backdrop_filter_mask()) { - *resource_id = 0; + *resource_id = viz::kInvalidResourceId; return; } @@ -1055,7 +1067,7 @@ void PictureLayerImpl::GetContentsResourceId( // Mask resource not ready yet. if (!iter || !*iter) { - *resource_id = 0; + *resource_id = viz::kInvalidResourceId; return; } @@ -1067,7 +1079,7 @@ void PictureLayerImpl::GetContentsResourceId( const TileDrawInfo& draw_info = iter->draw_info(); if (!draw_info.IsReadyToDraw() || draw_info.mode() != TileDrawInfo::RESOURCE_MODE) { - *resource_id = 0; + *resource_id = viz::kInvalidResourceId; return; } @@ -1349,14 +1361,31 @@ bool PictureLayerImpl::ShouldAdjustRasterScale() const { if (raster_contents_scale_ < MinimumContentsScale()) return true; - // Don't change the raster scale if any of the following are true: - // - We have an animating transform. - // - The raster scale is already ideal. - if (draw_properties().screen_space_transform_is_animating || - raster_source_scale_ == ideal_source_scale_) { + // Avoid frequent raster scale changes if we have an animating transform. + if (draw_properties().screen_space_transform_is_animating) { + // Except when the device viewport rect has changed because the raster scale + // may depend on the rect. + if (layer_tree_impl()->device_viewport_rect_changed()) + return true; + // Or when the raster scale is not affected by invalid scale and is too + // small compared to the ideal scale. + if (ideal_contents_scale_ > + raster_contents_scale_ * + kRatioToAdjustRasterScaleForTransformAnimation) { + auto* property_trees = layer_tree_impl()->property_trees(); + int transform_id = transform_tree_index(); + if (property_trees->AnimationScaleCacheIsInvalid(transform_id) || + !property_trees->AnimationAffectedByInvalidScale(transform_id)) { + return true; + } + } return false; } + // Don't change the raster scale if the raster scale is already ideal. + if (raster_source_scale_ == ideal_source_scale_) + return false; + // Don't update will-change: transform layers if the raster contents scale is // bigger than the minimum scale. if (HasWillChangeTransformHint() && @@ -1493,38 +1522,34 @@ void PictureLayerImpl::AdjustRasterScaleForTransformAnimation( float preserved_raster_contents_scale) { DCHECK(draw_properties().screen_space_transform_is_animating); - CombinedAnimationScale animation_scales = - layer_tree_impl()->property_trees()->GetAnimationScales( - transform_tree_index(), layer_tree_impl()); - float maximum_scale = animation_scales.maximum_animation_scale; - float starting_scale = animation_scales.starting_animation_scale; - // Adjust raster scale only if the animation scale is known. - if (maximum_scale == kNotScaled && starting_scale == kNotScaled) { - // Use at least the native scale if the animation scale is unknown. - raster_contents_scale_ = std::max(raster_contents_scale_, - ideal_page_scale_ * ideal_device_scale_); - } else { - // We rasterize at the maximum scale that will occur during the animation. - raster_contents_scale_ = std::max(maximum_scale, starting_scale); - } - DCHECK_NE(raster_contents_scale_, kNotScaled); - - // We will cap the adjusted scale with the viewport area, which is impossible - // if the viewport is empty. - gfx::Size viewport = layer_tree_impl()->GetDeviceViewport().size(); - if (viewport.IsEmpty()) - return; + float maximum_animation_scale = + layer_tree_impl()->property_trees()->MaximumAnimationToScreenScale( + transform_tree_index()); + raster_contents_scale_ = + std::max(raster_contents_scale_, maximum_animation_scale); // However we want to avoid excessive memory use. Choose a scale at which this // layer's rastered content is not larger than the viewport. - float max_viewport_dimension = std::max(viewport.width(), viewport.height()); + gfx::Size viewport = layer_tree_impl()->GetDeviceViewport().size(); + // To avoid too small scale in a small viewport. + constexpr int kMinViewportDimension = 500; + float max_viewport_dimension = + std::max({viewport.width(), viewport.height(), kMinViewportDimension}); DCHECK(max_viewport_dimension); // Use square to compensate for viewports with different aspect ratios. float squared_viewport_area = max_viewport_dimension * max_viewport_dimension; - gfx::Size bounds_at_maximum_scale = - gfx::ScaleToCeiledSize(raster_source_->GetSize(), raster_contents_scale_); - float maximum_area = static_cast<float>(bounds_at_maximum_scale.width()) * - bounds_at_maximum_scale.height(); + + gfx::SizeF raster_source_size(raster_source_->GetSize()); + // Clamp raster_source_size by max_viewport_dimension to avoid too small + // scale for huge layers for which the far from viewport area won't be + // rasterized and out of viewport area is rasterized in low priority. + gfx::SizeF max_visible_bounds( + std::min(raster_source_size.width(), max_viewport_dimension), + std::min(raster_source_size.height(), max_viewport_dimension)); + gfx::SizeF max_visible_bounds_at_max_scale = + gfx::ScaleSize(max_visible_bounds, raster_contents_scale_); + float maximum_area = max_visible_bounds_at_max_scale.width() * + max_visible_bounds_at_max_scale.height(); // Clamp the scale to make the rastered content not larger than the viewport. if (UNLIKELY(maximum_area > squared_viewport_area)) raster_contents_scale_ /= std::sqrt(maximum_area / squared_viewport_area); @@ -1563,8 +1588,6 @@ void PictureLayerImpl::CleanUpTilingsOnActiveLayer( tilings_->CleanUpTilings(min_acceptable_high_res_scale, max_acceptable_high_res_scale, used_tilings, twin_set); - DCHECK_GT(tilings_->num_tilings(), 0u); - SanityCheckTilingState(); } float PictureLayerImpl::MinimumRasterContentsScaleForWillChangeTransform() diff --git a/chromium/cc/layers/picture_layer_impl.h b/chromium/cc/layers/picture_layer_impl.h index a79a2441c89..e526ef44341 100644 --- a/chromium/cc/layers/picture_layer_impl.h +++ b/chromium/cc/layers/picture_layer_impl.h @@ -167,6 +167,10 @@ class CC_EXPORT PictureLayerImpl ideal_contents_scale_ = raster_contents_scale_ = scale; } + void AddLastAppendQuadsTilingForTesting(PictureLayerTiling* tiling) { + last_append_quads_tilings_.push_back(tiling); + } + protected: PictureLayerImpl(LayerTreeImpl* tree_impl, int id); PictureLayerTiling* AddTiling(const gfx::AxisTransform2d& contents_transform); diff --git a/chromium/cc/layers/picture_layer_impl_unittest.cc b/chromium/cc/layers/picture_layer_impl_unittest.cc index c9e135ee659..61ac7452a91 100644 --- a/chromium/cc/layers/picture_layer_impl_unittest.cc +++ b/chromium/cc/layers/picture_layer_impl_unittest.cc @@ -151,10 +151,7 @@ class PictureLayerImplTest : public TestLayerTreeHostBase { void SetupDrawProperties(FakePictureLayerImpl* layer, float ideal_contents_scale, float device_scale_factor, - float page_scale_factor, - float maximum_animation_contents_scale, - float starting_animation_contents_scale, - bool animating_transform_to_screen) { + float page_scale_factor) { layer->layer_tree_impl()->SetDeviceScaleFactor(device_scale_factor); host_impl()->active_tree()->SetPageScaleOnActiveTree(page_scale_factor); @@ -164,25 +161,14 @@ class PictureLayerImplTest : public TestLayerTreeHostBase { layer->draw_properties().target_space_transform = scale_transform; layer->set_contributes_to_drawn_render_surface(true); DCHECK_EQ(layer->GetIdealContentsScale(), ideal_contents_scale); - layer->layer_tree_impl()->property_trees()->SetAnimationScalesForTesting( - layer->transform_tree_index(), maximum_animation_contents_scale, - starting_animation_contents_scale); - layer->draw_properties().screen_space_transform_is_animating = - animating_transform_to_screen; } - void SetupDrawPropertiesAndUpdateTiles( - FakePictureLayerImpl* layer, - float ideal_contents_scale, - float device_scale_factor, - float page_scale_factor, - float maximum_animation_contents_scale, - float starting_animation_contents_scale, - bool animating_transform_to_screen) { + void SetupDrawPropertiesAndUpdateTiles(FakePictureLayerImpl* layer, + float ideal_contents_scale, + float device_scale_factor, + float page_scale_factor) { SetupDrawProperties(layer, ideal_contents_scale, device_scale_factor, - page_scale_factor, maximum_animation_contents_scale, - starting_animation_contents_scale, - animating_transform_to_screen); + page_scale_factor); layer->UpdateTiles(); } @@ -200,21 +186,48 @@ class PictureLayerImplTest : public TestLayerTreeHostBase { } } + void SetMaximumAnimationToScreenScale(FakePictureLayerImpl* layer, + float maximum_animation_to_screen_scale, + bool affected_by_invalid_scale) { + layer->layer_tree_impl() + ->property_trees() + ->SetMaximumAnimationToScreenScaleForTesting( + layer->transform_tree_index(), maximum_animation_to_screen_scale, + affected_by_invalid_scale); + layer->draw_properties().screen_space_transform_is_animating = true; + } + void SetContentsScaleOnBothLayers(float contents_scale, float device_scale_factor, - float page_scale_factor, - float maximum_animation_contents_scale, - float starting_animation_contents_scale, - bool animating_transform) { - SetupDrawPropertiesAndUpdateTiles( - pending_layer(), contents_scale, device_scale_factor, page_scale_factor, - maximum_animation_contents_scale, starting_animation_contents_scale, - animating_transform); - - SetupDrawPropertiesAndUpdateTiles( - active_layer(), contents_scale, device_scale_factor, page_scale_factor, - maximum_animation_contents_scale, starting_animation_contents_scale, - animating_transform); + float page_scale_factor) { + // Sets arbitrary animation scale to ensure it's not used in any way. + constexpr float kArbitraryScale = 12345.0f; + SetMaximumAnimationToScreenScale(pending_layer(), kArbitraryScale, false); + pending_layer()->draw_properties().screen_space_transform_is_animating = + false; + SetupDrawPropertiesAndUpdateTiles(pending_layer(), contents_scale, + device_scale_factor, page_scale_factor); + SetMaximumAnimationToScreenScale(active_layer(), kArbitraryScale, false); + active_layer()->draw_properties().screen_space_transform_is_animating = + false; + SetupDrawPropertiesAndUpdateTiles(active_layer(), contents_scale, + device_scale_factor, page_scale_factor); + } + + void SetContentsAndAnimationScalesOnBothLayers( + float contents_scale, + float device_scale_factor, + float page_scale_factor, + float maximum_animation_scale, + bool affected_by_invalid_scale) { + SetMaximumAnimationToScreenScale(pending_layer(), maximum_animation_scale, + affected_by_invalid_scale); + SetupDrawPropertiesAndUpdateTiles(pending_layer(), contents_scale, + device_scale_factor, page_scale_factor); + SetMaximumAnimationToScreenScale(active_layer(), maximum_animation_scale, + affected_by_invalid_scale); + SetupDrawPropertiesAndUpdateTiles(active_layer(), contents_scale, + device_scale_factor, page_scale_factor); } void ResetTilingsAndRasterScales() { @@ -319,8 +332,7 @@ TEST_F(LegacySWPictureLayerImplTest, ExternalViewportRectForPrioritizingTiles) { gfx::Size layer_bounds(400, 400); SetupDefaultTrees(layer_bounds); - SetupDrawPropertiesAndUpdateTiles(active_layer(), 1.f, 1.f, 1.f, 1.f, 0.f, - false); + SetupDrawPropertiesAndUpdateTiles(active_layer(), 1.f, 1.f, 1.f); host_impl()->AdvanceToNextFrame(base::TimeDelta::FromMilliseconds(200)); @@ -372,8 +384,7 @@ TEST_F(LegacySWPictureLayerImplTest, ViewportRectForTilePriorityIsCached) { gfx::Size layer_bounds(400, 400); SetupDefaultTrees(layer_bounds); - SetupDrawPropertiesAndUpdateTiles(active_layer(), 1.f, 1.f, 1.f, 1.f, 0.f, - false); + SetupDrawPropertiesAndUpdateTiles(active_layer(), 1.f, 1.f, 1.f); host_impl()->AdvanceToNextFrame(base::TimeDelta::FromMilliseconds(200)); @@ -518,12 +529,9 @@ TEST_F(LegacySWPictureLayerImplTest, UpdateTilesCreatesTilings) { EXPECT_EQ(0u, active_layer()->num_tilings()); SetupDrawPropertiesAndUpdateTiles(active_layer(), - 6.f, // ideal contents scale - 3.f, // device scale - 2.f, // page scale - 1.f, // maximum animation scale - 0.f, // starting animation scale - false); + 6.f, // ideal contents scale + 3.f, // device scale + 2.f); // page scale ASSERT_EQ(2u, active_layer()->tilings()->num_tilings()); EXPECT_FLOAT_EQ( 6.f, active_layer()->tilings()->tiling_at(0)->contents_scale_key()); @@ -533,12 +541,9 @@ TEST_F(LegacySWPictureLayerImplTest, UpdateTilesCreatesTilings) { // If we change the page scale factor, then we should get new tilings. SetupDrawPropertiesAndUpdateTiles(active_layer(), - 6.6f, // ideal contents scale - 3.f, // device scale - 2.2f, // page scale - 1.f, // maximum animation scale - 0.f, // starting animation scale - false); + 6.6f, // ideal contents scale + 3.f, // device scale + 2.2f); // page scale ASSERT_EQ(4u, active_layer()->tilings()->num_tilings()); EXPECT_FLOAT_EQ( 6.6f, active_layer()->tilings()->tiling_at(0)->contents_scale_key()); @@ -550,10 +555,7 @@ TEST_F(LegacySWPictureLayerImplTest, UpdateTilesCreatesTilings) { SetupDrawPropertiesAndUpdateTiles(active_layer(), 7.26f, // ideal contents scale 3.3f, // device scale - 2.2f, // page scale - 1.f, // maximum animation scale - 0.f, // starting animation scale - false); + 2.2f); // page scale ASSERT_EQ(6u, active_layer()->tilings()->num_tilings()); EXPECT_FLOAT_EQ( 7.26f, active_layer()->tilings()->tiling_at(0)->contents_scale_key()); @@ -566,10 +568,7 @@ TEST_F(LegacySWPictureLayerImplTest, UpdateTilesCreatesTilings) { SetupDrawPropertiesAndUpdateTiles(active_layer(), 7.26f, // ideal contents scale 2.2f, // device scale - 3.3f, // page scale - 1.f, // maximum animation scale - 0.f, // starting animation scale - false); + 3.3f); // page scale ASSERT_EQ(6u, active_layer()->tilings()->num_tilings()); EXPECT_FLOAT_EQ( 7.26f, active_layer()->tilings()->tiling_at(0)->contents_scale_key()); @@ -590,24 +589,18 @@ TEST_F(LegacySWPictureLayerImplTest, PendingLayerOnlyHasHighResTiling) { EXPECT_EQ(0u, pending_layer()->tilings()->num_tilings()); SetupDrawPropertiesAndUpdateTiles(pending_layer(), - 6.f, // ideal contents scale - 3.f, // device scale - 2.f, // page scale - 1.f, // maximum animation scale - 0.f, // starting animation scale - false); + 6.f, // ideal contents scale + 3.f, // device scale + 2.f); // page scale ASSERT_EQ(1u, pending_layer()->tilings()->num_tilings()); EXPECT_FLOAT_EQ( 6.f, pending_layer()->tilings()->tiling_at(0)->contents_scale_key()); // If we change the page scale factor, then we should get new tilings. SetupDrawPropertiesAndUpdateTiles(pending_layer(), - 6.6f, // ideal contents scale - 3.f, // device scale - 2.2f, // page scale - 1.f, // maximum animation scale - 0.f, // starting animation scale - false); + 6.6f, // ideal contents scale + 3.f, // device scale + 2.2f); // page scale ASSERT_EQ(1u, pending_layer()->tilings()->num_tilings()); EXPECT_FLOAT_EQ( 6.6f, pending_layer()->tilings()->tiling_at(0)->contents_scale_key()); @@ -616,10 +609,7 @@ TEST_F(LegacySWPictureLayerImplTest, PendingLayerOnlyHasHighResTiling) { SetupDrawPropertiesAndUpdateTiles(pending_layer(), 7.26f, // ideal contents scale 3.3f, // device scale - 2.2f, // page scale - 1.f, // maximum animation scale - 0.f, // starting animation scale - false); + 2.2f); // page scale ASSERT_EQ(1u, pending_layer()->tilings()->num_tilings()); EXPECT_FLOAT_EQ( 7.26f, pending_layer()->tilings()->tiling_at(0)->contents_scale_key()); @@ -629,10 +619,7 @@ TEST_F(LegacySWPictureLayerImplTest, PendingLayerOnlyHasHighResTiling) { SetupDrawPropertiesAndUpdateTiles(pending_layer(), 7.26f, // ideal contents scale 2.2f, // device scale - 3.3f, // page scale - 1.f, // maximum animation scale - 0.f, // starting animation scale - false); + 3.3f); // page scale ASSERT_EQ(1u, pending_layer()->tilings()->num_tilings()); EXPECT_FLOAT_EQ( 7.26f, pending_layer()->tilings()->tiling_at(0)->contents_scale_key()); @@ -701,7 +688,7 @@ TEST_F(LegacySWPictureLayerImplTest, ZoomOutCrash) { SetupDefaultTrees(layer_bounds); ResetTilingsAndRasterScales(); EXPECT_EQ(0u, active_layer()->tilings()->num_tilings()); - SetContentsScaleOnBothLayers(32.0f, 1.0f, 32.0f, 1.0f, 0.f, false); + SetContentsScaleOnBothLayers(32.0f, 1.0f, 32.0f); EXPECT_EQ(32.f, active_layer()->HighResTiling()->contents_scale_key()); // Since this test simulates a pinch it needs an input handler. @@ -710,8 +697,8 @@ TEST_F(LegacySWPictureLayerImplTest, ZoomOutCrash) { InputHandler::Create(static_cast<CompositorDelegateForInput&>(*host_impl())); host_impl()->GetInputHandler().PinchGestureBegin(); - SetContentsScaleOnBothLayers(1.0f, 1.0f, 1.0f, 1.0f, 0.f, false); - SetContentsScaleOnBothLayers(1.0f, 1.0f, 1.0f, 1.0f, 0.f, false); + SetContentsScaleOnBothLayers(1.0f, 1.0f, 1.0f); + SetContentsScaleOnBothLayers(1.0f, 1.0f, 1.0f); EXPECT_EQ(active_layer()->tilings()->NumHighResTilings(), 1); } @@ -732,7 +719,7 @@ TEST_F(LegacySWPictureLayerImplTest, ScaledBoundsOverflowInt) { EXPECT_GT(static_cast<float>(layer_bounds.width()) * scale, static_cast<float>(std::numeric_limits<int>::max())); - SetContentsScaleOnBothLayers(scale, 1.0f, scale, 1.0f, 0.f, false); + SetContentsScaleOnBothLayers(scale, 1.0f, scale); float adjusted_scale = active_layer()->HighResTiling()->contents_scale_key(); EXPECT_LT(adjusted_scale, scale); @@ -753,7 +740,7 @@ TEST_F(LegacySWPictureLayerImplTest, PinchGestureTilings) { SetupDefaultTrees(layer_bounds); ResetTilingsAndRasterScales(); - SetContentsScaleOnBothLayers(2.f, 1.0f, 2.f, 1.0f, 0.f, false); + SetContentsScaleOnBothLayers(2.f, 1.0f, 2.f); ASSERT_EQ(active_layer()->num_tilings(), 2u); ASSERT_EQ(pending_layer()->num_tilings(), 1u); EXPECT_EQ(active_layer()->tilings()->tiling_at(0)->contents_scale_key(), 2.f); @@ -776,7 +763,7 @@ TEST_F(LegacySWPictureLayerImplTest, PinchGestureTilings) { // Zoom out by a small amount. We should create a tiling at half // the scale (2/kMaxScaleRatioDuringPinch). - SetContentsScaleOnBothLayers(1.8f, 1.0f, 1.8f, 1.0f, 0.f, false); + SetContentsScaleOnBothLayers(1.8f, 1.0f, 1.8f); ASSERT_EQ(3u, active_layer()->tilings()->num_tilings()); EXPECT_FLOAT_EQ( 2.0f, active_layer()->tilings()->tiling_at(0)->contents_scale_key()); @@ -795,14 +782,14 @@ TEST_F(LegacySWPictureLayerImplTest, PinchGestureTilings) { // Zoom out further, close to our low-res scale factor. We should // use that tiling as high-res, and not create a new tiling. SetContentsScaleOnBothLayers(low_res_factor * 2.1f, 1.0f, - low_res_factor * 2.1f, 1.0f, 0.f, false); + low_res_factor * 2.1f); ASSERT_EQ(3u, active_layer()->tilings()->num_tilings()); EXPECT_FALSE( active_layer()->tilings()->FindTilingWithResolution(LOW_RESOLUTION)); // Zoom in a lot now. Since we increase by increments of // kMaxScaleRatioDuringPinch, this will create a new tiling at 4.0. - SetContentsScaleOnBothLayers(3.8f, 1.0f, 3.8f, 1.f, 0.f, false); + SetContentsScaleOnBothLayers(3.8f, 1.0f, 3.8f); ASSERT_EQ(4u, active_layer()->tilings()->num_tilings()); EXPECT_FLOAT_EQ( 4.0f, active_layer()->tilings()->tiling_at(0)->contents_scale_key()); @@ -821,7 +808,7 @@ TEST_F(LegacySWPictureLayerImplTest, PinchGestureTilings) { // After pinch ends, set the scale to what the raster scale was updated to // (checked above). - SetContentsScaleOnBothLayers(4.0f, 1.0f, 4.0f, 1.f, 0.f, false); + SetContentsScaleOnBothLayers(4.0f, 1.0f, 4.0f); ASSERT_EQ(4u, active_layer()->tilings()->num_tilings()); EXPECT_FLOAT_EQ( 4.0f, active_layer()->tilings()->tiling_at(0)->contents_scale_key()); @@ -841,7 +828,7 @@ TEST_F(LegacySWPictureLayerImplTest, SnappedTilingDuringZoom) { EXPECT_EQ(0u, active_layer()->tilings()->num_tilings()); // Set up the high and low res tilings before pinch zoom. - SetContentsScaleOnBothLayers(0.24f, 1.0f, 0.24f, 1.0f, 0.f, false); + SetContentsScaleOnBothLayers(0.24f, 1.0f, 0.24f); ASSERT_EQ(2u, active_layer()->tilings()->num_tilings()); EXPECT_FLOAT_EQ( 0.24f, active_layer()->tilings()->tiling_at(0)->contents_scale_key()); @@ -861,7 +848,7 @@ TEST_F(LegacySWPictureLayerImplTest, SnappedTilingDuringZoom) { // Zoom out by a small amount. We should create a tiling at half // the scale (1/kMaxScaleRatioDuringPinch). - SetContentsScaleOnBothLayers(0.2f, 1.0f, 0.2f, 1.0f, 0.f, false); + SetContentsScaleOnBothLayers(0.2f, 1.0f, 0.2f); ASSERT_EQ(3u, active_layer()->tilings()->num_tilings()); EXPECT_FLOAT_EQ( 0.24f, active_layer()->tilings()->tiling_at(0)->contents_scale_key()); @@ -875,19 +862,19 @@ TEST_F(LegacySWPictureLayerImplTest, SnappedTilingDuringZoom) { // Zoom out further, close to our low-res scale factor. We should // use that tiling as high-res, and not create a new tiling. - SetContentsScaleOnBothLayers(0.1f, 1.0f, 0.1f, 1.0f, 0.f, false); + SetContentsScaleOnBothLayers(0.1f, 1.0f, 0.1f); ASSERT_EQ(3u, active_layer()->tilings()->num_tilings()); // Zoom in. 0.22(desired_scale) should be snapped to 0.24 during zoom-in // because 0.22(desired_scale) is within the ratio(1.2). - SetContentsScaleOnBothLayers(0.22f, 1.0f, 0.22f, 1.0f, 0.f, false); + SetContentsScaleOnBothLayers(0.22f, 1.0f, 0.22f); ASSERT_EQ(3u, active_layer()->tilings()->num_tilings()); EXPECT_FLOAT_EQ( 0.24f, active_layer()->tilings()->tiling_at(0)->contents_scale_key()); // Zoom in a lot. Since we move in factors of two, we should get a scale that // is a power of 2 times 0.24. - SetContentsScaleOnBothLayers(1.f, 1.0f, 1.f, 1.0f, 0.f, false); + SetContentsScaleOnBothLayers(1.f, 1.0f, 1.f); ASSERT_EQ(4u, active_layer()->tilings()->num_tilings()); EXPECT_FLOAT_EQ( 1.92f, active_layer()->tilings()->tiling_at(0)->contents_scale_key()); @@ -938,7 +925,7 @@ TEST_F(LegacySWPictureLayerImplTest, CleanUpTilings) { // Changing the ideal but not creating new tilings. scale = 1.5f; page_scale = 1.5f; - SetContentsScaleOnBothLayers(scale, 1.f, page_scale, 1.f, 0.f, false); + SetContentsScaleOnBothLayers(scale, 1.f, page_scale); EXPECT_FLOAT_EQ(2u, active_layer()->tilings()->num_tilings()); EXPECT_FLOAT_EQ( 1.f, active_layer()->tilings()->tiling_at(0)->contents_scale_key()); @@ -961,7 +948,7 @@ TEST_F(LegacySWPictureLayerImplTest, CleanUpTilings) { // Create a 1.2 scale tiling. Now we have 1.0 and 1.2 tilings. Ideal = 1.2. scale = 1.2f; page_scale = 1.2f; - SetContentsScaleOnBothLayers(1.2f, 1.f, page_scale, 1.f, 0.f, false); + SetContentsScaleOnBothLayers(1.2f, 1.f, page_scale); ASSERT_EQ(4u, active_layer()->tilings()->num_tilings()); EXPECT_FLOAT_EQ( 1.2f, active_layer()->tilings()->tiling_at(0)->contents_scale_key()); @@ -995,7 +982,7 @@ TEST_F(LegacySWPictureLayerImplTest, CleanUpTilings) { active_layer()->tilings()->tiling_at(3)->contents_scale_key()); // Now move the ideal scale to 0.5. Our target stays 1.2. - SetContentsScaleOnBothLayers(0.5f, 1.f, page_scale, 1.f, 0.f, false); + SetContentsScaleOnBothLayers(0.5f, 1.f, page_scale); // The high resolution tiling is between target and ideal, so is not // removed. The low res tiling for the old ideal=1.0 scale is removed. @@ -1011,7 +998,7 @@ TEST_F(LegacySWPictureLayerImplTest, CleanUpTilings) { active_layer()->tilings()->tiling_at(2)->contents_scale_key()); // Now move the ideal scale to 1.0. Our target stays 1.2. - SetContentsScaleOnBothLayers(1.f, 1.f, page_scale, 1.f, 0.f, false); + SetContentsScaleOnBothLayers(1.f, 1.f, page_scale); // All the tilings are between are target and the ideal, so they are not // removed. @@ -1027,8 +1014,7 @@ TEST_F(LegacySWPictureLayerImplTest, CleanUpTilings) { active_layer()->tilings()->tiling_at(2)->contents_scale_key()); // Now move the ideal scale to 1.1 on the active layer. Our target stays 1.2. - SetupDrawPropertiesAndUpdateTiles(active_layer(), 1.1f, 1.f, page_scale, 1.f, - 0.f, false); + SetupDrawPropertiesAndUpdateTiles(active_layer(), 1.1f, 1.f, page_scale); // Because the pending layer's ideal scale is still 1.0, our tilings fall // in the range [1.0,1.2] and are kept. @@ -1045,8 +1031,7 @@ TEST_F(LegacySWPictureLayerImplTest, CleanUpTilings) { // Move the ideal scale on the pending layer to 1.1 as well. Our target stays // 1.2 still. - SetupDrawPropertiesAndUpdateTiles(pending_layer(), 1.1f, 1.f, page_scale, 1.f, - 0.f, false); + SetupDrawPropertiesAndUpdateTiles(pending_layer(), 1.1f, 1.f, page_scale); // Our 1.0 tiling now falls outside the range between our ideal scale and our // target raster scale. But it is in our used tilings set, so nothing is @@ -1088,23 +1073,19 @@ TEST_F(LegacySWPictureLayerImplTest, DontAddLowResDuringAnimation) { float device_scale = 1.f; float page_scale = 1.f; float maximum_animation_scale = 1.f; - float starting_animation_scale = kNotScaled; - bool animating_transform = true; + bool affected_by_invalid_scale = false; ResetTilingsAndRasterScales(); // Animating, so don't create low res even if there isn't one already. - SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale, - maximum_animation_scale, - starting_animation_scale, animating_transform); + SetContentsAndAnimationScalesOnBothLayers(contents_scale, device_scale, + page_scale, maximum_animation_scale, + affected_by_invalid_scale); EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 1.f); EXPECT_BOTH_EQ(num_tilings(), 1u); // Stop animating, low res gets created. - animating_transform = false; - SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale, - maximum_animation_scale, - starting_animation_scale, animating_transform); + SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale); EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 1.f); EXPECT_EQ(active_layer()->LowResTiling()->contents_scale_key(), low_res_factor); @@ -1120,10 +1101,9 @@ TEST_F(LegacySWPictureLayerImplTest, DontAddLowResDuringAnimation) { contents_scale = 2.f; page_scale = 2.f; maximum_animation_scale = 2.f; - animating_transform = true; - SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale, - maximum_animation_scale, - starting_animation_scale, animating_transform); + SetContentsAndAnimationScalesOnBothLayers(contents_scale, device_scale, + page_scale, maximum_animation_scale, + affected_by_invalid_scale); EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 2.f); EXPECT_FALSE(active_layer()->LowResTiling()); EXPECT_FALSE(pending_layer()->LowResTiling()); @@ -1131,10 +1111,7 @@ TEST_F(LegacySWPictureLayerImplTest, DontAddLowResDuringAnimation) { EXPECT_EQ(1u, pending_layer()->num_tilings()); // Stop animating, new low res gets created for final page scale. - animating_transform = false; - SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale, - maximum_animation_scale, - starting_animation_scale, animating_transform); + SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale); EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 2.f); EXPECT_EQ(active_layer()->LowResTiling()->contents_scale_key(), 2.f * low_res_factor); @@ -1155,15 +1132,10 @@ TEST_F(LegacySWPictureLayerImplTest, DontAddLowResForSmallLayers) { float low_res_factor = host_impl()->settings().low_res_contents_scale_factor; float device_scale = 1.f; float page_scale = 1.f; - float maximum_animation_scale = 1.f; - float starting_animation_scale = kNotScaled; - bool animating_transform = false; // Contents exactly fit on one tile at scale 1, no low res. float contents_scale = 1.f; - SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale, - maximum_animation_scale, - starting_animation_scale, animating_transform); + SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale); EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), contents_scale); EXPECT_BOTH_EQ(num_tilings(), 1u); @@ -1171,9 +1143,7 @@ TEST_F(LegacySWPictureLayerImplTest, DontAddLowResForSmallLayers) { // Contents that are smaller than one tile, no low res. contents_scale = 0.123f; - SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale, - maximum_animation_scale, - starting_animation_scale, animating_transform); + SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale); EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), contents_scale); EXPECT_BOTH_EQ(num_tilings(), 1u); @@ -1183,9 +1153,7 @@ TEST_F(LegacySWPictureLayerImplTest, DontAddLowResForSmallLayers) { // Any content bounds that would create more than one tile will // generate a low res tiling. contents_scale = 2.5f; - SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale, - maximum_animation_scale, - starting_animation_scale, animating_transform); + SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale); EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), contents_scale); EXPECT_EQ(active_layer()->LowResTiling()->contents_scale_key(), contents_scale * low_res_factor); @@ -1206,9 +1174,8 @@ TEST_F(LegacySWPictureLayerImplTest, DontAddLowResForSmallLayers) { mask->ReleaseTileResources(); mask->RecreateTileResources(); - SetupDrawPropertiesAndUpdateTiles( - mask, contents_scale, device_scale, page_scale, maximum_animation_scale, - starting_animation_scale, animating_transform); + SetupDrawPropertiesAndUpdateTiles(mask, contents_scale, device_scale, + page_scale); EXPECT_EQ(mask->HighResTiling()->contents_scale_key(), contents_scale); EXPECT_EQ(mask->num_tilings(), 1u); } @@ -1251,7 +1218,7 @@ TEST_F(LegacySWPictureLayerImplTest, HugeBackdropFilterMasksGetScaledDown) { gfx::SizeF mask_uv_size; active_mask->GetContentsResourceId(&mask_resource_id, &mask_texture_size, &mask_uv_size); - EXPECT_NE(0u, mask_resource_id); + EXPECT_NE(viz::kInvalidResourceId, mask_resource_id); EXPECT_EQ(active_mask->bounds(), mask_texture_size); EXPECT_EQ(gfx::SizeF(1.0f, 1.0f), mask_uv_size); @@ -1260,8 +1227,7 @@ TEST_F(LegacySWPictureLayerImplTest, HugeBackdropFilterMasksGetScaledDown) { active_mask->ReleaseTileResources(); pending_mask->RecreateTileResources(); active_mask->RecreateTileResources(); - SetupDrawPropertiesAndUpdateTiles(active_mask, 1.f, 1.f, 1.f, 1.f, 0.f, - false); + SetupDrawPropertiesAndUpdateTiles(active_mask, 1.f, 1.f, 1.f); active_mask->HighResTiling()->CreateAllTilesForTesting(); EXPECT_EQ(1u, active_mask->HighResTiling()->AllTilesForTesting().size()); @@ -1292,7 +1258,7 @@ TEST_F(LegacySWPictureLayerImplTest, HugeBackdropFilterMasksGetScaledDown) { // The mask resource exists. active_mask->GetContentsResourceId(&mask_resource_id, &mask_texture_size, &mask_uv_size); - EXPECT_NE(0u, mask_resource_id); + EXPECT_NE(viz::kInvalidResourceId, mask_resource_id); gfx::Size expected_size = active_mask->bounds(); expected_size.SetToMin(gfx::Size(max_texture_size, max_texture_size)); EXPECT_EQ(expected_size, mask_texture_size); @@ -1303,11 +1269,10 @@ TEST_F(LegacySWPictureLayerImplTest, HugeBackdropFilterMasksGetScaledDown) { active_mask->ReleaseTileResources(); pending_mask->RecreateTileResources(); active_mask->RecreateTileResources(); - SetupDrawPropertiesAndUpdateTiles(active_mask, 1.f, 1.f, 1.f, 1.f, 0.f, - false); + SetupDrawPropertiesAndUpdateTiles(active_mask, 1.f, 1.f, 1.f); active_mask->HighResTiling()->CreateAllTilesForTesting(); EXPECT_EQ(1u, active_mask->HighResTiling()->AllTilesForTesting().size()); - EXPECT_NE(0u, mask_resource_id); + EXPECT_NE(viz::kInvalidResourceId, mask_resource_id); EXPECT_EQ(expected_size, mask_texture_size); // Do another activate, the same holds. @@ -1317,7 +1282,7 @@ TEST_F(LegacySWPictureLayerImplTest, HugeBackdropFilterMasksGetScaledDown) { active_layer()->GetContentsResourceId(&mask_resource_id, &mask_texture_size, &mask_uv_size); EXPECT_EQ(expected_size, mask_texture_size); - EXPECT_EQ(0u, mask_resource_id); + EXPECT_EQ(viz::kInvalidResourceId, mask_resource_id); EXPECT_EQ(gfx::SizeF(1.0f, 1.0f), mask_uv_size); // Resize even larger, so that the scale would be smaller than the minimum @@ -1379,7 +1344,7 @@ TEST_F(LegacySWPictureLayerImplTest, ScaledBackdropFilterMaskLayer) { gfx::SizeF mask_uv_size; active_mask->GetContentsResourceId(&mask_resource_id, &mask_texture_size, &mask_uv_size); - EXPECT_NE(0u, mask_resource_id); + EXPECT_NE(viz::kInvalidResourceId, mask_resource_id); gfx::Size expected_mask_texture_size = gfx::ScaleToCeiledSize(active_mask->bounds(), 1.3f); EXPECT_EQ(mask_texture_size, expected_mask_texture_size); @@ -1426,7 +1391,7 @@ TEST_F(LegacySWPictureLayerImplTest, ScaledMaskLayer) { gfx::SizeF mask_uv_size; active_mask->GetContentsResourceId(&mask_resource_id, &mask_texture_size, &mask_uv_size); - EXPECT_EQ(0u, mask_resource_id); + EXPECT_EQ(viz::kInvalidResourceId, mask_resource_id); EXPECT_EQ(gfx::Size(), mask_texture_size); EXPECT_EQ(gfx::SizeF(), mask_uv_size); } @@ -1446,12 +1411,9 @@ TEST_F(LegacySWPictureLayerImplTest, ReleaseTileResources) { // This should create new tilings. SetupDrawPropertiesAndUpdateTiles(pending_layer(), - 1.f, // ideal contents scale - 1.f, // device scale - 1.f, // page scale - 1.f, // maximum animation scale - 0.f, // starting animation_scale - false); + 1.f, // ideal contents scale + 1.f, // device scale + 1.f); // page scale EXPECT_EQ(1u, pending_layer()->tilings()->num_tilings()); } @@ -1503,8 +1465,7 @@ TEST_F(LegacySWPictureLayerImplTest, ClampTilesToMaxTileSize) { ResetLayerTreeFrameSink( FakeLayerTreeFrameSink::Create3d(std::move(gl_owned))); - SetupDrawPropertiesAndUpdateTiles(pending_layer(), 1.f, 1.f, 1.f, 1.f, 0.f, - false); + SetupDrawPropertiesAndUpdateTiles(pending_layer(), 1.f, 1.f, 1.f); ASSERT_EQ(1u, pending_layer()->tilings()->num_tilings()); pending_layer()->tilings()->tiling_at(0)->CreateAllTilesForTesting(); @@ -1538,8 +1499,7 @@ TEST_F(LegacySWPictureLayerImplTest, ClampSingleTileToToMaxTileSize) { ResetLayerTreeFrameSink( FakeLayerTreeFrameSink::Create3d(std::move(gl_owned))); - SetupDrawPropertiesAndUpdateTiles(active_layer(), 1.f, 1.f, 1.f, 1.f, 0.f, - false); + SetupDrawPropertiesAndUpdateTiles(active_layer(), 1.f, 1.f, 1.f); ASSERT_LE(1u, active_layer()->tilings()->num_tilings()); active_layer()->tilings()->tiling_at(0)->CreateAllTilesForTesting(); @@ -2100,10 +2060,8 @@ TEST_F(LegacySWPictureLayerImplTest, // Due to layer scale throttling, the raster contents scale is changed to 1, // while the ideal is still 2. - SetupDrawPropertiesAndUpdateTiles(active_layer(), 1.f, 1.f, 1.f, 1.f, 0.f, - false); - SetupDrawPropertiesAndUpdateTiles(active_layer(), 2.f, 1.f, 1.f, 1.f, 0.f, - false); + SetupDrawPropertiesAndUpdateTiles(active_layer(), 1.f, 1.f, 1.f); + SetupDrawPropertiesAndUpdateTiles(active_layer(), 2.f, 1.f, 1.f); EXPECT_EQ(1.f, active_layer()->HighResTiling()->contents_scale_key()); EXPECT_EQ(1.f, active_layer()->raster_contents_scale()); @@ -2523,26 +2481,78 @@ TEST_F(LegacySWPictureLayerImplTest, HighResCreatedWhenBoundsShrink) { FakeRasterSource::CreateFilled(gfx::Size(10, 10)); SetupPendingTree(pending_raster_source); - // Sanity checks. EXPECT_EQ(1u, pending_layer()->tilings()->num_tilings()); - EXPECT_TRUE(pending_layer()->tilings()->FindTilingWithScaleKey(0.5f)); + EXPECT_EQ(1, pending_layer()->tilings()->NumHighResTilings()); + auto* high_res = pending_layer()->tilings()->FindTilingWithScaleKey(0.5f); + ASSERT_TRUE(high_res); + EXPECT_EQ(HIGH_RESOLUTION, high_res->resolution()); ActivateTree(); + EXPECT_EQ(1u, active_layer()->tilings()->num_tilings()); + EXPECT_EQ(1, active_layer()->tilings()->NumHighResTilings()); + high_res = active_layer()->tilings()->FindTilingWithScaleKey(0.5f); + ASSERT_TRUE(high_res); + EXPECT_EQ(HIGH_RESOLUTION, high_res->resolution()); + // Now, set the bounds to be 1x1, so that minimum contents scale becomes 1. pending_raster_source = FakeRasterSource::CreateFilled(gfx::Size(1, 1)); SetupPendingTree(pending_raster_source); - // Another sanity check. EXPECT_EQ(1.f, pending_layer()->MinimumContentsScale()); // Since the MinContentsScale is 1, the 0.5 tiling should have been replaced // by a 1.0 tiling during the UDP in SetupPendingTree. EXPECT_EQ(1u, pending_layer()->tilings()->num_tilings()); - PictureLayerTiling* tiling = - pending_layer()->tilings()->FindTilingWithScaleKey(1.0f); - ASSERT_TRUE(tiling); - EXPECT_EQ(HIGH_RESOLUTION, tiling->resolution()); + high_res = pending_layer()->tilings()->FindTilingWithScaleKey(1.0f); + ASSERT_TRUE(high_res); + EXPECT_EQ(HIGH_RESOLUTION, high_res->resolution()); + + ActivateTree(); + EXPECT_EQ(1u, active_layer()->tilings()->num_tilings()); + high_res = active_layer()->tilings()->FindTilingWithScaleKey(1.0f); + ASSERT_TRUE(high_res); + EXPECT_EQ(HIGH_RESOLUTION, high_res->resolution()); +} + +// Tests when directly committing to the active tree, a small raster source +// makes the minimum scale bigger than the the previous high res tiling scale, +// causing the high res tiling (if not previously used for quads) to be removed. +// See crbug.com/1160003. +TEST_F(LegacySWPictureLayerImplTest, + HighResCreatedWhenBoundsShrinkOnActiveLayerWithUsedNonIdealScaleTiling) { + // Put 0.5 as high res. + SetInitialDeviceScaleFactor(0.5f); + + SetupPendingTree(FakeRasterSource::CreateFilled(gfx::Size(10, 10))); + ActivateTree(); + active_layer() + ->AddTiling(gfx::AxisTransform2d(1.0f, gfx::Vector2dF())) + ->set_resolution(NON_IDEAL_RESOLUTION); + + EXPECT_EQ(2u, active_layer()->tilings()->num_tilings()); + EXPECT_EQ(1, active_layer()->tilings()->NumHighResTilings()); + auto* high_res = active_layer()->tilings()->FindTilingWithScaleKey(0.5f); + ASSERT_TRUE(high_res); + EXPECT_EQ(HIGH_RESOLUTION, high_res->resolution()); + EXPECT_TRUE(active_layer()->tilings()->FindTilingWithScaleKey(1.0f)); + + // Now, set the bounds to be 1x1, so that minimum contents scale becomes 1. + active_layer()->SetBounds(gfx::Size(1, 1)); + active_layer()->SetRasterSource( + FakeRasterSource::CreateFilled(gfx::Size(1, 1)), Region()); + active_layer()->AddLastAppendQuadsTilingForTesting( + active_layer()->tilings()->FindTilingWithScaleKey(1.0f)); + active_layer()->UpdateTiles(); + + EXPECT_EQ(1.f, active_layer()->MinimumContentsScale()); + + // Since the MinContentsScale is 1, the 0.5 tiling should have been replaced + // by a 1.0 tiling during UpdateRasterSource() and UpdateTiles(). + EXPECT_EQ(1u, active_layer()->tilings()->num_tilings()); + high_res = active_layer()->tilings()->FindTilingWithScaleKey(1.0f); + ASSERT_TRUE(high_res); + EXPECT_EQ(HIGH_RESOLUTION, high_res->resolution()); } TEST_F(LegacySWPictureLayerImplTest, LowResTilingWithoutGpuRasterization) { @@ -2612,11 +2622,9 @@ TEST_F(CommitToActiveTreePictureLayerImplTest, SetupDefaultTrees(layer_bounds); EXPECT_TRUE(host_impl()->use_gpu_rasterization()); - SetContentsScaleOnBothLayers( - dsf /* contents_scale */, dsf /* device_scale_factor */, - 1.0f /* page_scale_factor */, 1.0f /* maximum_animation_contents_scale */, - 1.0f /* starting_animation_contents_scale */, - false /* animating_transform */); + SetContentsScaleOnBothLayers(dsf /* contents_scale */, + dsf /* device_scale_factor */, + 1.0f /* page_scale_factor */); active_layer()->HighResTiling()->UpdateAllRequiredStateForTesting(); @@ -2627,7 +2635,7 @@ TEST_F(CommitToActiveTreePictureLayerImplTest, TEST_F(LegacySWPictureLayerImplTest, NoTilingIfDoesNotDrawContent) { // Set up layers with tilings. SetupDefaultTrees(gfx::Size(10, 10)); - SetContentsScaleOnBothLayers(1.f, 1.f, 1.f, 1.f, 0.f, false); + SetContentsScaleOnBothLayers(1.f, 1.f, 1.f); pending_layer()->PushPropertiesTo(active_layer()); EXPECT_TRUE(pending_layer()->DrawsContent()); EXPECT_TRUE(pending_layer()->CanHaveTilings()); @@ -2659,8 +2667,7 @@ TEST_F(LegacySWPictureLayerImplTest, FirstTilingDuringPinch) { // this case 4. host_impl()->GetInputHandler().PinchGestureBegin(); float high_res_scale = 2.3f; - SetContentsScaleOnBothLayers(high_res_scale, 1.f, high_res_scale, 1.f, 0.f, - false); + SetContentsScaleOnBothLayers(high_res_scale, 1.f, high_res_scale); EXPECT_EQ(4.f, pending_layer()->HighResTiling()->contents_scale_key()); } @@ -2679,8 +2686,7 @@ TEST_F(LegacySWPictureLayerImplTest, PinchingTooSmall) { float high_res_scale = 0.0001f; EXPECT_LT(high_res_scale, pending_layer()->MinimumContentsScale()); - SetContentsScaleOnBothLayers(high_res_scale, 1.f, high_res_scale, 1.f, 0.f, - false); + SetContentsScaleOnBothLayers(high_res_scale, 1.f, high_res_scale); EXPECT_FLOAT_EQ(pending_layer()->MinimumContentsScale(), pending_layer()->HighResTiling()->contents_scale_key()); } @@ -2691,7 +2697,7 @@ TEST_F(LegacySWPictureLayerImplTest, PinchingTooSmallWithContentsScale) { ResetTilingsAndRasterScales(); float contents_scale = 0.15f; - SetContentsScaleOnBothLayers(contents_scale, 1.f, 1.f, 1.f, 0.f, false); + SetContentsScaleOnBothLayers(contents_scale, 1.f, 1.f); ASSERT_GE(pending_layer()->num_tilings(), 0u); EXPECT_FLOAT_EQ(contents_scale, @@ -2708,97 +2714,130 @@ TEST_F(LegacySWPictureLayerImplTest, PinchingTooSmallWithContentsScale) { EXPECT_LT(page_scale * contents_scale, pending_layer()->MinimumContentsScale()); - SetContentsScaleOnBothLayers(contents_scale * page_scale, 1.f, page_scale, - 1.f, 0.f, false); + SetContentsScaleOnBothLayers(contents_scale * page_scale, 1.f, page_scale); ASSERT_GE(pending_layer()->num_tilings(), 0u); EXPECT_FLOAT_EQ(pending_layer()->MinimumContentsScale(), pending_layer()->HighResTiling()->contents_scale_key()); } -TEST_F(LegacySWPictureLayerImplTest, - ConsiderAnimationStartScaleForRasterScale) { +TEST_F(LegacySWPictureLayerImplTest, HighResTilingDuringAnimation) { gfx::Size viewport_size(1000, 1000); host_impl()->active_tree()->SetDeviceViewportRect(gfx::Rect(viewport_size)); gfx::Size layer_bounds(100, 100); SetupDefaultTrees(layer_bounds); - float contents_scale = 2.f; + float contents_scale = 1.f; float device_scale = 1.f; float page_scale = 1.f; - float maximum_animation_scale = 3.f; - float starting_animation_scale = 1.f; - bool animating_transform = true; + float maximum_animation_scale = 1.f; + bool affected_by_invalid_scale = false; EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 1.f); - // Maximum animation scale is greater than starting animation scale - SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale, - maximum_animation_scale, - starting_animation_scale, animating_transform); + // Starting an animation should cause tiling resolution to get set to the + // maximum animation scale factor. + maximum_animation_scale = 3.f; + contents_scale = 2.f; + + SetContentsAndAnimationScalesOnBothLayers(contents_scale, device_scale, + page_scale, maximum_animation_scale, + affected_by_invalid_scale); + EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 3.f); + + // Further changes to scale during the animation should not cause a new + // high-res tiling to get created. + contents_scale = 4.f; + maximum_animation_scale = 5.f; + + SetContentsAndAnimationScalesOnBothLayers(contents_scale, device_scale, + page_scale, maximum_animation_scale, + affected_by_invalid_scale); EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 3.f); - animating_transform = false; + // Once we stop animating, a new high-res tiling should be created. + SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale); + EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 4.f); + + // When animating with a maxmium animation scale factor that is so large + // that the layer grows larger than the viewport at this scale, a new + // high-res tiling should get created at a source scale that the rasterized + // layer will not be larger than the viewport, not at its maximum scale. + contents_scale = 2.f; + maximum_animation_scale = 11.f; + + SetContentsAndAnimationScalesOnBothLayers(contents_scale, device_scale, + page_scale, maximum_animation_scale, + affected_by_invalid_scale); + EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 10.f); // Once we stop animating, a new high-res tiling should be created. - SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale, - maximum_animation_scale, - starting_animation_scale, animating_transform); - EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 2.f); + contents_scale = 11.f; - // Starting animation scale greater than maximum animation scale. - // Bounds at starting scale within the viewport. - animating_transform = true; - starting_animation_scale = 5.f; + SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale); + EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 11.f); + + // When animating with a maxmium animation scale factor that is so large + // that the layer grows larger than the viewport at this scale, and where + // the initial source scale is < 1, a new high-res tiling should get created + // at a source scale that the rasterized layer will not be larger than the + // viewport. + contents_scale = 0.1f; + maximum_animation_scale = 11.f; - SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale, - maximum_animation_scale, - starting_animation_scale, animating_transform); - EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 5.f); + SetContentsAndAnimationScalesOnBothLayers(contents_scale, device_scale, + page_scale, maximum_animation_scale, + affected_by_invalid_scale); + EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 10.f); // Once we stop animating, a new high-res tiling should be created. - animating_transform = false; - SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale, - maximum_animation_scale, - starting_animation_scale, animating_transform); - EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 2.f); + contents_scale = 12.f; + + SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale); + EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 12.f); - // Starting Animation scale greater than maximum animation scale. - // Bounds at starting scale outside the viewport. - animating_transform = true; - starting_animation_scale = 11.f; + // When animating toward a smaller scale, but that is still so large that the + // layer grows larger than the viewport at this scale, a new high-res tiling + // should get created at a source scale that the rasterized layer is not + // larger than the viewport. + contents_scale = 11.f; + maximum_animation_scale = 11.f; - SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale, - maximum_animation_scale, - starting_animation_scale, animating_transform); + SetContentsAndAnimationScalesOnBothLayers(contents_scale, device_scale, + page_scale, maximum_animation_scale, + affected_by_invalid_scale); EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 10.f); + + // Once we stop animating, a new high-res tiling should be created. + contents_scale = 11.f; + + SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale); + EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 11.f); } -TEST_F(LegacySWPictureLayerImplTest, HighResTilingDuringAnimation) { - gfx::Size viewport_size(1000, 1000); +TEST_F(LegacySWPictureLayerImplTest, HighResTilingDuringAnimationWideLayer) { + gfx::Size viewport_size(2048, 2048); host_impl()->active_tree()->SetDeviceViewportRect(gfx::Rect(viewport_size)); - gfx::Size layer_bounds(100, 100); + gfx::Size layer_bounds(1000000, 32); SetupDefaultTrees(layer_bounds); float contents_scale = 1.f; float device_scale = 1.f; float page_scale = 1.f; float maximum_animation_scale = 1.f; - float starting_animation_scale = kNotScaled; - bool animating_transform = false; + bool affected_by_invalid_scale = false; EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 1.f); // Starting an animation should cause tiling resolution to get set to the // maximum animation scale factor. - animating_transform = true; maximum_animation_scale = 3.f; contents_scale = 2.f; - SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale, - maximum_animation_scale, - starting_animation_scale, animating_transform); + SetContentsAndAnimationScalesOnBothLayers(contents_scale, device_scale, + page_scale, maximum_animation_scale, + affected_by_invalid_scale); EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 3.f); // Further changes to scale during the animation should not cause a new @@ -2806,127 +2845,221 @@ TEST_F(LegacySWPictureLayerImplTest, HighResTilingDuringAnimation) { contents_scale = 4.f; maximum_animation_scale = 5.f; - SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale, - maximum_animation_scale, - starting_animation_scale, animating_transform); + SetContentsAndAnimationScalesOnBothLayers(contents_scale, device_scale, + page_scale, maximum_animation_scale, + affected_by_invalid_scale); EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 3.f); // Once we stop animating, a new high-res tiling should be created. - animating_transform = false; - - SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale, - maximum_animation_scale, - starting_animation_scale, animating_transform); - EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 4.f); - - // When animating with an unknown animation scale factors, a new high-res - // tiling should be created at the current contents scale. - animating_transform = true; - contents_scale = 2.f; - maximum_animation_scale = kNotScaled; - - SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale, - maximum_animation_scale, - starting_animation_scale, animating_transform); - EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 2.f); - - // Further changes to scale during the animation should not cause a new - // high-res tiling to get created. - contents_scale = 3.f; - - SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale, - maximum_animation_scale, - starting_animation_scale, animating_transform); - EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 2.f); - - // Once we stop animating, a new high-res tiling should be created. - animating_transform = false; - contents_scale = 4.f; - - SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale, - maximum_animation_scale, - starting_animation_scale, animating_transform); + SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale); EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 4.f); // When animating with a maxmium animation scale factor that is so large - // that the layer grows larger than the viewport at this scale, a new - // high-res tiling should get created at a source scale that the rasterized - // layer will not be larger than the viewport, not at its maximum scale. - animating_transform = true; + // that the layer grows larger than the viewport at this scale, a new high-res + // tiling should get created at a source scale that the rasterized visible + // rect will not be larger than the viewport, not at its maximum scale. contents_scale = 2.f; maximum_animation_scale = 11.f; - SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale, - maximum_animation_scale, - starting_animation_scale, animating_transform); - EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 10.f); + SetContentsAndAnimationScalesOnBothLayers(contents_scale, device_scale, + page_scale, maximum_animation_scale, + affected_by_invalid_scale); + EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 8.f); // Once we stop animating, a new high-res tiling should be created. - animating_transform = false; contents_scale = 11.f; - SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale, - maximum_animation_scale, - starting_animation_scale, animating_transform); + SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale); EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 11.f); // When animating with a maxmium animation scale factor that is so large // that the layer grows larger than the viewport at this scale, and where - // the intial source scale is < 1, a new high-res tiling should get created - // at a source scale that the rasterized layer will not be larger than the - // viewport. - animating_transform = true; + // the initial source scale is < 1, a new high-res tiling should get created + // at a source scale that the rasterized visible rect will not be larger than + // the viewport. contents_scale = 0.1f; maximum_animation_scale = 11.f; - SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale, - maximum_animation_scale, - starting_animation_scale, animating_transform); - EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 10.f); + SetContentsAndAnimationScalesOnBothLayers(contents_scale, device_scale, + page_scale, maximum_animation_scale, + affected_by_invalid_scale); + EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 8.f); // Once we stop animating, a new high-res tiling should be created. - animating_transform = false; contents_scale = 12.f; - SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale, - maximum_animation_scale, - starting_animation_scale, animating_transform); + SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale); EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 12.f); // When animating toward a smaller scale, but that is still so large that the // layer grows larger than the viewport at this scale, a new high-res tiling - // should get created at a source scale that the rasterized layer is not - // larger than the viewport. - animating_transform = true; + // should get created at a source scale that the rasterized visible rect is + // not larger than the viewport. contents_scale = 11.f; maximum_animation_scale = 11.f; - SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale, - maximum_animation_scale, - starting_animation_scale, animating_transform); - EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 10.f); + SetContentsAndAnimationScalesOnBothLayers(contents_scale, device_scale, + page_scale, maximum_animation_scale, + affected_by_invalid_scale); + EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 8.f); // Once we stop animating, a new high-res tiling should be created. - animating_transform = false; contents_scale = 11.f; - SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale, - maximum_animation_scale, - starting_animation_scale, animating_transform); + SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale); EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 11.f); +} + +TEST_F(LegacySWPictureLayerImplTest, + HighResTilingDuringAnimationSmallerAnimationScale) { + gfx::Size viewport_size(1000, 1000); + host_impl()->active_tree()->SetDeviceViewportRect(gfx::Rect(viewport_size)); + + gfx::Size layer_bounds(100, 100); + SetupDefaultTrees(layer_bounds); + + float contents_scale = 1.f; + float device_scale = 1.f; + float page_scale = 1.f; + float maximum_animation_scale = 1.f; + bool affected_by_invalid_scale = false; + + EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 1.f); - // When animating with an unknown animation scale factors, a new high-res - // tiling should be created at the native scale if the current contents - // scale is smaller. - animating_transform = true; + // When animating with smaller animation scale factors (i.e. not accurately + // calculated because some limitations, e.g. nested scales), a new high-res + // tiling should be created at the current contents scale. contents_scale = 0.5f; - maximum_animation_scale = kNotScaled; + maximum_animation_scale = 0.4f; + + SetContentsAndAnimationScalesOnBothLayers(contents_scale, device_scale, + page_scale, maximum_animation_scale, + affected_by_invalid_scale); + EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 0.5f); - SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale, - maximum_animation_scale, - starting_animation_scale, animating_transform); - EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), - page_scale * device_scale); + // Increase the contents scale a bit, and the current high-res tiling should + // still be used. + contents_scale = 0.6f; + SetContentsAndAnimationScalesOnBothLayers(contents_scale, device_scale, + page_scale, maximum_animation_scale, + affected_by_invalid_scale); + EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 0.5f); + + // A new high-res tiling should be created at the current contents scale if + // its >1.5x of the current high-res scale. + contents_scale = 0.8f; + SetContentsAndAnimationScalesOnBothLayers(contents_scale, device_scale, + page_scale, maximum_animation_scale, + affected_by_invalid_scale); + EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 0.8f); + + // Reduce the contents scale, and the current high-res tiling should still be + // used. + contents_scale = 0.4f; + SetContentsAndAnimationScalesOnBothLayers(contents_scale, device_scale, + page_scale, maximum_animation_scale, + affected_by_invalid_scale); + EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 0.8f); +} + +TEST_F(LegacySWPictureLayerImplTest, + HighResTilingDuringAnimationSmallerAnimationScaleWithInvalidScale) { + gfx::Size viewport_size(1000, 1000); + host_impl()->active_tree()->SetDeviceViewportRect(gfx::Rect(viewport_size)); + + gfx::Size layer_bounds(100, 100); + SetupDefaultTrees(layer_bounds); + + float contents_scale = 1.f; + float device_scale = 1.f; + float page_scale = 1.f; + float maximum_animation_scale = 1.f; + bool affected_by_invalid_scale = true; + + EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 1.f); + + // When animating with smaller animation scale factors (i.e. not accurately + // calculated because some limitations, e.g. nested scales), a new high-res + // tiling should be created at the current contents scale. + contents_scale = 0.5f; + maximum_animation_scale = 0.4f; + + SetContentsAndAnimationScalesOnBothLayers(contents_scale, device_scale, + page_scale, maximum_animation_scale, + affected_by_invalid_scale); + EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 0.5f); + + // Increase the contents scale a bit, and the current high-res tiling should + // still be used. + contents_scale = 0.6f; + SetContentsAndAnimationScalesOnBothLayers(contents_scale, device_scale, + page_scale, maximum_animation_scale, + affected_by_invalid_scale); + EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 0.5f); + + // Raster scale change during animation should be avoided. + contents_scale = 1.2f; + SetContentsAndAnimationScalesOnBothLayers(contents_scale, device_scale, + page_scale, maximum_animation_scale, + affected_by_invalid_scale); + EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 0.5f); + + // Raster scale change during animation should be avoided. + contents_scale = 0.4f; + SetContentsAndAnimationScalesOnBothLayers(contents_scale, device_scale, + page_scale, maximum_animation_scale, + affected_by_invalid_scale); + EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 0.5f); + + // Should update raster scale if the cache is invalidated (simulating that a + // new property tree is pushed). + contents_scale = 1.2f; + SetMaximumAnimationToScreenScale(pending_layer(), maximum_animation_scale, + affected_by_invalid_scale); + host_impl()->pending_tree()->property_trees()->ResetCachedData(); + SetupDrawPropertiesAndUpdateTiles(pending_layer(), contents_scale, + device_scale, page_scale); + SetMaximumAnimationToScreenScale(active_layer(), maximum_animation_scale, + affected_by_invalid_scale); + host_impl()->active_tree()->property_trees()->ResetCachedData(); + SetupDrawPropertiesAndUpdateTiles(active_layer(), contents_scale, + device_scale, page_scale); + EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 1.2f); +} + +TEST_F(LegacySWPictureLayerImplTest, ViewportSizeChangeDuringAnimation) { + gfx::Size layer_bounds(100, 100); + SetupDefaultTrees(layer_bounds); + + host_impl()->pending_tree()->SetDeviceViewportRect(gfx::Rect()); + + EXPECT_EQ(pending_layer()->HighResTiling()->contents_scale_key(), 1.f); + + float maximum_animation_scale = 20.f; + // This flag should be ignored on viewport size change. + bool affected_by_invalid_scale = true; + + // Starting an animation should cause tiling resolution to get set to the + // maximum animation scale factor, clamped by the viewport size (using default + // minimum 500x500 as the viewport is empty for now). + SetMaximumAnimationToScreenScale(pending_layer(), maximum_animation_scale, + affected_by_invalid_scale); + pending_layer()->UpdateTiles(); + EXPECT_EQ(pending_layer()->HighResTiling()->contents_scale_key(), 5.f); + + // Setting viewport rect smaller than the minimum won't change raster scale. + host_impl()->pending_tree()->SetDeviceViewportRect(gfx::Rect(400, 400)); + SetMaximumAnimationToScreenScale(pending_layer(), maximum_animation_scale, + affected_by_invalid_scale); + pending_layer()->UpdateTiles(); + EXPECT_EQ(pending_layer()->HighResTiling()->contents_scale_key(), 5.f); + + // For a larger viewport size, the clamped scale is also larger. + host_impl()->pending_tree()->SetDeviceViewportRect(gfx::Rect(1000, 200)); + SetMaximumAnimationToScreenScale(pending_layer(), maximum_animation_scale, + affected_by_invalid_scale); + pending_layer()->UpdateTiles(); + EXPECT_EQ(pending_layer()->HighResTiling()->contents_scale_key(), 10.f); } TEST_F(LegacySWPictureLayerImplTest, @@ -2941,8 +3074,7 @@ TEST_F(LegacySWPictureLayerImplTest, float device_scale = 1.f; float page_scale = 1.f; float maximum_animation_scale = 1.f; - float starting_animation_scale = kNotScaled; - bool animating_transform = false; + bool affected_by_invalid_scale = false; EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 1.f); @@ -2951,41 +3083,31 @@ TEST_F(LegacySWPictureLayerImplTest, // Starting an animation should cause tiling resolution to get set to the // maximum animation scale factor. - animating_transform = true; maximum_animation_scale = 2.f; contents_scale = 1.f; - SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale, - maximum_animation_scale, - starting_animation_scale, animating_transform); + SetContentsAndAnimationScalesOnBothLayers(contents_scale, device_scale, + page_scale, maximum_animation_scale, + affected_by_invalid_scale); EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 2.f); // Once we stop animating, because we have a will-change: transform hint // we should not reset the scale factor. - animating_transform = false; - - SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale, - maximum_animation_scale, - starting_animation_scale, animating_transform); + SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale); EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 2.f); // Starting an animation with a different maximum animation scale should // not cause a change either. - animating_transform = true; maximum_animation_scale = 1.5f; - SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale, - maximum_animation_scale, - starting_animation_scale, animating_transform); + SetContentsAndAnimationScalesOnBothLayers(contents_scale, device_scale, + page_scale, maximum_animation_scale, + affected_by_invalid_scale); EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 2.f); // Again, stop animating, because we have a will-change: transform hint // we should not reset the scale factor. - animating_transform = false; - - SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale, - maximum_animation_scale, - starting_animation_scale, animating_transform); + SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale); EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 2.f); } @@ -3000,20 +3122,18 @@ TEST_F(LegacySWPictureLayerImplTest, HighResTilingDuringAnimationAspectRatio) { float device_scale = 1.f; float page_scale = 1.f; float maximum_animation_scale = 1.f; - float starting_animation_scale = kNotScaled; - bool animating_transform = false; + bool affected_by_invalid_scale = false; EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 1.f); // Allow rastering at maximum scale if the animation size is smaller than - // the square of the maximum viewporrt dimension. - animating_transform = true; + // the square of the maximum viewport dimension. contents_scale = 2.f; maximum_animation_scale = 15.f; - SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale, - maximum_animation_scale, - starting_animation_scale, animating_transform); + SetContentsAndAnimationScalesOnBothLayers(contents_scale, device_scale, + page_scale, maximum_animation_scale, + affected_by_invalid_scale); EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 15.f); } @@ -3029,21 +3149,19 @@ TEST_F(LegacySWPictureLayerImplTest, float device_scale = 1.f; float page_scale = 1.f; float maximum_animation_scale = 1.f; - float starting_animation_scale = kNotScaled; - bool animating_transform = false; + bool affected_by_invalid_scale = false; EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 1.f); // The maximum animation scale exceeds the squared size of the maximum // viewport dimension, so raster scale should be shrunk to make the // rasterized layer not larger than the viewport. - animating_transform = true; contents_scale = 2.f; maximum_animation_scale = 21.f; - SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale, - maximum_animation_scale, - starting_animation_scale, animating_transform); + SetContentsAndAnimationScalesOnBothLayers(contents_scale, device_scale, + page_scale, maximum_animation_scale, + affected_by_invalid_scale); EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 20.f); } @@ -3489,29 +3607,20 @@ TEST_F(LegacySWPictureLayerImplTest, RasterScaleChangeWithoutAnimation) { float contents_scale = 2.f; float device_scale = 1.5f; float page_scale = 1.f; - float maximum_animation_scale = 1.f; - float starting_animation_scale = kNotScaled; - bool animating_transform = false; - SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale, - maximum_animation_scale, - starting_animation_scale, animating_transform); + SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale); EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 2.f); // Changing the source scale without being in an animation will cause // the layer to change scale. contents_scale = 3.f; - SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale, - maximum_animation_scale, - starting_animation_scale, animating_transform); + SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale); EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 3.f); contents_scale = 0.5f; - SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale, - maximum_animation_scale, - starting_animation_scale, animating_transform); + SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale); EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 0.5f); // If we change the layer contents scale after setting will change @@ -3522,9 +3631,7 @@ TEST_F(LegacySWPictureLayerImplTest, RasterScaleChangeWithoutAnimation) { contents_scale = 0.75f; - SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale, - maximum_animation_scale, - starting_animation_scale, animating_transform); + SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale); // The scale is clamped to the native scale. EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 1.5f); @@ -3532,17 +3639,13 @@ TEST_F(LegacySWPictureLayerImplTest, RasterScaleChangeWithoutAnimation) { // contents scale. contents_scale = 2.f; - SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale, - maximum_animation_scale, - starting_animation_scale, animating_transform); + SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale); EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 1.5f); // Ditto. contents_scale = 20.f; - SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale, - maximum_animation_scale, - starting_animation_scale, animating_transform); + SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale); EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 1.5f); // Disabling the will-change hint will once again make the raster scale update @@ -3552,9 +3655,7 @@ TEST_F(LegacySWPictureLayerImplTest, RasterScaleChangeWithoutAnimation) { contents_scale = 3.f; - SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale, - maximum_animation_scale, - starting_animation_scale, animating_transform); + SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale); EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 3.f); } @@ -3567,13 +3668,8 @@ TEST_F(LegacySWPictureLayerImplTest, TinyRasterScale) { float contents_scale = 0.01f; float device_scale = 1.5f; float page_scale = 1.f; - float maximum_animation_scale = 1.f; - float starting_animation_scale = kNotScaled; - bool animating_transform = false; - SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale, - maximum_animation_scale, - starting_animation_scale, animating_transform); + SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale); EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 0.01f); // If we change the layer contents scale after setting will change @@ -3582,9 +3678,7 @@ TEST_F(LegacySWPictureLayerImplTest, TinyRasterScale) { GetTransformNode(active_layer())->will_change_transform = true; GetTransformNode(pending_layer())->will_change_transform = true; - SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale, - maximum_animation_scale, - starting_animation_scale, animating_transform); + SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale); // The scale is clamped to the native scale. EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 0.01f); @@ -3592,25 +3686,19 @@ TEST_F(LegacySWPictureLayerImplTest, TinyRasterScale) { // contents scale. contents_scale = 0.02f; - SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale, - maximum_animation_scale, - starting_animation_scale, animating_transform); + SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale); EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 0.01f); // ... unless the difference is very big. contents_scale = 0.12f; - SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale, - maximum_animation_scale, - starting_animation_scale, animating_transform); + SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale); EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 0.12f); // Bigger scale will be clamped to the native scale. contents_scale = 0.5f; - SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale, - maximum_animation_scale, - starting_animation_scale, animating_transform); + SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale); EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 1.5f); } @@ -3690,24 +3778,18 @@ TEST_F(NoLowResPictureLayerImplTest, ManageTilingsCreatesTilings) { ResetTilingsAndRasterScales(); SetupDrawPropertiesAndUpdateTiles(active_layer(), - 6.f, // ideal contents scale - 3.f, // device scale - 2.f, // page scale - 1.f, // maximum animation scale - 0.f, // starting animation scale - false); + 6.f, // ideal contents scale + 3.f, // device scale + 2.f); // page scale ASSERT_EQ(1u, active_layer()->tilings()->num_tilings()); EXPECT_FLOAT_EQ( 6.f, active_layer()->tilings()->tiling_at(0)->contents_scale_key()); // If we change the page scale factor, then we should get new tilings. SetupDrawPropertiesAndUpdateTiles(active_layer(), - 6.6f, // ideal contents scale - 3.f, // device scale - 2.2f, // page scale - 1.f, // maximum animation scale - 0.f, // starting animation scale - false); + 6.6f, // ideal contents scale + 3.f, // device scale + 2.2f); // page scale ASSERT_EQ(2u, active_layer()->tilings()->num_tilings()); EXPECT_FLOAT_EQ( 6.6f, active_layer()->tilings()->tiling_at(0)->contents_scale_key()); @@ -3716,10 +3798,7 @@ TEST_F(NoLowResPictureLayerImplTest, ManageTilingsCreatesTilings) { SetupDrawPropertiesAndUpdateTiles(active_layer(), 7.26f, // ideal contents scale 3.3f, // device scale - 2.2f, // page scale - 1.f, // maximum animation scale - 0.f, // starting animation scale - false); + 2.2f); // page scale ASSERT_EQ(3u, active_layer()->tilings()->num_tilings()); EXPECT_FLOAT_EQ( 7.26f, active_layer()->tilings()->tiling_at(0)->contents_scale_key()); @@ -3729,10 +3808,7 @@ TEST_F(NoLowResPictureLayerImplTest, ManageTilingsCreatesTilings) { SetupDrawPropertiesAndUpdateTiles(active_layer(), 7.26f, // ideal contents scale 2.2f, // device scale - 3.3f, // page scale - 1.f, // maximum animation scale - 0.f, // starting animation scale - false); + 3.3f); // page scale ASSERT_EQ(3u, active_layer()->tilings()->num_tilings()); EXPECT_FLOAT_EQ( 7.26f, active_layer()->tilings()->tiling_at(0)->contents_scale_key()); @@ -3748,24 +3824,18 @@ TEST_F(NoLowResPictureLayerImplTest, PendingLayerOnlyHasHighResTiling) { ResetTilingsAndRasterScales(); SetupDrawPropertiesAndUpdateTiles(pending_layer(), - 6.f, // ideal contents scale - 3.f, // device scale - 2.f, // page scale - 1.f, // maximum animation scale - 0.f, // starting animation scale - false); + 6.f, // ideal contents scale + 3.f, // device scale + 2.f); // page scale ASSERT_EQ(1u, pending_layer()->tilings()->num_tilings()); EXPECT_FLOAT_EQ( 6.f, pending_layer()->tilings()->tiling_at(0)->contents_scale_key()); // If we change the page scale factor, then we should get new tilings. SetupDrawPropertiesAndUpdateTiles(pending_layer(), - 6.6f, // ideal contents scale - 3.f, // device scale - 2.2f, // page scale - 1.f, // maximum animation scale - 0.f, // starting animation scale - false); + 6.6f, // ideal contents scale + 3.f, // device scale + 2.2f); // page scale ASSERT_EQ(1u, pending_layer()->tilings()->num_tilings()); EXPECT_FLOAT_EQ( 6.6f, pending_layer()->tilings()->tiling_at(0)->contents_scale_key()); @@ -3774,10 +3844,7 @@ TEST_F(NoLowResPictureLayerImplTest, PendingLayerOnlyHasHighResTiling) { SetupDrawPropertiesAndUpdateTiles(pending_layer(), 7.26f, // ideal contents scale 3.3f, // device scale - 2.2f, // page scale - 1.f, // maximum animation scale - 0.f, // starting animation scale - false); + 2.2f); // page scale ASSERT_EQ(1u, pending_layer()->tilings()->num_tilings()); EXPECT_FLOAT_EQ( 7.26f, pending_layer()->tilings()->tiling_at(0)->contents_scale_key()); @@ -3787,10 +3854,7 @@ TEST_F(NoLowResPictureLayerImplTest, PendingLayerOnlyHasHighResTiling) { SetupDrawPropertiesAndUpdateTiles(pending_layer(), 7.26f, // ideal contents scale 2.2f, // device scale - 3.3f, // page scale - 1.f, // maximum animation scale - 0.f, // starting animation scale - false); + 3.3f); // page scale ASSERT_EQ(1u, pending_layer()->tilings()->num_tilings()); EXPECT_FLOAT_EQ( 7.26f, pending_layer()->tilings()->tiling_at(0)->contents_scale_key()); @@ -3871,8 +3935,7 @@ TEST_F(NoLowResPictureLayerImplTest, CleanUpTilings) { GetTransformNode(active_layer())->will_change_transform = true; ResetTilingsAndRasterScales(); - SetContentsScaleOnBothLayers(scale, device_scale, page_scale, 1.f, 0.f, - false); + SetContentsScaleOnBothLayers(scale, device_scale, page_scale); ASSERT_EQ(1u, active_layer()->tilings()->num_tilings()); // Ensure UpdateTiles won't remove any tilings. Note this is unrelated to @@ -3895,8 +3958,7 @@ TEST_F(NoLowResPictureLayerImplTest, CleanUpTilings) { // Changing the ideal but not creating new tilings. scale *= 1.5f; page_scale *= 1.5f; - SetContentsScaleOnBothLayers(scale, device_scale, page_scale, 1.f, 0.f, - false); + SetContentsScaleOnBothLayers(scale, device_scale, page_scale); ASSERT_EQ(1u, active_layer()->tilings()->num_tilings()); // The tilings are still our target scale, so they aren't removed. @@ -3909,7 +3971,7 @@ TEST_F(NoLowResPictureLayerImplTest, CleanUpTilings) { // Create a 1.2 scale tiling. Now we have 1.0 and 1.2 tilings. Ideal = 1.2. scale /= 4.f; page_scale /= 4.f; - SetContentsScaleOnBothLayers(1.2f, device_scale, page_scale, 1.f, 0.f, false); + SetContentsScaleOnBothLayers(1.2f, device_scale, page_scale); ASSERT_EQ(2u, active_layer()->tilings()->num_tilings()); EXPECT_FLOAT_EQ( 1.f, active_layer()->tilings()->tiling_at(1)->contents_scale_key()); @@ -3924,7 +3986,7 @@ TEST_F(NoLowResPictureLayerImplTest, CleanUpTilings) { ASSERT_EQ(2u, active_layer()->tilings()->num_tilings()); // Now move the ideal scale to 0.5. Our target stays 1.2. - SetContentsScaleOnBothLayers(0.5f, device_scale, page_scale, 1.f, 0.f, false); + SetContentsScaleOnBothLayers(0.5f, device_scale, page_scale); // The high resolution tiling is between target and ideal, so is not // removed. The low res tiling for the old ideal=1.0 scale is removed. @@ -3933,7 +3995,7 @@ TEST_F(NoLowResPictureLayerImplTest, CleanUpTilings) { ASSERT_EQ(2u, active_layer()->tilings()->num_tilings()); // Now move the ideal scale to 1.0. Our target stays 1.2. - SetContentsScaleOnBothLayers(1.f, device_scale, page_scale, 1.f, 0.f, false); + SetContentsScaleOnBothLayers(1.f, device_scale, page_scale); // All the tilings are between are target and the ideal, so they are not // removed. @@ -3943,7 +4005,7 @@ TEST_F(NoLowResPictureLayerImplTest, CleanUpTilings) { // Now move the ideal scale to 1.1 on the active layer. Our target stays 1.2. SetupDrawPropertiesAndUpdateTiles(active_layer(), 1.1f, device_scale, - page_scale, 1.f, 0.f, false); + page_scale); // Because the pending layer's ideal scale is still 1.0, our tilings fall // in the range [1.0,1.2] and are kept. @@ -3954,7 +4016,7 @@ TEST_F(NoLowResPictureLayerImplTest, CleanUpTilings) { // Move the ideal scale on the pending layer to 1.1 as well. Our target stays // 1.2 still. SetupDrawPropertiesAndUpdateTiles(pending_layer(), 1.1f, device_scale, - page_scale, 1.f, 0.f, false); + page_scale); // Our 1.0 tiling now falls outside the range between our ideal scale and our // target raster scale. But it is in our used tilings set, so nothing is @@ -3991,12 +4053,9 @@ TEST_F(NoLowResPictureLayerImplTest, ReleaseTileResources) { // This should create new tilings. SetupDrawPropertiesAndUpdateTiles(pending_layer(), - 1.3f, // ideal contents scale - 2.7f, // device scale - 3.2f, // page scale - 1.f, // maximum animation scale - 0.f, // starting animation scale - false); + 1.3f, // ideal contents scale + 2.7f, // device scale + 3.2f); // page scale EXPECT_EQ(1u, pending_layer()->tilings()->num_tilings()); } @@ -4008,8 +4067,7 @@ TEST_F(LegacySWPictureLayerImplTest, SharedQuadStateContainsMaxTilingScale) { SetupDefaultTrees(layer_bounds); ResetTilingsAndRasterScales(); - SetupDrawPropertiesAndUpdateTiles(active_layer(), 2.5f, 1.f, 1.f, 1.f, 0.f, - false); + SetupDrawPropertiesAndUpdateTiles(active_layer(), 2.5f, 1.f, 1.f); float max_contents_scale = active_layer()->MaximumTilingContentsScale(); EXPECT_EQ(2.5f, max_contents_scale); @@ -4894,8 +4952,7 @@ TEST_F(LegacySWPictureLayerImplTest, ChangeInViewportAllowsTilingUpdates) { // Update tiles. pending_layer()->draw_properties().visible_layer_rect = viewport; pending_layer()->draw_properties().screen_space_transform = transform; - SetupDrawPropertiesAndUpdateTiles(pending_layer(), 1.f, 1.f, 1.f, 1.f, 0.f, - false); + SetupDrawPropertiesAndUpdateTiles(pending_layer(), 1.f, 1.f, 1.f); pending_layer()->HighResTiling()->UpdateAllRequiredStateForTesting(); // Ensure we can't activate. @@ -4908,8 +4965,7 @@ TEST_F(LegacySWPictureLayerImplTest, ChangeInViewportAllowsTilingUpdates) { // Update tiles. pending_layer()->draw_properties().visible_layer_rect = viewport; pending_layer()->draw_properties().screen_space_transform = transform; - SetupDrawPropertiesAndUpdateTiles(pending_layer(), 1.f, 1.f, 1.f, 1.f, 0.f, - false); + SetupDrawPropertiesAndUpdateTiles(pending_layer(), 1.f, 1.f, 1.f); pending_layer()->HighResTiling()->UpdateAllRequiredStateForTesting(); // Make sure all viewport tiles (viewport from the tiling) are ready to draw. @@ -4961,9 +5017,9 @@ TEST_F(LegacySWPictureLayerImplTest, CloneMissingRecordings) { // dropped recordings). This will cause us to be missing some tiles. SetupPendingTreeWithFixedTileSize(partial_raster_source, tile_size, Region(gfx::Rect(layer_bounds))); - EXPECT_EQ(3u * 3u, pending_tiling->AllTilesForTesting().size()); + EXPECT_EQ(4u * 4u, pending_tiling->AllTilesForTesting().size()); EXPECT_FALSE(pending_tiling->TileAt(0, 0)); - EXPECT_FALSE(pending_tiling->TileAt(1, 1)); + EXPECT_TRUE(pending_tiling->TileAt(1, 1)); EXPECT_TRUE(pending_tiling->TileAt(2, 2)); // Active is not affected yet. @@ -4971,9 +5027,9 @@ TEST_F(LegacySWPictureLayerImplTest, CloneMissingRecordings) { // Activate the tree. The same tiles go missing on the active tree. ActivateTree(); - EXPECT_EQ(3u * 3u, active_tiling->AllTilesForTesting().size()); + EXPECT_EQ(4u * 4u, active_tiling->AllTilesForTesting().size()); EXPECT_FALSE(active_tiling->TileAt(0, 0)); - EXPECT_FALSE(active_tiling->TileAt(1, 1)); + EXPECT_TRUE(active_tiling->TileAt(1, 1)); EXPECT_TRUE(active_tiling->TileAt(2, 2)); // Now put a full recording on the pending tree again. We'll get all our tiles @@ -4986,7 +5042,7 @@ TEST_F(LegacySWPictureLayerImplTest, CloneMissingRecordings) { Tile::Id tile22 = pending_tiling->TileAt(2, 2)->id(); // Active is not affected yet. - EXPECT_EQ(3u * 3u, active_tiling->AllTilesForTesting().size()); + EXPECT_EQ(4u * 4u, active_tiling->AllTilesForTesting().size()); // Activate the tree. The tiles are moved to the active tree. ActivateTree(); @@ -5161,7 +5217,7 @@ TEST_F(LegacySWPictureLayerImplTest, UpdateLCDTextPushToActiveTree) { SetupPendingTree(FakeRasterSource::CreateFilledWithText(gfx::Size(200, 200))); float page_scale = 4.f; SetupDrawPropertiesAndUpdateTiles(pending_layer(), page_scale, 1.0f, - page_scale, 1.0f, 0.f, false); + page_scale); EXPECT_TRUE(pending_layer()->can_use_lcd_text()); EXPECT_TRUE(pending_layer()->HighResTiling()->can_use_lcd_text()); ActivateTree(); @@ -5178,7 +5234,7 @@ TEST_F(LegacySWPictureLayerImplTest, UpdateLCDTextPushToActiveTree) { SetupPendingTree(FakeRasterSource::CreateFilledWithText(gfx::Size(200, 200))); SetupDrawPropertiesAndUpdateTiles(pending_layer(), page_scale, 1.0f, - page_scale, 1.0f, 0.f, false); + page_scale); pending_layer()->SetContentsOpaque(false); pending_layer()->UpdateTiles(); EXPECT_FALSE(pending_layer()->can_use_lcd_text()); @@ -5376,7 +5432,7 @@ TEST_F(NoLowResPictureLayerImplTest, LowResWasHighResCollision) { ResetTilingsAndRasterScales(); float page_scale = 2.f; - SetContentsScaleOnBothLayers(page_scale, 1.0f, page_scale, 1.0f, 0.f, false); + SetContentsScaleOnBothLayers(page_scale, 1.0f, page_scale); EXPECT_BOTH_EQ(num_tilings(), 1u); EXPECT_BOTH_EQ(tilings()->tiling_at(0)->contents_scale_key(), page_scale); @@ -5390,7 +5446,7 @@ TEST_F(NoLowResPictureLayerImplTest, LowResWasHighResCollision) { // Zoom out to exactly the low res factor so that the previous high res // would be equal to the current low res (if it were possible to have one). float zoomed = page_scale / low_res_factor; - SetContentsScaleOnBothLayers(zoomed, 1.0f, zoomed, 1.0f, 0.f, false); + SetContentsScaleOnBothLayers(zoomed, 1.0f, zoomed); EXPECT_EQ(1u, pending_layer()->num_tilings()); EXPECT_EQ(zoomed, pending_layer()->tilings()->tiling_at(0)->contents_scale_key()); @@ -5408,7 +5464,7 @@ TEST_F(LegacySWPictureLayerImplTest, HighResWasLowResCollision) { float low_res = page_scale * low_res_factor; float extra_low_res = low_res * low_res_factor; SetupDrawPropertiesAndUpdateTiles(active_layer(), page_scale, 1.0f, - page_scale, 1.0f, 0.f, false); + page_scale); EXPECT_EQ(2u, active_layer()->tilings()->num_tilings()); EXPECT_EQ(page_scale, active_layer()->tilings()->tiling_at(0)->contents_scale_key()); @@ -5431,8 +5487,7 @@ TEST_F(LegacySWPictureLayerImplTest, HighResWasLowResCollision) { // Zoom in to exactly the low res factor so that the previous low res // would be equal to the current high res. - SetupDrawPropertiesAndUpdateTiles(active_layer(), low_res, 1.0f, low_res, - 1.0f, 0.f, false); + SetupDrawPropertiesAndUpdateTiles(active_layer(), low_res, 1.0f, low_res); // 3 tilings. The old high res, the new high res (old low res) and the new low // res. EXPECT_EQ(3u, active_layer()->num_tilings()); @@ -5479,8 +5534,7 @@ TEST_F(LegacySWPictureLayerImplTest, CompositedImageCalculateContentsScale) { SetupRootProperties(pending_layer_ptr); UpdateDrawProperties(pending_tree); - SetupDrawPropertiesAndUpdateTiles(pending_layer_ptr, 2.f, 3.f, 4.f, 1.f, 1.f, - false); + SetupDrawPropertiesAndUpdateTiles(pending_layer_ptr, 2.f, 3.f, 4.f); EXPECT_FLOAT_EQ(1.f, pending_layer_ptr->MaximumTilingContentsScale()); } @@ -5509,12 +5563,9 @@ TEST_F(LegacySWPictureLayerImplTest, CompositedImageIgnoreIdealContentsScale) { const float suggested_ideal_contents_scale = 2.f; const float device_scale_factor = 3.f; const float page_scale_factor = 4.f; - const float animation_contents_scale = 1.f; - const bool animating_transform_to_screen = false; - SetupDrawPropertiesAndUpdateTiles( - pending_layer_ptr, suggested_ideal_contents_scale, device_scale_factor, - page_scale_factor, animation_contents_scale, animation_contents_scale, - animating_transform_to_screen); + SetupDrawPropertiesAndUpdateTiles(pending_layer_ptr, + suggested_ideal_contents_scale, + device_scale_factor, page_scale_factor); EXPECT_EQ(1.f, pending_layer_ptr->tilings()->tiling_at(0)->contents_scale_key()); @@ -5523,10 +5574,9 @@ TEST_F(LegacySWPictureLayerImplTest, CompositedImageIgnoreIdealContentsScale) { FakePictureLayerImpl* active_layer = static_cast<FakePictureLayerImpl*>( host_impl()->active_tree()->root_layer()); - SetupDrawPropertiesAndUpdateTiles( - active_layer, suggested_ideal_contents_scale, device_scale_factor, - page_scale_factor, animation_contents_scale, animation_contents_scale, - animating_transform_to_screen); + SetupDrawPropertiesAndUpdateTiles(active_layer, + suggested_ideal_contents_scale, + device_scale_factor, page_scale_factor); EXPECT_EQ(1.f, active_layer->tilings()->tiling_at(0)->contents_scale_key()); active_layer->set_visible_layer_rect(layer_rect); @@ -5571,7 +5621,7 @@ TEST_F(LegacySWPictureLayerImplTest, CompositedImageRasterScaleChanges) { break; } SetupDrawPropertiesAndUpdateTiles(pending_layer(), ideal_contents_scale, - 1.f, 1.f, 1.f, 1.f, false); + 1.f, 1.f); EXPECT_FLOAT_EQ(expected_contents_scale, pending_layer() ->picture_layer_tiling_set() @@ -5594,7 +5644,7 @@ TEST_F(LegacySWPictureLayerImplTest, CompositedImageRasterScaleChanges) { break; } SetupDrawPropertiesAndUpdateTiles(pending_layer(), ideal_contents_scale, - 1.f, 1.f, 1.f, 1.f, false); + 1.f, 1.f); EXPECT_FLOAT_EQ(expected_contents_scale, pending_layer() ->picture_layer_tiling_set() @@ -5614,8 +5664,7 @@ TEST_F(LegacySWPictureLayerImplTest, CompositedImageRasterOnChange) { // Set an image size that is smaller than the layer bounds. gfx::Size image_size(200, 200); pending_layer()->SetDirectlyCompositedImageSize(image_size); - SetupDrawPropertiesAndUpdateTiles(pending_layer(), 1.f, 1.f, 1.f, 1.f, 1.f, - false); + SetupDrawPropertiesAndUpdateTiles(pending_layer(), 1.f, 1.f, 1.f); EXPECT_FLOAT_EQ(0.5f, pending_layer() ->picture_layer_tiling_set() ->FindTilingWithResolution(HIGH_RESOLUTION) @@ -5623,8 +5672,7 @@ TEST_F(LegacySWPictureLayerImplTest, CompositedImageRasterOnChange) { // Change the bounds and ensure we recalculated raster scale. pending_layer()->SetBounds(gfx::Size(320, 320)); - SetupDrawPropertiesAndUpdateTiles(pending_layer(), 1.f, 1.f, 1.f, 1.f, 1.f, - false); + SetupDrawPropertiesAndUpdateTiles(pending_layer(), 1.f, 1.f, 1.f); EXPECT_FLOAT_EQ(0.625f, pending_layer() ->picture_layer_tiling_set() ->FindTilingWithResolution(HIGH_RESOLUTION) @@ -5635,8 +5683,7 @@ TEST_F(LegacySWPictureLayerImplTest, CompositedImageRasterOnChange) { // is less than 4x the ideal source scale). pending_layer()->SetBounds(layer_bounds); pending_layer()->SetDirectlyCompositedImageSize(gfx::Size(2000, 2000)); - SetupDrawPropertiesAndUpdateTiles(pending_layer(), 1.f, 1.f, 1.f, 1.f, 1.f, - false); + SetupDrawPropertiesAndUpdateTiles(pending_layer(), 1.f, 1.f, 1.f); EXPECT_FLOAT_EQ(2.5f, pending_layer() ->picture_layer_tiling_set() ->FindTilingWithResolution(HIGH_RESOLUTION) @@ -5645,8 +5692,7 @@ TEST_F(LegacySWPictureLayerImplTest, CompositedImageRasterOnChange) { // Update the bounds to no longer match the aspect ratio, but still compute // the same raster scale. pending_layer()->SetBounds(gfx::Size(600, 500)); - SetupDrawPropertiesAndUpdateTiles(pending_layer(), 1.f, 1.f, 1.f, 1.f, 1.f, - false); + SetupDrawPropertiesAndUpdateTiles(pending_layer(), 1.f, 1.f, 1.f); EXPECT_FLOAT_EQ(4.f, pending_layer() ->picture_layer_tiling_set() ->FindTilingWithResolution(HIGH_RESOLUTION) @@ -5655,8 +5701,7 @@ TEST_F(LegacySWPictureLayerImplTest, CompositedImageRasterOnChange) { // Update the bounds and and bump up the ideal scale so that the scale down // restriction is lifted. pending_layer()->SetBounds(layer_bounds); - SetupDrawPropertiesAndUpdateTiles(pending_layer(), 4.f, 1.f, 1.f, 1.f, 1.f, - false); + SetupDrawPropertiesAndUpdateTiles(pending_layer(), 4.f, 1.f, 1.f); EXPECT_FLOAT_EQ(5.f, pending_layer() ->picture_layer_tiling_set() ->FindTilingWithResolution(HIGH_RESOLUTION) @@ -5664,15 +5709,13 @@ TEST_F(LegacySWPictureLayerImplTest, CompositedImageRasterOnChange) { // Lower the ideal scale to see that the clamping still applied as it is // lowered. - SetupDrawPropertiesAndUpdateTiles(pending_layer(), 0.5f, 1.f, 1.f, 1.f, 1.f, - false); + SetupDrawPropertiesAndUpdateTiles(pending_layer(), 0.5f, 1.f, 1.f); EXPECT_FLOAT_EQ(1.25f, pending_layer() ->picture_layer_tiling_set() ->FindTilingWithResolution(HIGH_RESOLUTION) ->contents_scale_key()); - SetupDrawPropertiesAndUpdateTiles(pending_layer(), 0.25f, 1.f, 1.f, 1.f, 1.f, - false); + SetupDrawPropertiesAndUpdateTiles(pending_layer(), 0.25f, 1.f, 1.f); EXPECT_FLOAT_EQ(0.625f, pending_layer() ->picture_layer_tiling_set() ->FindTilingWithResolution(HIGH_RESOLUTION) @@ -5690,8 +5733,7 @@ TEST_F(LegacySWPictureLayerImplTest, CompositedImageRasterOptOutTransitions) { // in to directly composited images. pending_layer()->SetBounds(layer_bounds); pending_layer()->SetDirectlyCompositedImageSize(layer_bounds); - SetupDrawPropertiesAndUpdateTiles(pending_layer(), 0.3f, 1.f, 1.f, 1.f, 1.f, - false); + SetupDrawPropertiesAndUpdateTiles(pending_layer(), 0.3f, 1.f, 1.f); EXPECT_FLOAT_EQ(1.f, pending_layer() ->picture_layer_tiling_set() ->FindTilingWithResolution(HIGH_RESOLUTION) @@ -5702,8 +5744,7 @@ TEST_F(LegacySWPictureLayerImplTest, CompositedImageRasterOptOutTransitions) { // 0.1f (matching the ideal source scale). gfx::Size image_size(300, 300); pending_layer()->SetDirectlyCompositedImageSize(image_size); - SetupDrawPropertiesAndUpdateTiles(pending_layer(), 0.2f, 1.f, 1.f, 1.f, 1.f, - false); + SetupDrawPropertiesAndUpdateTiles(pending_layer(), 0.2f, 1.f, 1.f); EXPECT_FLOAT_EQ(0.2f, pending_layer() ->picture_layer_tiling_set() ->FindTilingWithResolution(HIGH_RESOLUTION) @@ -5714,8 +5755,7 @@ TEST_F(LegacySWPictureLayerImplTest, CompositedImageRasterOptOutTransitions) { pending_layer()->SetBounds(ScaleToFlooredSize(layer_bounds, 2)); pending_layer()->SetDirectlyCompositedImageSize( ScaleToFlooredSize(image_size, 2)); - SetupDrawPropertiesAndUpdateTiles(pending_layer(), 0.2f, 1.f, 1.f, 1.f, 1.f, - false); + SetupDrawPropertiesAndUpdateTiles(pending_layer(), 0.2f, 1.f, 1.f); EXPECT_FLOAT_EQ(0.46875, pending_layer() ->picture_layer_tiling_set() ->FindTilingWithResolution(HIGH_RESOLUTION) @@ -5731,7 +5771,7 @@ TEST_F(LegacySWPictureLayerImplTest, Region()); // Start with scale & translation of * 2.25 + (0.25, 0.5). - SetupDrawProperties(pending_layer(), 2.25f, 1.5f, 1.f, 2.25f, 2.25f, false); + SetupDrawProperties(pending_layer(), 2.25f, 1.5f, 1.f); gfx::Transform translate1; translate1.Translate(0.25f, 0.5f); pending_layer()->draw_properties().screen_space_transform.ConcatTransform( @@ -5751,7 +5791,7 @@ TEST_F(LegacySWPictureLayerImplTest, // Change to scale & translation of * 2.25 + (0.75, 0.25). // Verifies that layer movement updates raster translation. - SetupDrawProperties(pending_layer(), 2.25f, 1.5f, 1.f, 2.25f, 2.25f, false); + SetupDrawProperties(pending_layer(), 2.25f, 1.5f, 1.f); gfx::Transform translate2; translate2.Translate(0.75f, 0.25f); pending_layer()->draw_properties().screen_space_transform.ConcatTransform( @@ -5771,7 +5811,7 @@ TEST_F(LegacySWPictureLayerImplTest, // Now change the device scale factor but keep the same total scale. Old tiles // with the same scale would become non-ideal and deleted on pending layers. - SetupDrawProperties(pending_layer(), 2.25f, 1.0f, 1.f, 2.25f, 2.25f, false); + SetupDrawProperties(pending_layer(), 2.25f, 1.0f, 1.f); pending_layer()->draw_properties().screen_space_transform.ConcatTransform( translate2); pending_layer()->draw_properties().target_space_transform = @@ -5797,7 +5837,7 @@ TEST_F(LegacySWPictureLayerImplTest, Region()); // Start with scale & translation of * 2.25 + (0.25, 0.5) on the active layer. - SetupDrawProperties(active_layer(), 2.25f, 1.5f, 1.f, 2.25f, 2.25f, false); + SetupDrawProperties(active_layer(), 2.25f, 1.5f, 1.f); gfx::Transform translate1; translate1.Translate(0.25f, 0.5f); active_layer()->draw_properties().screen_space_transform.ConcatTransform( @@ -5817,7 +5857,7 @@ TEST_F(LegacySWPictureLayerImplTest, } // Create a pending layer with the same scale but different translation. - SetupDrawProperties(pending_layer(), 2.25f, 1.5f, 1.f, 2.25f, 2.25f, false); + SetupDrawProperties(pending_layer(), 2.25f, 1.5f, 1.f); gfx::Transform translate2; translate2.Translate(0.75f, 0.25f); pending_layer()->draw_properties().screen_space_transform.ConcatTransform( diff --git a/chromium/cc/layers/picture_layer_unittest.cc b/chromium/cc/layers/picture_layer_unittest.cc index 94cd8ecc191..f0f15a7cbb2 100644 --- a/chromium/cc/layers/picture_layer_unittest.cc +++ b/chromium/cc/layers/picture_layer_unittest.cc @@ -160,8 +160,7 @@ TEST(PictureLayerTest, ClearVisibleRectWhenNoTiling) { gfx::Size layer_size(50, 50); FakeContentLayerClient client; client.set_bounds(layer_size); - client.add_draw_image(CreateDiscardablePaintImage(layer_size), gfx::Point(), - PaintFlags()); + client.add_draw_image(CreateDiscardablePaintImage(layer_size), gfx::Point()); scoped_refptr<PictureLayer> layer = PictureLayer::Create(&client); layer->SetBounds(gfx::Size(10, 10)); diff --git a/chromium/cc/layers/render_surface_impl.cc b/chromium/cc/layers/render_surface_impl.cc index a1eb58ca267..25d5ca89aa5 100644 --- a/chromium/cc/layers/render_surface_impl.cc +++ b/chromium/cc/layers/render_surface_impl.cc @@ -45,7 +45,7 @@ RenderSurfaceImpl::RenderSurfaceImpl(LayerTreeImpl* layer_tree_impl, ancestor_property_changed_(false), contributes_to_drawn_surface_(false), is_render_surface_list_member_(false), - can_use_cached_backdrop_filtered_result_(false), + intersects_damage_under_(true), nearest_occlusion_immune_ancestor_(nullptr) { damage_tracker_ = DamageTracker::Create(); } @@ -157,10 +157,19 @@ bool RenderSurfaceImpl::HasCopyRequest() const { return OwningEffectNode()->has_copy_request; } +viz::SubtreeCaptureId RenderSurfaceImpl::SubtreeCaptureId() const { + return OwningEffectNode()->subtree_capture_id; +} + bool RenderSurfaceImpl::ShouldCacheRenderSurface() const { return OwningEffectNode()->cache_render_surface; } +bool RenderSurfaceImpl::CopyOfOutputRequired() const { + return HasCopyRequest() || ShouldCacheRenderSurface() || + SubtreeCaptureId().is_valid(); +} + int RenderSurfaceImpl::TransformTreeIndex() const { return OwningEffectNode()->transform_id; } @@ -210,7 +219,7 @@ gfx::Rect RenderSurfaceImpl::CalculateExpandedClipForFilters( } gfx::Rect RenderSurfaceImpl::CalculateClippedAccumulatedContentRect() { - if (ShouldCacheRenderSurface() || HasCopyRequest() || !is_clipped()) + if (CopyOfOutputRequired() || !is_clipped()) return accumulated_content_rect(); if (accumulated_content_rect().IsEmpty()) @@ -377,6 +386,7 @@ RenderSurfaceImpl::CreateRenderPass() { pass->backdrop_filters = BackdropFilters(); pass->backdrop_filter_bounds = BackdropFilterBounds(); pass->generate_mipmap = TrilinearFiltering(); + pass->subtree_capture_id = SubtreeCaptureId(); pass->cache_render_pass = ShouldCacheRenderSurface(); pass->has_damage_from_contributing_content = HasDamageFromeContributingContent(); @@ -413,7 +423,7 @@ void RenderSurfaceImpl::AppendQuads(DrawMode draw_mode, } LayerImpl* mask_layer = BackdropMaskLayer(); - viz::ResourceId mask_resource_id = 0; + viz::ResourceId mask_resource_id = viz::kInvalidResourceId; gfx::Size mask_texture_size; gfx::RectF mask_uv_rect; gfx::Vector2dF surface_contents_scale = @@ -452,13 +462,12 @@ void RenderSurfaceImpl::AppendQuads(DrawMode draw_mode, gfx::RectF tex_coord_rect(gfx::Rect(content_rect().size())); auto* quad = render_pass->CreateAndAppendDrawQuad<viz::CompositorRenderPassDrawQuad>(); - quad->SetAll(shared_quad_state, content_rect(), unoccluded_content_rect, - /*needs_blending=*/true, render_pass_id(), mask_resource_id, - mask_uv_rect, mask_texture_size, surface_contents_scale, - gfx::PointF(), tex_coord_rect, - !layer_tree_impl_->settings().enable_edge_anti_aliasing, - OwningEffectNode()->backdrop_filter_quality, - can_use_cached_backdrop_filtered_result_); + quad->SetAll( + shared_quad_state, content_rect(), unoccluded_content_rect, + /*needs_blending=*/true, render_pass_id(), mask_resource_id, mask_uv_rect, + mask_texture_size, surface_contents_scale, gfx::PointF(), tex_coord_rect, + !layer_tree_impl_->settings().enable_edge_anti_aliasing, + OwningEffectNode()->backdrop_filter_quality, intersects_damage_under_); } } // namespace cc diff --git a/chromium/cc/layers/render_surface_impl.h b/chromium/cc/layers/render_surface_impl.h index 0dc37664806..105e04a7067 100644 --- a/chromium/cc/layers/render_surface_impl.h +++ b/chromium/cc/layers/render_surface_impl.h @@ -18,6 +18,7 @@ #include "cc/trees/property_tree.h" #include "components/viz/common/quads/compositor_render_pass.h" #include "components/viz/common/quads/shared_quad_state.h" +#include "components/viz/common/surfaces/subtree_capture_id.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/rect_f.h" #include "ui/gfx/mask_filter_info.h" @@ -122,14 +123,10 @@ class CC_EXPORT RenderSurfaceImpl { return is_render_surface_list_member_; } - void set_can_use_cached_backdrop_filtered_result( - bool can_use_cached_backdrop_filtered_result) { - can_use_cached_backdrop_filtered_result_ = - can_use_cached_backdrop_filtered_result; - } - bool can_use_cached_backdrop_filtered_result() const { - return can_use_cached_backdrop_filtered_result_; + void set_intersects_damage_under(bool intersects_damage_under) { + intersects_damage_under_ = intersects_damage_under; } + bool intersects_damage_under() const { return intersects_damage_under_; } void CalculateContentRectFromAccumulatedContentRect(int max_texture_size); void SetContentRectToViewport(); @@ -178,8 +175,15 @@ class CC_EXPORT RenderSurfaceImpl { bool HasCopyRequest() const; + viz::SubtreeCaptureId SubtreeCaptureId() const; + bool ShouldCacheRenderSurface() const; + // Returns true if it's required to copy the output of this surface (i.e. when + // it has copy requests, should be cached, or has a valid subtree capture ID), + // and should be e.g. immune from occlusion, etc. Returns false otherise. + bool CopyOfOutputRequired() const; + void ResetPropertyChangedFlags(); bool SurfacePropertyChanged() const; bool SurfacePropertyChangedOnlyFromDescendant() const; @@ -260,7 +264,7 @@ class CC_EXPORT RenderSurfaceImpl { bool contributes_to_drawn_surface_ : 1; bool is_render_surface_list_member_ : 1; - bool can_use_cached_backdrop_filtered_result_ : 1; + bool intersects_damage_under_ : 1; Occlusion occlusion_in_content_space_; diff --git a/chromium/cc/layers/render_surface_impl_unittest.cc b/chromium/cc/layers/render_surface_impl_unittest.cc index e5f5a504372..e4216685d59 100644 --- a/chromium/cc/layers/render_surface_impl_unittest.cc +++ b/chromium/cc/layers/render_surface_impl_unittest.cc @@ -119,7 +119,7 @@ TEST(RenderSurfaceLayerImplTest, AppendQuadsWithScaledMask) { // Mask layers don't use quad's mask functionality. EXPECT_EQ(gfx::RectF(), quad->mask_uv_rect); EXPECT_EQ(gfx::Vector2dF(2.f, 2.f), quad->filters_scale); - EXPECT_EQ(0u, quad->mask_resource_id()); + EXPECT_EQ(viz::kInvalidResourceId, quad->mask_resource_id()); } TEST(RenderSurfaceLayerImplTest, ResourcelessAppendQuadsSkipMask) { @@ -129,7 +129,7 @@ TEST(RenderSurfaceLayerImplTest, ResourcelessAppendQuadsSkipMask) { const viz::CompositorRenderPassDrawQuad* quad = viz::CompositorRenderPassDrawQuad::MaterialCast( render_pass->quad_list.front()); - EXPECT_EQ(0u, quad->mask_resource_id()); + EXPECT_EQ(viz::kInvalidResourceId, quad->mask_resource_id()); } TEST(RenderSurfaceLayerImplTest, diff --git a/chromium/cc/layers/render_surface_unittest.cc b/chromium/cc/layers/render_surface_unittest.cc index 250f71bd69f..ee59a342c1c 100644 --- a/chromium/cc/layers/render_surface_unittest.cc +++ b/chromium/cc/layers/render_surface_unittest.cc @@ -70,8 +70,9 @@ class FakePictureLayerImplForRenderSurfaceTest : public FakePictureLayerImpl { bool needs_blending = false; for (const auto& rect : quad_rects_) { auto* quad = render_pass->CreateAndAppendDrawQuad<viz::TileDrawQuad>(); - quad->SetNew(shared_quad_state, rect, rect, needs_blending, 0, - gfx::RectF(rect), bounds(), false, false, false); + quad->SetNew(shared_quad_state, rect, rect, needs_blending, + viz::kInvalidResourceId, gfx::RectF(rect), bounds(), false, + false, false); } } diff --git a/chromium/cc/layers/texture_layer_impl.cc b/chromium/cc/layers/texture_layer_impl.cc index 33bdf59fcc0..569643a891a 100644 --- a/chromium/cc/layers/texture_layer_impl.cc +++ b/chromium/cc/layers/texture_layer_impl.cc @@ -7,6 +7,8 @@ #include <stddef.h> #include <stdint.h> +#include <memory> +#include <utility> #include <vector> #include "base/logging.h" @@ -102,7 +104,7 @@ bool TextureLayerImpl::WillDraw( own_resource_ = false; } - return resource_id_; + return resource_id_ != viz::kInvalidResourceId; } void TextureLayerImpl::AppendQuads(viz::CompositorRenderPass* render_pass, @@ -289,7 +291,7 @@ void TextureLayerImpl::FreeTransferableResource() { DCHECK(!own_resource_); auto* resource_provider = layer_tree_impl()->resource_provider(); resource_provider->RemoveImportedResource(resource_id_); - resource_id_ = 0; + resource_id_ = viz::kInvalidResourceId; } } diff --git a/chromium/cc/layers/texture_layer_impl.h b/chromium/cc/layers/texture_layer_impl.h index d7cc7fed89f..6ab05573472 100644 --- a/chromium/cc/layers/texture_layer_impl.h +++ b/chromium/cc/layers/texture_layer_impl.h @@ -5,7 +5,9 @@ #ifndef CC_LAYERS_TEXTURE_LAYER_IMPL_H_ #define CC_LAYERS_TEXTURE_LAYER_IMPL_H_ +#include <memory> #include <string> +#include <vector> #include "base/callback.h" #include "base/containers/flat_map.h" @@ -100,7 +102,7 @@ class CC_EXPORT TextureLayerImpl : public LayerImpl { // Local ResourceId for the TransferableResource, to be used with the // compositor's viz::ClientResourceProvider in order to refer to the // TransferableResource given to it. - viz::ResourceId resource_id_ = 0; + viz::ResourceId resource_id_ = viz::kInvalidResourceId; std::unique_ptr<viz::SingleReleaseCallback> release_callback_; // As a pending layer, the set of SharedBitmapIds and the underlying diff --git a/chromium/cc/layers/texture_layer_unittest.cc b/chromium/cc/layers/texture_layer_unittest.cc index 3511e89eed6..fdc4f74623b 100644 --- a/chromium/cc/layers/texture_layer_unittest.cc +++ b/chromium/cc/layers/texture_layer_unittest.cc @@ -1004,6 +1004,7 @@ class TextureLayerChangeInvisibleMailboxTest ++prepare_called_; if (!resource_changed_) return false; + resource_changed_ = false; *resource = resource_; *release_callback = viz::SingleReleaseCallback::Create(base::BindOnce( &TextureLayerChangeInvisibleMailboxTest::ResourceReleased, diff --git a/chromium/cc/layers/ui_resource_layer_impl.cc b/chromium/cc/layers/ui_resource_layer_impl.cc index 40eb09f5b1f..48ba1eb42a6 100644 --- a/chromium/cc/layers/ui_resource_layer_impl.cc +++ b/chromium/cc/layers/ui_resource_layer_impl.cc @@ -4,6 +4,8 @@ #include "cc/layers/ui_resource_layer_impl.h" +#include <memory> + #include "base/strings/stringprintf.h" #include "base/trace_event/traced_value.h" #include "cc/base/math_util.h" @@ -102,7 +104,7 @@ void UIResourceLayerImpl::AppendQuads(viz::CompositorRenderPass* render_pass, viz::ResourceId resource = ui_resource_id_ ? layer_tree_impl()->ResourceIdForUIResource(ui_resource_id_) - : 0; + : viz::kInvalidResourceId; bool are_contents_opaque = resource ? (layer_tree_impl()->IsUIResourceOpaque(ui_resource_id_) || contents_opaque()) diff --git a/chromium/cc/layers/video_layer_impl_unittest.cc b/chromium/cc/layers/video_layer_impl_unittest.cc index 00af2c84846..6a205ea443b 100644 --- a/chromium/cc/layers/video_layer_impl_unittest.cc +++ b/chromium/cc/layers/video_layer_impl_unittest.cc @@ -395,7 +395,7 @@ TEST(VideoLayerImplTest, NativeYUVFrameGeneratesYUVQuad) { gfx::Size(10, 10), gfx::Rect(10, 10), gfx::Size(10, 10), base::TimeDelta()); ASSERT_TRUE(video_frame); - video_frame->metadata()->allow_overlay = true; + video_frame->metadata().allow_overlay = true; FakeVideoFrameProvider provider; provider.set_frame(video_frame); @@ -438,7 +438,7 @@ TEST(VideoLayerImplTest, NativeARGBFrameGeneratesTextureQuad) { media::PIXEL_FORMAT_ARGB, mailbox_holders, base::DoNothing(), resource_size, gfx::Rect(10, 10), resource_size, base::TimeDelta()); ASSERT_TRUE(video_frame); - video_frame->metadata()->allow_overlay = true; + video_frame->metadata().allow_overlay = true; FakeVideoFrameProvider provider; provider.set_frame(video_frame); diff --git a/chromium/cc/metrics/OWNERS b/chromium/cc/metrics/OWNERS index 0cb4f83e792..39db757e54a 100644 --- a/chromium/cc/metrics/OWNERS +++ b/chromium/cc/metrics/OWNERS @@ -1,2 +1,4 @@ per-file ukm_smoothness_data.h=set noparent per-file ukm_smoothness_data.h=file://ipc/SECURITY_OWNERS +per-file shared_metrics_buffer.h=set noparent +per-file shared_metrics_buffer.h=file://ipc/SECURITY_OWNERS diff --git a/chromium/cc/metrics/compositor_frame_reporter.cc b/chromium/cc/metrics/compositor_frame_reporter.cc index 93d0e2352a7..fcad7a8cd4e 100644 --- a/chromium/cc/metrics/compositor_frame_reporter.cc +++ b/chromium/cc/metrics/compositor_frame_reporter.cc @@ -75,6 +75,102 @@ constexpr const char* GetVizBreakdownName(VizBreakdown stage) { } } +// Returns the name of the event dispatch breakdown of EventLatency histograms +// between `start_stage` and `end_stage`. +constexpr const char* GetEventLatencyDispatchBreakdownName( + EventMetrics::DispatchStage start_stage, + EventMetrics::DispatchStage end_stage) { + switch (start_stage) { + case EventMetrics::DispatchStage::kGenerated: + DCHECK_EQ(end_stage, + EventMetrics::DispatchStage::kArrivedInRendererCompositor); + return "GenerationToRendererCompositor"; + case EventMetrics::DispatchStage::kArrivedInRendererCompositor: + switch (end_stage) { + case EventMetrics::DispatchStage::kRendererCompositorStarted: + return "RendererCompositorQueueingDelay"; + case EventMetrics::DispatchStage::kRendererMainStarted: + return "RendererCompositorToMain"; + default: + NOTREACHED(); + return nullptr; + } + case EventMetrics::DispatchStage::kRendererCompositorStarted: + DCHECK_EQ(end_stage, + EventMetrics::DispatchStage::kRendererCompositorFinished); + return "RendererCompositorProcessing"; + case EventMetrics::DispatchStage::kRendererCompositorFinished: + DCHECK_EQ(end_stage, EventMetrics::DispatchStage::kRendererMainStarted); + return "RendererCompositorToMain"; + case EventMetrics::DispatchStage::kRendererMainStarted: + DCHECK_EQ(end_stage, EventMetrics::DispatchStage::kRendererMainFinished); + return "RendererMainProcessing"; + case EventMetrics::DispatchStage::kRendererMainFinished: + NOTREACHED(); + return nullptr; + } +} + +// Returns the name of EventLatency breakdown between `dispatch_stage` and +// `compositor_stage`. +constexpr const char* GetEventLatencyDispatchToCompositorBreakdownName( + EventMetrics::DispatchStage dispatch_stage, + CompositorFrameReporter::StageType compositor_stage) { + switch (dispatch_stage) { + case EventMetrics::DispatchStage::kRendererCompositorFinished: + switch (compositor_stage) { + case CompositorFrameReporter::StageType:: + kBeginImplFrameToSendBeginMainFrame: + return "RendererCompositorFinishedToBeginImplFrame"; + case CompositorFrameReporter::StageType::kSendBeginMainFrameToCommit: + return "RendererCompositorFinishedToSendBeginMainFrame"; + case CompositorFrameReporter::StageType::kCommit: + return "RendererCompositorFinishedToCommit"; + case CompositorFrameReporter::StageType::kEndCommitToActivation: + return "RendererCompositorFinishedToEndCommit"; + case CompositorFrameReporter::StageType::kActivation: + return "RendererCompositorFinishedToActivation"; + case CompositorFrameReporter::StageType:: + kEndActivateToSubmitCompositorFrame: + return "RendererCompositorFinishedToEndActivate"; + case CompositorFrameReporter::StageType:: + kSubmitCompositorFrameToPresentationCompositorFrame: + return "RendererCompositorFinishedToSubmitCompositorFrame"; + default: + NOTREACHED(); + return nullptr; + } + break; + case EventMetrics::DispatchStage::kRendererMainFinished: + switch (compositor_stage) { + case CompositorFrameReporter::StageType:: + kBeginImplFrameToSendBeginMainFrame: + return "RendererMainFinishedToBeginImplFrame"; + case CompositorFrameReporter::StageType::kSendBeginMainFrameToCommit: + return "RendererMainFinishedToSendBeginMainFrame"; + case CompositorFrameReporter::StageType::kCommit: + return "RendererMainFinishedToCommit"; + case CompositorFrameReporter::StageType::kEndCommitToActivation: + return "RendererMainFinishedToEndCommit"; + case CompositorFrameReporter::StageType::kActivation: + return "RendererMainFinishedToActivation"; + case CompositorFrameReporter::StageType:: + kEndActivateToSubmitCompositorFrame: + return "RendererMainFinishedToEndActivate"; + case CompositorFrameReporter::StageType:: + kSubmitCompositorFrameToPresentationCompositorFrame: + return "RendererMainFinishedToSubmitCompositorFrame"; + default: + NOTREACHED(); + return nullptr; + } + break; + default: + NOTREACHED(); + return nullptr; + } +} + // Names for CompositorFrameReporter::StageType, which should be updated in case // of changes to the enum. constexpr const char* GetStageName(int stage_type_index, @@ -201,10 +297,8 @@ constexpr int kEventLatencyEventTypeCount = static_cast<int>(EventMetrics::EventType::kMaxValue) + 1; constexpr int kEventLatencyScrollTypeCount = static_cast<int>(EventMetrics::ScrollType::kMaxValue) + 1; -constexpr int kMaxEventLatencyHistogramBaseIndex = - kEventLatencyEventTypeCount * kEventLatencyScrollTypeCount; constexpr int kMaxEventLatencyHistogramIndex = - kMaxEventLatencyHistogramBaseIndex * (kStageTypeCount + kAllBreakdownCount); + kEventLatencyEventTypeCount * kEventLatencyScrollTypeCount; constexpr base::TimeDelta kEventLatencyHistogramMin = base::TimeDelta::FromMicroseconds(1); constexpr base::TimeDelta kEventLatencyHistogramMax = @@ -241,37 +335,203 @@ base::TimeTicks ComputeSafeDeadlineForFrame(const viz::BeginFrameArgs& args) { return args.frame_time + (args.interval * 1.5); } -#define REPORT_VIZ_TRACE_EVENT(start_time, end_time, index, trace_func) \ - if (start_time <= end_time) { \ - const char* substage_name = \ - GetVizBreakdownName(static_cast<VizBreakdown>(index)); \ - trace_func(start_time, end_time, substage_name); \ +bool IsScrollActive(const CompositorFrameReporter::ActiveTrackers& trackers) { + return trackers.test( + static_cast<size_t>(FrameSequenceTrackerType::kWheelScroll)) || + trackers.test( + static_cast<size_t>(FrameSequenceTrackerType::kTouchScroll)) || + trackers.test( + static_cast<size_t>(FrameSequenceTrackerType::kScrollbarScroll)); +} + +} // namespace + +// CompositorFrameReporter::ProcessedBlinkBreakdown::Iterator ================== + +CompositorFrameReporter::ProcessedBlinkBreakdown::Iterator::Iterator( + const ProcessedBlinkBreakdown* owner) + : owner_(owner) {} + +CompositorFrameReporter::ProcessedBlinkBreakdown::Iterator::~Iterator() = + default; + +bool CompositorFrameReporter::ProcessedBlinkBreakdown::Iterator::IsValid() + const { + return index_ < base::size(owner_->list_); +} + +void CompositorFrameReporter::ProcessedBlinkBreakdown::Iterator::Advance() { + DCHECK(IsValid()); + index_++; +} + +BlinkBreakdown +CompositorFrameReporter::ProcessedBlinkBreakdown::Iterator::GetBreakdown() + const { + DCHECK(IsValid()); + return static_cast<BlinkBreakdown>(index_); +} + +base::TimeDelta +CompositorFrameReporter::ProcessedBlinkBreakdown::Iterator::GetLatency() const { + DCHECK(IsValid()); + return owner_->list_[index_]; +} + +// CompositorFrameReporter::ProcessedBlinkBreakdown ============================ + +CompositorFrameReporter::ProcessedBlinkBreakdown::ProcessedBlinkBreakdown( + base::TimeTicks blink_start_time, + base::TimeTicks begin_main_frame_start, + const BeginMainFrameMetrics& blink_breakdown) { + if (blink_start_time.is_null()) + return; + + list_[static_cast<int>(BlinkBreakdown::kHandleInputEvents)] = + blink_breakdown.handle_input_events; + list_[static_cast<int>(BlinkBreakdown::kAnimate)] = blink_breakdown.animate; + list_[static_cast<int>(BlinkBreakdown::kStyleUpdate)] = + blink_breakdown.style_update; + list_[static_cast<int>(BlinkBreakdown::kLayoutUpdate)] = + blink_breakdown.layout_update; + list_[static_cast<int>(BlinkBreakdown::kPrepaint)] = blink_breakdown.prepaint; + list_[static_cast<int>(BlinkBreakdown::kCompositingInputs)] = + blink_breakdown.compositing_inputs; + list_[static_cast<int>(BlinkBreakdown::kCompositingAssignments)] = + blink_breakdown.compositing_assignments; + list_[static_cast<int>(BlinkBreakdown::kPaint)] = blink_breakdown.paint; + list_[static_cast<int>(BlinkBreakdown::kCompositeCommit)] = + blink_breakdown.composite_commit; + list_[static_cast<int>(BlinkBreakdown::kUpdateLayers)] = + blink_breakdown.update_layers; + list_[static_cast<int>(BlinkBreakdown::kBeginMainSentToStarted)] = + begin_main_frame_start - blink_start_time; +} + +CompositorFrameReporter::ProcessedBlinkBreakdown::~ProcessedBlinkBreakdown() = + default; + +CompositorFrameReporter::ProcessedBlinkBreakdown::Iterator +CompositorFrameReporter::ProcessedBlinkBreakdown::CreateIterator() const { + return Iterator(this); +} + +// CompositorFrameReporter::ProcessedVizBreakdown::Iterator ==================== + +CompositorFrameReporter::ProcessedVizBreakdown::Iterator::Iterator( + const ProcessedVizBreakdown* owner, + bool skip_swap_start_to_swap_end) + : owner_(owner), skip_swap_start_to_swap_end_(skip_swap_start_to_swap_end) { + DCHECK(owner_); +} + +CompositorFrameReporter::ProcessedVizBreakdown::Iterator::~Iterator() = default; + +bool CompositorFrameReporter::ProcessedVizBreakdown::Iterator::IsValid() const { + return index_ < base::size(owner_->list_) && owner_->list_[index_]; +} + +void CompositorFrameReporter::ProcessedVizBreakdown::Iterator::Advance() { + DCHECK(IsValid()); + index_++; + if (static_cast<VizBreakdown>(index_) == VizBreakdown::kSwapStartToSwapEnd && + skip_swap_start_to_swap_end_) { + index_++; } +} + +VizBreakdown +CompositorFrameReporter::ProcessedVizBreakdown::Iterator::GetBreakdown() const { + DCHECK(IsValid()); + return static_cast<VizBreakdown>(index_); +} + +base::TimeTicks +CompositorFrameReporter::ProcessedVizBreakdown::Iterator::GetStartTime() const { + DCHECK(IsValid()); + return owner_->list_[index_]->first; +} + +base::TimeTicks +CompositorFrameReporter::ProcessedVizBreakdown::Iterator::GetEndTime() const { + DCHECK(IsValid()); + return owner_->list_[index_]->second; +} -#define REPORT_VIZ_BREAKDOWN_TRACES(trace_func) \ - size_t start_to_buffer_available = \ - static_cast<size_t>(VizBreakdown::kSwapStartToBufferAvailable); \ - bool has_ready_timings = !!viz_breakdown_list_[start_to_buffer_available]; \ - for (size_t i = 0; i < start_to_buffer_available; i++) { \ - if (!viz_breakdown_list_[i]) { \ - break; \ - } \ - if (i == static_cast<size_t>(VizBreakdown::kSwapStartToSwapEnd) && \ - has_ready_timings) { \ - size_t latch_to_swap_end = \ - static_cast<size_t>(VizBreakdown::kLatchToSwapEnd); \ - for (size_t j = start_to_buffer_available; j <= latch_to_swap_end; \ - j++) { \ - REPORT_VIZ_TRACE_EVENT(viz_breakdown_list_[j]->first, \ - viz_breakdown_list_[j]->second, j, trace_func); \ - } \ - } else { \ - REPORT_VIZ_TRACE_EVENT(viz_breakdown_list_[i]->first, \ - viz_breakdown_list_[i]->second, i, trace_func); \ - } \ +base::TimeDelta +CompositorFrameReporter::ProcessedVizBreakdown::Iterator::GetDuration() const { + DCHECK(IsValid()); + return owner_->list_[index_]->second - owner_->list_[index_]->first; +} + +// CompositorFrameReporter::ProcessedVizBreakdown ============================== + +CompositorFrameReporter::ProcessedVizBreakdown::ProcessedVizBreakdown( + base::TimeTicks viz_start_time, + const viz::FrameTimingDetails& viz_breakdown) { + if (viz_start_time.is_null()) + return; + + // Check if `viz_breakdown` is set. Testing indicates that sometimes the + // received_compositor_frame_timestamp can be earlier than the given + // `viz_start_time`. Avoid reporting negative times. + if (viz_breakdown.received_compositor_frame_timestamp.is_null() || + viz_breakdown.received_compositor_frame_timestamp < viz_start_time) { + return; } + list_[static_cast<int>(VizBreakdown::kSubmitToReceiveCompositorFrame)] = + std::make_pair(viz_start_time, + viz_breakdown.received_compositor_frame_timestamp); -} // namespace + if (viz_breakdown.draw_start_timestamp.is_null()) + return; + list_[static_cast<int>(VizBreakdown::kReceivedCompositorFrameToStartDraw)] = + std::make_pair(viz_breakdown.received_compositor_frame_timestamp, + viz_breakdown.draw_start_timestamp); + + if (viz_breakdown.swap_timings.is_null()) + return; + list_[static_cast<int>(VizBreakdown::kStartDrawToSwapStart)] = + std::make_pair(viz_breakdown.draw_start_timestamp, + viz_breakdown.swap_timings.swap_start); + + list_[static_cast<int>(VizBreakdown::kSwapStartToSwapEnd)] = + std::make_pair(viz_breakdown.swap_timings.swap_start, + viz_breakdown.swap_timings.swap_end); + + list_[static_cast<int>(VizBreakdown::kSwapEndToPresentationCompositorFrame)] = + std::make_pair(viz_breakdown.swap_timings.swap_end, + viz_breakdown.presentation_feedback.timestamp); + swap_start_ = viz_breakdown.swap_timings.swap_start; + + if (viz_breakdown.presentation_feedback.ready_timestamp.is_null()) + return; + buffer_ready_available_ = true; + list_[static_cast<int>(VizBreakdown::kSwapStartToBufferAvailable)] = + std::make_pair(viz_breakdown.swap_timings.swap_start, + viz_breakdown.presentation_feedback.available_timestamp); + list_[static_cast<int>(VizBreakdown::kBufferAvailableToBufferReady)] = + std::make_pair(viz_breakdown.presentation_feedback.available_timestamp, + viz_breakdown.presentation_feedback.ready_timestamp); + list_[static_cast<int>(VizBreakdown::kBufferReadyToLatch)] = + std::make_pair(viz_breakdown.presentation_feedback.ready_timestamp, + viz_breakdown.presentation_feedback.latch_timestamp); + list_[static_cast<int>(VizBreakdown::kLatchToSwapEnd)] = + std::make_pair(viz_breakdown.presentation_feedback.latch_timestamp, + viz_breakdown.swap_timings.swap_end); +} + +CompositorFrameReporter::ProcessedVizBreakdown::~ProcessedVizBreakdown() = + default; + +CompositorFrameReporter::ProcessedVizBreakdown::Iterator +CompositorFrameReporter::ProcessedVizBreakdown::CreateIterator( + bool skip_swap_start_to_swap_end_if_breakdown_available) const { + return Iterator(this, skip_swap_start_to_swap_end_if_breakdown_available && + buffer_ready_available_); +} + +// CompositorFrameReporter ===================================================== CompositorFrameReporter::CompositorFrameReporter( const ActiveTrackers& active_trackers, @@ -288,11 +548,15 @@ CompositorFrameReporter::CompositorFrameReporter( dropped_frame_counter_(dropped_frame_counter), smooth_thread_(smooth_thread), layer_tree_host_id_(layer_tree_host_id) { - dropped_frame_counter_->OnBeginFrame(args); + dropped_frame_counter_->OnBeginFrame(args, IsScrollActive(active_trackers_)); } std::unique_ptr<CompositorFrameReporter> CompositorFrameReporter::CopyReporterAtBeginImplStage() { + // If |this| reporter is dependent on another reporter to decide about partial + // update, then |this| should not have any such dependents. + DCHECK(!partial_update_decider_); + if (stage_history_.empty() || stage_history_.front().stage_type != StageType::kBeginImplFrameToSendBeginMainFrame || @@ -309,11 +573,11 @@ CompositorFrameReporter::CopyReporterAtBeginImplStage() { StageType::kBeginImplFrameToSendBeginMainFrame; new_reporter->current_stage_.start_time = stage_history_.front().start_time; new_reporter->set_tick_clock(tick_clock_); - new_reporter->cloned_from_ = weak_factory_.GetWeakPtr(); - // TODO(https://crbug.com/1127872) Check |cloned_to_| is null before replacing - // it. - cloned_to_ = new_reporter->GetWeakPtr(); + // Set up the new reporter so that it depends on |this| for partial update + // information. + new_reporter->SetPartialUpdateDecider(weak_factory_.GetWeakPtr()); + return new_reporter; } @@ -418,8 +682,10 @@ void CompositorFrameReporter::TerminateReporter() { if (frame_termination_status_ == FrameTerminationStatus::kUnknown) TerminateFrame(FrameTerminationStatus::kUnknown, Now()); - PopulateBlinkBreakdownList(); - PopulateVizBreakdownList(); + processed_blink_breakdown_ = std::make_unique<ProcessedBlinkBreakdown>( + blink_start_time_, begin_main_frame_start_, blink_breakdown_); + processed_viz_breakdown_ = + std::make_unique<ProcessedVizBreakdown>(viz_start_time_, viz_breakdown_); DCHECK_EQ(current_stage_.start_time, base::TimeTicks()); switch (frame_termination_status_) { @@ -434,21 +700,34 @@ void CompositorFrameReporter::TerminateReporter() { case FrameTerminationStatus::kReplacedByNewReporter: EnableReportType(FrameReportType::kDroppedFrame); break; - case FrameTerminationStatus::kDidNotProduceFrame: - if (frame_skip_reason_.has_value() && - frame_skip_reason() == FrameSkippedReason::kNoDamage) { - // If this reporter was cloned, and the cloned repoter was marked as + case FrameTerminationStatus::kDidNotProduceFrame: { + const bool no_update_from_main = + frame_skip_reason_.has_value() && + frame_skip_reason() == FrameSkippedReason::kNoDamage; + const bool no_update_from_compositor = + !has_partial_update_ && frame_skip_reason_.has_value() && + frame_skip_reason() == FrameSkippedReason::kWaitingOnMain; + + if (no_update_from_main) { + // If this reporter was cloned, and the cloned reporter was marked as // containing 'partial update' (i.e. missing desired updates from the // main-thread), but this reporter terminated with 'no damage', then - // reset the 'partial update' flag from the cloned reporter. - if (cloned_to_ && cloned_to_->has_partial_update()) - cloned_to_->set_has_partial_update(false); - } else { - // If no frames were produced, it was not due to no-damage, then it is a - // dropped frame. + // reset the 'partial update' flag from the cloned reporter (as well as + // other depending reporters). + while (!partial_update_dependents_.empty()) { + auto dependent = partial_update_dependents_.front(); + if (dependent) + dependent->set_has_partial_update(false); + partial_update_dependents_.pop(); + } + } else if (!no_update_from_compositor) { + // If rather main thread has damage or compositor thread has partial + // damage, then it's a dropped frame. EnableReportType(FrameReportType::kDroppedFrame); } + break; + } case FrameTerminationStatus::kUnknown: break; } @@ -485,6 +764,11 @@ void CompositorFrameReporter::TerminateReporter() { dropped_frame_counter_->OnEndFrame(args_, IsDroppedFrameAffectingSmoothness()); } + + if (discarded_partial_update_dependents_count_ > 0) + UMA_HISTOGRAM_CUSTOM_COUNTS( + "Graphics.Smoothness.Diagnostic.DiscardedDependentCount", + discarded_partial_update_dependents_count_, 1, 1000, 50); } void CompositorFrameReporter::EndCurrentStage(base::TimeTicks end_time) { @@ -513,7 +797,8 @@ void CompositorFrameReporter::ReportCompositorLatencyHistograms() const { UMA_HISTOGRAM_ENUMERATION("CompositorLatency.Type", report_type); if (latency_ukm_reporter_) { latency_ukm_reporter_->ReportCompositorLatencyUkm( - report_type, stage_history_, active_trackers_, viz_breakdown_); + report_type, stage_history_, active_trackers_, + *processed_blink_breakdown_, *processed_viz_breakdown_); } bool any_active_interaction = false; for (size_t fst_type = 0; fst_type < active_trackers_.size(); ++fst_type) { @@ -601,29 +886,25 @@ void CompositorFrameReporter::ReportStageHistogramWithBreakdown( void CompositorFrameReporter::ReportCompositorLatencyBlinkBreakdowns( FrameSequenceTrackerType frame_sequence_tracker_type) const { - for (size_t i = 0; i < base::size(blink_breakdown_list_); i++) { - ReportCompositorLatencyHistogram(frame_sequence_tracker_type, - kBlinkBreakdownInitialIndex + i, - blink_breakdown_list_[i]); + DCHECK(processed_blink_breakdown_); + for (auto it = processed_blink_breakdown_->CreateIterator(); it.IsValid(); + it.Advance()) { + ReportCompositorLatencyHistogram( + frame_sequence_tracker_type, + kBlinkBreakdownInitialIndex + static_cast<size_t>(it.GetBreakdown()), + it.GetLatency()); } } void CompositorFrameReporter::ReportCompositorLatencyVizBreakdowns( FrameSequenceTrackerType frame_sequence_tracker_type) const { - for (size_t i = 0; i < base::size(viz_breakdown_list_); i++) { - if (!viz_breakdown_list_[i]) { -#if DCHECK_IS_ON() - // Remaining breakdowns should be unset. - for (; i < base::size(viz_breakdown_list_); i++) - DCHECK(!viz_breakdown_list_[i]); -#endif - break; - } - const base::TimeTicks start_time = viz_breakdown_list_[i]->first; - const base::TimeTicks end_time = viz_breakdown_list_[i]->second; - ReportCompositorLatencyHistogram(frame_sequence_tracker_type, - kVizBreakdownInitialIndex + i, - end_time - start_time); + DCHECK(processed_viz_breakdown_); + for (auto it = processed_viz_breakdown_->CreateIterator(false); it.IsValid(); + it.Advance()) { + ReportCompositorLatencyHistogram( + frame_sequence_tracker_type, + kVizBreakdownInitialIndex + static_cast<size_t>(it.GetBreakdown()), + it.GetDuration()); } } @@ -670,8 +951,16 @@ void CompositorFrameReporter::ReportCompositorLatencyHistogram( } void CompositorFrameReporter::ReportEventLatencyHistograms() const { + const StageData& total_latency_stage = stage_history_.back(); + DCHECK_EQ(StageType::kTotalLatency, total_latency_stage.stage_type); + + const std::string total_latency_stage_name = + GetStageName(static_cast<int>(StageType::kTotalLatency)); + const std::string total_latency_histogram_name = + "EventLatency." + total_latency_stage_name; + for (const auto& event_metrics : events_metrics_) { - DCHECK_NE(event_metrics, nullptr); + DCHECK(event_metrics); const std::string histogram_base_name = GetEventLatencyHistogramBaseName(*event_metrics); const int event_type_index = static_cast<int>(event_metrics->type()); @@ -679,156 +968,67 @@ void CompositorFrameReporter::ReportEventLatencyHistograms() const { event_metrics->scroll_type() ? static_cast<int>(*event_metrics->scroll_type()) : 0; - const int histogram_base_index = + const int event_histogram_index = event_type_index * kEventLatencyScrollTypeCount + scroll_type_index; + const base::TimeTicks generated_timestamp = + event_metrics->GetDispatchStageTimestamp( + EventMetrics::DispatchStage::kGenerated); + DCHECK_LT(generated_timestamp, total_latency_stage.end_time); + // For scroll events, report total latency up to gpu-swap-begin. This is // useful in comparing new EventLatency metrics with LatencyInfo-based // scroll event latency metrics. - if (event_metrics->scroll_type() && + if (event_metrics->ShouldReportScrollingTotalLatency() && !viz_breakdown_.swap_timings.is_null()) { const base::TimeDelta swap_begin_latency = - viz_breakdown_.swap_timings.swap_start - event_metrics->time_stamp(); - const std::string swap_begin_histogram_name = + viz_breakdown_.swap_timings.swap_start - generated_timestamp; + const std::string event_swap_begin_histogram_name = histogram_base_name + ".TotalLatencyToSwapBegin"; - // Note: There's a 1:1 mapping between `histogram_base_index` and - // `swap_begin_histogram_name` which allows the use of + // Note: There's a 1:1 mapping between `event_histogram_index` and + // `event_swap_begin_histogram_name` which allows the use of // `STATIC_HISTOGRAM_POINTER_GROUP()` to cache histogram objects. STATIC_HISTOGRAM_POINTER_GROUP( - swap_begin_histogram_name, histogram_base_index, - kMaxEventLatencyHistogramBaseIndex, + event_swap_begin_histogram_name, event_histogram_index, + kMaxEventLatencyHistogramIndex, AddTimeMicrosecondsGranularity(swap_begin_latency), base::Histogram::FactoryMicrosecondsTimeGet( - swap_begin_histogram_name, kEventLatencyHistogramMin, + event_swap_begin_histogram_name, kEventLatencyHistogramMin, kEventLatencyHistogramMax, kEventLatencyHistogramBucketCount, base::HistogramBase::kUmaTargetedHistogramFlag)); } - // It is possible for an event to arrive in the compositor in the middle of - // a frame (e.g. the browser received the event *after* renderer received a - // begin-impl, and the event reached the compositor before that frame - // ended). To handle such cases, find the first stage that happens after the - // event's arrival in the browser. - auto stage_it = - std::find_if(stage_history_.begin(), stage_history_.end(), - [&event_metrics](const StageData& stage) { - return stage.start_time > event_metrics->time_stamp(); - }); - // TODO(crbug.com/1079116): Ideally, at least the start time of - // SubmitCompositorFrameToPresentationCompositorFrame stage should be - // greater than the event time stamp, but apparently, this is not always the - // case (see crbug.com/1093698). For now, skip to the next event in such - // cases. Hopefully, the work to reduce discrepancies between the new - // EventLatency and the old Event.Latency metrics would fix this issue. If - // not, we need to reconsider investigating this issue. - if (stage_it == stage_history_.end()) - continue; - - const base::TimeDelta b2r_latency = - stage_it->start_time - event_metrics->time_stamp(); - const std::string b2r_histogram_name = - histogram_base_name + ".BrowserToRendererCompositor"; - // Note: There's a 1:1 mapping between `histogram_base_index` and - // `b2r_histogram_name` which allows the use of + // Report total latency up to presentation for the event. + const base::TimeDelta total_latency = + total_latency_stage.end_time - generated_timestamp; + const std::string event_total_latency_histogram_name = + base::StrCat({histogram_base_name, ".", total_latency_stage_name}); + // Note: There's a 1:1 mapping between `event_histogram_index` and + // `event_total_latency_histogram_name` which allows the use of // `STATIC_HISTOGRAM_POINTER_GROUP()` to cache histogram objects. STATIC_HISTOGRAM_POINTER_GROUP( - b2r_histogram_name, histogram_base_index, - kMaxEventLatencyHistogramBaseIndex, - AddTimeMicrosecondsGranularity(b2r_latency), + event_total_latency_histogram_name, event_histogram_index, + kMaxEventLatencyHistogramIndex, + AddTimeMicrosecondsGranularity(total_latency), base::Histogram::FactoryMicrosecondsTimeGet( - b2r_histogram_name, kEventLatencyHistogramMin, + event_total_latency_histogram_name, kEventLatencyHistogramMin, kEventLatencyHistogramMax, kEventLatencyHistogramBucketCount, base::HistogramBase::kUmaTargetedHistogramFlag)); - for (; stage_it != stage_history_.end(); ++stage_it) { - // Total latency is calculated since the event timestamp. - const base::TimeTicks start_time = - stage_it->stage_type == StageType::kTotalLatency - ? event_metrics->time_stamp() - : stage_it->start_time; - const base::TimeDelta latency = stage_it->end_time - start_time; - const int stage_type_index = static_cast<int>(stage_it->stage_type); - ReportEventLatencyHistogram(histogram_base_index, histogram_base_name, - stage_type_index, latency); - - switch (stage_it->stage_type) { - case StageType::kSendBeginMainFrameToCommit: - ReportEventLatencyBlinkBreakdowns(histogram_base_index, - histogram_base_name); - break; - case StageType::kSubmitCompositorFrameToPresentationCompositorFrame: - ReportEventLatencyVizBreakdowns(histogram_base_index, - histogram_base_name); - break; - case StageType::kTotalLatency: - UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES( - "EventLatency.TotalLatency", latency, kEventLatencyHistogramMin, - kEventLatencyHistogramMax, kEventLatencyHistogramBucketCount); - break; - default: - break; - } - } + // Also, report total latency up to presentation for all event types in an + // aggregate histogram. + UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES( + total_latency_histogram_name, total_latency, kEventLatencyHistogramMin, + kEventLatencyHistogramMax, kEventLatencyHistogramBucketCount); } if (latency_ukm_reporter_) { latency_ukm_reporter_->ReportEventLatencyUkm( - events_metrics_, stage_history_, viz_breakdown_); + events_metrics_, stage_history_, *processed_blink_breakdown_, + *processed_viz_breakdown_); } } -void CompositorFrameReporter::ReportEventLatencyBlinkBreakdowns( - int histogram_base_index, - const std::string& histogram_base_name) const { - for (size_t i = 0; i < base::size(blink_breakdown_list_); i++) { - ReportEventLatencyHistogram(histogram_base_index, histogram_base_name, - kBlinkBreakdownInitialIndex + i, - blink_breakdown_list_[i]); - } -} - -void CompositorFrameReporter::ReportEventLatencyVizBreakdowns( - int histogram_base_index, - const std::string& histogram_base_name) const { - for (size_t i = 0; i < base::size(viz_breakdown_list_); i++) { - if (!viz_breakdown_list_[i]) { -#if DCHECK_IS_ON() - // Remaining breakdowns should be unset. - for (; i < base::size(viz_breakdown_list_); i++) - DCHECK(!viz_breakdown_list_[i]); -#endif - break; - } - const base::TimeTicks start_time = viz_breakdown_list_[i]->first; - const base::TimeTicks end_time = viz_breakdown_list_[i]->second; - ReportEventLatencyHistogram(histogram_base_index, histogram_base_name, - kVizBreakdownInitialIndex + i, - end_time - start_time); - } -} - -void CompositorFrameReporter::ReportEventLatencyHistogram( - int histogram_base_index, - const std::string& histogram_base_name, - int stage_type_index, - base::TimeDelta latency) const { - const std::string histogram_name = - base::StrCat({histogram_base_name, ".", GetStageName(stage_type_index)}); - const int histogram_index = - histogram_base_index * (kStageTypeCount + kAllBreakdownCount) + - stage_type_index; - // Note: There's a 1:1 mapping between `histogram_index` and `histogram_name` - // which allows the use of `STATIC_HISTOGRAM_POINTER_GROUP()` to cache - // histogram objects. - STATIC_HISTOGRAM_POINTER_GROUP( - histogram_name, histogram_index, kMaxEventLatencyHistogramIndex, - AddTimeMicrosecondsGranularity(latency), - base::Histogram::FactoryMicrosecondsTimeGet( - histogram_name, kEventLatencyHistogramMin, kEventLatencyHistogramMax, - kEventLatencyHistogramBucketCount, - base::HistogramBase::kUmaTargetedHistogramFlag)); -} - void CompositorFrameReporter::ReportCompositorLatencyTraceEvents() const { if (stage_history_.empty()) return; @@ -840,8 +1040,8 @@ void CompositorFrameReporter::ReportCompositorLatencyTraceEvents() const { const auto trace_track = perfetto::Track(reinterpret_cast<uint64_t>(this)); TRACE_EVENT_BEGIN( - "cc,benchmark", "PipelineReporter", trace_track, - stage_history_.front().start_time, [&](perfetto::EventContext context) { + "cc,benchmark", "PipelineReporter", trace_track, args_.frame_time, + [&](perfetto::EventContext context) { using perfetto::protos::pbzero::ChromeFrameReporter; bool frame_dropped = TestReportType(FrameReportType::kDroppedFrame); ChromeFrameReporter::State state; @@ -851,7 +1051,7 @@ void CompositorFrameReporter::ReportCompositorLatencyTraceEvents() const { FrameTerminationStatus::kDidNotProduceFrame) { state = ChromeFrameReporter::STATE_NO_UPDATE_DESIRED; } else { - state = has_partial_update() + state = has_partial_update_ ? ChromeFrameReporter::STATE_PRESENTED_PARTIAL : ChromeFrameReporter::STATE_PRESENTED_ALL; } @@ -881,17 +1081,23 @@ void CompositorFrameReporter::ReportCompositorLatencyTraceEvents() const { if (stage.start_time == stage.end_time) continue; const char* stage_name = GetStageName(stage_type_index); - TRACE_EVENT_BEGIN("cc,benchmark", stage_name, trace_track, - stage.start_time); + TRACE_EVENT_BEGIN("cc,benchmark", perfetto::StaticString{stage_name}, + trace_track, stage.start_time); if (stage.stage_type == StageType::kSubmitCompositorFrameToPresentationCompositorFrame) { - REPORT_VIZ_BREAKDOWN_TRACES([&](base::TimeTicks start_time, - base::TimeTicks end_time, - const char* substage_name) { - TRACE_EVENT_BEGIN("cc,benchmark", substage_name, trace_track, + DCHECK(processed_viz_breakdown_); + for (auto it = processed_viz_breakdown_->CreateIterator(true); + it.IsValid(); it.Advance()) { + base::TimeTicks start_time = it.GetStartTime(); + base::TimeTicks end_time = it.GetEndTime(); + if (start_time >= end_time) + continue; + const char* breakdown_name = GetVizBreakdownName(it.GetBreakdown()); + TRACE_EVENT_BEGIN("cc,benchmark", + perfetto::StaticString{breakdown_name}, trace_track, start_time); TRACE_EVENT_END("cc,benchmark", trace_track, end_time); - }); + } } TRACE_EVENT_END("cc,benchmark", trace_track, stage.end_time); } @@ -901,36 +1107,83 @@ void CompositorFrameReporter::ReportCompositorLatencyTraceEvents() const { } void CompositorFrameReporter::ReportEventLatencyTraceEvents() const { + // TODO(mohsen): This function is becoming large and there is concerns about + // having this in the compositor critical path. crbug.com/1072740 is + // considering doing the reporting off-thread, but as a short-term solution, + // we should investigate whether we can skip this function entirely if tracing + // is off and whether that has any positive impact or not. for (const auto& event_metrics : events_metrics_) { + const base::TimeTicks generated_timestamp = + event_metrics->GetDispatchStageTimestamp( + EventMetrics::DispatchStage::kGenerated); + const auto trace_id = TRACE_ID_LOCAL(event_metrics.get()); TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP1( - "cc,input", "EventLatency", trace_id, event_metrics->time_stamp(), - "event", event_metrics->GetTypeName()); - - // Find the first stage that happens after the event's arrival in the - // browser. - auto stage_it = - std::find_if(stage_history_.begin(), stage_history_.end(), - [&event_metrics](const StageData& stage) { - return stage.start_time > event_metrics->time_stamp(); - }); + "cc,input", "EventLatency", trace_id, generated_timestamp, "event", + event_metrics->GetTypeName()); + + // Event dispatch stages. + EventMetrics::DispatchStage dispatch_stage = + EventMetrics::DispatchStage::kGenerated; + base::TimeTicks dispatch_timestamp = + event_metrics->GetDispatchStageTimestamp(dispatch_stage); + while (dispatch_stage != EventMetrics::DispatchStage::kMaxValue) { + DCHECK(!dispatch_timestamp.is_null()); + + // Find the end dispatch stage. + auto end_stage = static_cast<EventMetrics::DispatchStage>( + static_cast<int>(dispatch_stage) + 1); + base::TimeTicks end_timestamp = + event_metrics->GetDispatchStageTimestamp(end_stage); + while (end_timestamp.is_null() && + end_stage != EventMetrics::DispatchStage::kMaxValue) { + end_stage = static_cast<EventMetrics::DispatchStage>( + static_cast<int>(end_stage) + 1); + end_timestamp = event_metrics->GetDispatchStageTimestamp(end_stage); + } + if (end_timestamp.is_null()) + break; + + const char* breakdown_name = + GetEventLatencyDispatchBreakdownName(dispatch_stage, end_stage); + TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0( + "cc,input", breakdown_name, trace_id, dispatch_timestamp); + TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP0("cc,input", breakdown_name, + trace_id, end_timestamp); + + dispatch_stage = end_stage; + dispatch_timestamp = end_timestamp; + } + + // Find the first compositor stage that happens after the final dispatch + // stage. + auto stage_it = std::find_if(stage_history_.begin(), stage_history_.end(), + [dispatch_timestamp](const StageData& stage) { + return stage.start_time > dispatch_timestamp; + }); // TODO(crbug.com/1079116): Ideally, at least the start time of // SubmitCompositorFrameToPresentationCompositorFrame stage should be - // greater than the event time stamp, but apparently, this is not always the - // case (see crbug.com/1093698). For now, skip to the next event in such - // cases. Hopefully, the work to reduce discrepancies between the new - // EventLatency and the old Event.Latency metrics would fix this issue. If - // not, we need to reconsider investigating this issue. + // greater than the final event dispatch timestamp, but apparently, this is + // not always the case (see crbug.com/1093698). For now, skip to the next + // event in such cases. Hopefully, the work to reduce discrepancies between + // the new EventLatency and the old Event.Latency metrics would fix this + // issue. If not, we need to reconsider investigating this issue. if (stage_it == stage_history_.end()) continue; + DCHECK(dispatch_stage == + EventMetrics::DispatchStage::kRendererCompositorFinished || + dispatch_stage == + EventMetrics::DispatchStage::kRendererMainFinished); + const char* d2c_breakdown_name = + GetEventLatencyDispatchToCompositorBreakdownName(dispatch_stage, + stage_it->stage_type); TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0( - "cc,input", "BrowserToRendererCompositor", trace_id, - event_metrics->time_stamp()); + "cc,input", d2c_breakdown_name, trace_id, dispatch_timestamp); TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP0( - "cc,input", "BrowserToRendererCompositor", trace_id, - stage_it->start_time); + "cc,input", d2c_breakdown_name, trace_id, stage_it->start_time); + // Compositor stages. for (; stage_it != stage_history_.end(); ++stage_it) { const int stage_type_index = static_cast<int>(stage_it->stage_type); CHECK_LT(stage_type_index, static_cast<int>(StageType::kStageTypeCount)); @@ -947,14 +1200,19 @@ void CompositorFrameReporter::ReportEventLatencyTraceEvents() const { if (stage_it->stage_type == StageType::kSubmitCompositorFrameToPresentationCompositorFrame) { - REPORT_VIZ_BREAKDOWN_TRACES([&](base::TimeTicks start_time, - base::TimeTicks end_time, - const char* substage_name) { + DCHECK(processed_viz_breakdown_); + for (auto it = processed_viz_breakdown_->CreateIterator(true); + it.IsValid(); it.Advance()) { + base::TimeTicks start_time = it.GetStartTime(); + base::TimeTicks end_time = it.GetEndTime(); + if (start_time >= end_time) + continue; + const char* breakdown_name = GetVizBreakdownName(it.GetBreakdown()); TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0( - "cc,input", substage_name, trace_id, start_time); + "cc,input", breakdown_name, trace_id, start_time); TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP0( - "cc,input", substage_name, trace_id, end_time); - }); + "cc,input", breakdown_name, trace_id, end_time); + } } TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP0( @@ -965,92 +1223,6 @@ void CompositorFrameReporter::ReportEventLatencyTraceEvents() const { } } -void CompositorFrameReporter::PopulateBlinkBreakdownList() { - if (blink_start_time_.is_null()) - return; - - blink_breakdown_list_[static_cast<int>(BlinkBreakdown::kHandleInputEvents)] = - blink_breakdown_.handle_input_events; - blink_breakdown_list_[static_cast<int>(BlinkBreakdown::kAnimate)] = - blink_breakdown_.animate; - blink_breakdown_list_[static_cast<int>(BlinkBreakdown::kStyleUpdate)] = - blink_breakdown_.style_update; - blink_breakdown_list_[static_cast<int>(BlinkBreakdown::kLayoutUpdate)] = - blink_breakdown_.layout_update; - blink_breakdown_list_[static_cast<int>(BlinkBreakdown::kPrepaint)] = - blink_breakdown_.prepaint; - blink_breakdown_list_[static_cast<int>(BlinkBreakdown::kCompositingInputs)] = - blink_breakdown_.compositing_inputs; - blink_breakdown_list_[static_cast<int>( - BlinkBreakdown::kCompositingAssignments)] = - blink_breakdown_.compositing_assignments; - blink_breakdown_list_[static_cast<int>(BlinkBreakdown::kPaint)] = - blink_breakdown_.paint; - blink_breakdown_list_[static_cast<int>(BlinkBreakdown::kCompositeCommit)] = - blink_breakdown_.composite_commit; - blink_breakdown_list_[static_cast<int>(BlinkBreakdown::kUpdateLayers)] = - blink_breakdown_.update_layers; - blink_breakdown_list_[static_cast<int>( - BlinkBreakdown::kBeginMainSentToStarted)] = - begin_main_frame_start_ - blink_start_time_; -} - -void CompositorFrameReporter::PopulateVizBreakdownList() { - if (viz_start_time_.is_null()) - return; - - // Check if viz_breakdown is set. Testing indicates that sometimes the - // received_compositor_frame_timestamp can be earlier than the given - // |start_time|. Avoid reporting negative times. - if (viz_breakdown_.received_compositor_frame_timestamp.is_null() || - viz_breakdown_.received_compositor_frame_timestamp < viz_start_time_) { - return; - } - viz_breakdown_list_[static_cast<int>( - VizBreakdown::kSubmitToReceiveCompositorFrame)] = - std::make_pair(viz_start_time_, - viz_breakdown_.received_compositor_frame_timestamp); - - if (viz_breakdown_.draw_start_timestamp.is_null()) - return; - viz_breakdown_list_[static_cast<int>( - VizBreakdown::kReceivedCompositorFrameToStartDraw)] = - std::make_pair(viz_breakdown_.received_compositor_frame_timestamp, - viz_breakdown_.draw_start_timestamp); - - if (viz_breakdown_.swap_timings.is_null()) - return; - viz_breakdown_list_[static_cast<int>(VizBreakdown::kStartDrawToSwapStart)] = - std::make_pair(viz_breakdown_.draw_start_timestamp, - viz_breakdown_.swap_timings.swap_start); - - viz_breakdown_list_[static_cast<int>(VizBreakdown::kSwapStartToSwapEnd)] = - std::make_pair(viz_breakdown_.swap_timings.swap_start, - viz_breakdown_.swap_timings.swap_end); - - viz_breakdown_list_[static_cast<int>( - VizBreakdown::kSwapEndToPresentationCompositorFrame)] = - std::make_pair(viz_breakdown_.swap_timings.swap_end, - viz_breakdown_.presentation_feedback.timestamp); - - if (viz_breakdown_.presentation_feedback.ready_timestamp.is_null()) - return; - viz_breakdown_list_[static_cast<int>( - VizBreakdown::kSwapStartToBufferAvailable)] = - std::make_pair(viz_breakdown_.swap_timings.swap_start, - viz_breakdown_.presentation_feedback.available_timestamp); - viz_breakdown_list_[static_cast<int>( - VizBreakdown::kBufferAvailableToBufferReady)] = - std::make_pair(viz_breakdown_.presentation_feedback.available_timestamp, - viz_breakdown_.presentation_feedback.ready_timestamp); - viz_breakdown_list_[static_cast<int>(VizBreakdown::kBufferReadyToLatch)] = - std::make_pair(viz_breakdown_.presentation_feedback.ready_timestamp, - viz_breakdown_.presentation_feedback.latch_timestamp); - viz_breakdown_list_[static_cast<int>(VizBreakdown::kLatchToSwapEnd)] = - std::make_pair(viz_breakdown_.presentation_feedback.latch_timestamp, - viz_breakdown_.swap_timings.swap_end); -} - base::TimeDelta CompositorFrameReporter::SumOfStageHistory() const { base::TimeDelta sum; for (const StageData& stage : stage_history_) @@ -1089,8 +1261,41 @@ base::WeakPtr<CompositorFrameReporter> CompositorFrameReporter::GetWeakPtr() { void CompositorFrameReporter::AdoptReporter( std::unique_ptr<CompositorFrameReporter> reporter) { - DCHECK_EQ(cloned_to_.get(), reporter.get()); - own_cloned_to_ = std::move(reporter); + // If |this| reporter is dependent on another reporter to decide about partial + // update, then |this| should not have any such dependents. + DCHECK(!partial_update_decider_); + DCHECK(!partial_update_dependents_.empty()); + owned_partial_update_dependents_.push(std::move(reporter)); + DiscardOldPartialUpdateReporters(); +} + +void CompositorFrameReporter::SetPartialUpdateDecider( + base::WeakPtr<CompositorFrameReporter> decider) { + DCHECK(decider); + has_partial_update_ = true; + partial_update_decider_ = decider; + decider->partial_update_dependents_.push(GetWeakPtr()); + DCHECK(partial_update_dependents_.empty()); +} + +void CompositorFrameReporter::DiscardOldPartialUpdateReporters() { + DCHECK_LE(owned_partial_update_dependents_.size(), + partial_update_dependents_.size()); + while (owned_partial_update_dependents_.size() > 300u) { + auto& dependent = owned_partial_update_dependents_.front(); + dependent->set_has_partial_update(false); + partial_update_dependents_.pop(); + owned_partial_update_dependents_.pop(); + discarded_partial_update_dependents_count_++; + } +} + +bool CompositorFrameReporter::MightHavePartialUpdate() const { + return !!partial_update_decider_; +} + +size_t CompositorFrameReporter::GetPartialUpdateDependentsCount() const { + return partial_update_dependents_.size(); } } // namespace cc diff --git a/chromium/cc/metrics/compositor_frame_reporter.h b/chromium/cc/metrics/compositor_frame_reporter.h index 6c5cb9ba86d..0e9f0001b06 100644 --- a/chromium/cc/metrics/compositor_frame_reporter.h +++ b/chromium/cc/metrics/compositor_frame_reporter.h @@ -7,6 +7,7 @@ #include <bitset> #include <memory> +#include <queue> #include <string> #include <utility> #include <vector> @@ -140,6 +141,88 @@ class CC_EXPORT CompositorFrameReporter { kSmoothBoth }; + // Holds a processed list of Blink breakdowns with an `Iterator` class to + // easily iterator over them. + class CC_EXPORT ProcessedBlinkBreakdown { + public: + class Iterator { + public: + explicit Iterator(const ProcessedBlinkBreakdown* owner); + ~Iterator(); + + bool IsValid() const; + void Advance(); + BlinkBreakdown GetBreakdown() const; + base::TimeDelta GetLatency() const; + + private: + const ProcessedBlinkBreakdown* owner_; + + size_t index_ = 0; + }; + + ProcessedBlinkBreakdown(base::TimeTicks blink_start_time, + base::TimeTicks begin_main_frame_start, + const BeginMainFrameMetrics& blink_breakdown); + ~ProcessedBlinkBreakdown(); + + ProcessedBlinkBreakdown(const ProcessedBlinkBreakdown&) = delete; + ProcessedBlinkBreakdown& operator=(const ProcessedBlinkBreakdown&) = delete; + + // Returns a new iterator for the Blink breakdowns. + Iterator CreateIterator() const; + + private: + base::TimeDelta list_[static_cast<int>(BlinkBreakdown::kBreakdownCount)]; + }; + + // Holds a processed list of Viz breakdowns with an `Iterator` class to easily + // iterate over them. + class CC_EXPORT ProcessedVizBreakdown { + public: + class Iterator { + public: + Iterator(const ProcessedVizBreakdown* owner, + bool skip_swap_start_to_swap_end); + ~Iterator(); + + bool IsValid() const; + void Advance(); + VizBreakdown GetBreakdown() const; + base::TimeTicks GetStartTime() const; + base::TimeTicks GetEndTime() const; + base::TimeDelta GetDuration() const; + + private: + const ProcessedVizBreakdown* owner_; + const bool skip_swap_start_to_swap_end_; + + size_t index_ = 0; + }; + + ProcessedVizBreakdown(base::TimeTicks viz_start_time, + const viz::FrameTimingDetails& viz_breakdown); + ~ProcessedVizBreakdown(); + + ProcessedVizBreakdown(const ProcessedVizBreakdown&) = delete; + ProcessedVizBreakdown& operator=(const ProcessedVizBreakdown&) = delete; + + // Returns a new iterator for the Viz breakdowns. If buffer ready breakdowns + // are available, `skip_swap_start_to_swap_end_if_breakdown_available` can + // be used to skip `kSwapStartToSwapEnd` breakdown. + Iterator CreateIterator( + bool skip_swap_start_to_swap_end_if_breakdown_available) const; + + base::TimeTicks swap_start() const { return swap_start_; } + + private: + base::Optional<std::pair<base::TimeTicks, base::TimeTicks>> + list_[static_cast<int>(VizBreakdown::kBreakdownCount)]; + + bool buffer_ready_available_ = false; + base::TimeTicks swap_start_; + }; + using ActiveTrackers = std::bitset<static_cast<size_t>(FrameSequenceTrackerType::kMaxType)>; @@ -156,6 +239,11 @@ class CC_EXPORT CompositorFrameReporter { CompositorFrameReporter& operator=(const CompositorFrameReporter& reporter) = delete; + // Creates and returns a clone of the reporter, only if it is currently in the + // 'begin impl frame' stage. For any other state, it returns null. + // This is used only when there is a partial update. So the cloned reporter + // depends in this reporter to decide whether it contains be partial updates + // or complete updates. std::unique_ptr<CompositorFrameReporter> CopyReporterAtBeginImplStage(); // Note that the started stage may be reported to UMA. If the histogram is @@ -200,10 +288,10 @@ class CC_EXPORT CompositorFrameReporter { tick_clock_ = tick_clock; } - bool has_partial_update() const { return has_partial_update_; } - void set_has_partial_update(bool has_partial_update) { - has_partial_update_ = has_partial_update; - } + void SetPartialUpdateDecider(base::WeakPtr<CompositorFrameReporter> decider); + + bool MightHavePartialUpdate() const; + size_t GetPartialUpdateDependentsCount() const; const viz::BeginFrameId& frame_id() const { return args_.frame_id; } @@ -214,11 +302,18 @@ class CC_EXPORT CompositorFrameReporter { // If this is a cloned reporter, then this returns a weak-ptr to the original // reporter this was cloned from (using |CopyReporterAtBeginImplStage()|). - base::WeakPtr<CompositorFrameReporter> cloned_from() { return cloned_from_; } - protected: + base::WeakPtr<CompositorFrameReporter> partial_update_decider() { + return partial_update_decider_; + } + base::WeakPtr<CompositorFrameReporter> GetWeakPtr(); + protected: + void set_has_partial_update(bool has_partial_update) { + has_partial_update_ = has_partial_update; + } + private: void TerminateReporter(); void EndCurrentStage(base::TimeTicks end_time); @@ -238,16 +333,6 @@ class CC_EXPORT CompositorFrameReporter { base::TimeDelta time_delta) const; void ReportEventLatencyHistograms() const; - void ReportEventLatencyBlinkBreakdowns( - int histogram_base_index, - const std::string& histogram_base_name) const; - void ReportEventLatencyVizBreakdowns( - int histogram_base_index, - const std::string& histogram_base_name) const; - void ReportEventLatencyHistogram(int histogram_base_index, - const std::string& histogram_base_name, - int stage_type_index, - base::TimeDelta latency) const; void ReportCompositorLatencyTraceEvents() const; void ReportEventLatencyTraceEvents() const; @@ -259,12 +344,12 @@ class CC_EXPORT CompositorFrameReporter { return report_types_.test(static_cast<size_t>(report_type)); } - void PopulateBlinkBreakdownList(); - void PopulateVizBreakdownList(); - // This method is only used for DCheck base::TimeDelta SumOfStageHistory() const; + // Terminating reporters in partial_update_dependents_ after a limit. + void DiscardOldPartialUpdateReporters(); + base::TimeTicks Now() const; bool IsDroppedFrameAffectingSmoothness() const; @@ -276,13 +361,11 @@ class CC_EXPORT CompositorFrameReporter { BeginMainFrameMetrics blink_breakdown_; base::TimeTicks blink_start_time_; - base::TimeDelta - blink_breakdown_list_[static_cast<int>(BlinkBreakdown::kBreakdownCount)]; + std::unique_ptr<ProcessedBlinkBreakdown> processed_blink_breakdown_; viz::FrameTimingDetails viz_breakdown_; base::TimeTicks viz_start_time_; - base::Optional<std::pair<base::TimeTicks, base::TimeTicks>> - viz_breakdown_list_[static_cast<int>(VizBreakdown::kBreakdownCount)]; + std::unique_ptr<ProcessedVizBreakdown> processed_viz_breakdown_; // Stage data is recorded here. On destruction these stages will be reported // to UMA if the termination status is |kPresentedFrame|. Reported data will @@ -324,18 +407,24 @@ class CC_EXPORT CompositorFrameReporter { const SmoothThread smooth_thread_; const int layer_tree_host_id_; - // If this is a cloned pointer, then |cloned_from_| is a weak pointer to the - // original reporter this was cloned from. - base::WeakPtr<CompositorFrameReporter> cloned_from_; - - // If this reporter was cloned, then |cloned_to_| is a weak pointer to the - // cloned repoter. - base::WeakPtr<CompositorFrameReporter> cloned_to_; - - // A cloned reporter is not originally owned by the original reporter. - // However, it can 'adopt' it (using |AdoptReporter()| if the cloned reporter - // needs to stay alive until the original reporter terminates. - std::unique_ptr<CompositorFrameReporter> own_cloned_to_; + // For a reporter A, if the main-thread takes a long time to respond + // to a begin-main-frame, then all reporters created (and terminated) until + // the main-thread responds depends on this reporter to decide whether those + // frames contained partial updates (i.e. main-thread made some visual + // updates, but were not included in the frame), or complete updates. + // In such cases, |partial_update_dependents_| for A contains all the frames + // that depend on A for deciding whether they had partial updates or not, and + // |partial_update_decider_| is set to A for all these reporters. + std::queue<base::WeakPtr<CompositorFrameReporter>> partial_update_dependents_; + base::WeakPtr<CompositorFrameReporter> partial_update_decider_; + uint32_t discarded_partial_update_dependents_count_ = 0; + + // From the above example, it may be necessary for A to keep all the + // dependents alive until A terminates, so that the dependents can set their + // |has_partial_update_| flags correctly. This is done by passing ownership of + // these reporters (using |AdoptReporter()|). + std::queue<std::unique_ptr<CompositorFrameReporter>> + owned_partial_update_dependents_; base::WeakPtrFactory<CompositorFrameReporter> weak_factory_{this}; }; diff --git a/chromium/cc/metrics/compositor_frame_reporter_unittest.cc b/chromium/cc/metrics/compositor_frame_reporter_unittest.cc index 8455ec95bac..e7a38ba77fc 100644 --- a/chromium/cc/metrics/compositor_frame_reporter_unittest.cc +++ b/chromium/cc/metrics/compositor_frame_reporter_unittest.cc @@ -4,6 +4,7 @@ #include "cc/metrics/compositor_frame_reporter.h" +#include <algorithm> #include <memory> #include <utility> #include <vector> @@ -80,6 +81,40 @@ class CompositorFrameReporterTest : public testing::Test { return viz_breakdown; } + std::unique_ptr<EventMetrics> CreateEventMetrics( + ui::EventType type, + base::Optional<EventMetrics::ScrollUpdateType> scroll_update_type, + base::Optional<ui::ScrollInputType> scroll_input_type) { + const base::TimeTicks event_time = AdvanceNowByMs(3); + AdvanceNowByMs(3); + std::unique_ptr<EventMetrics> metrics = EventMetrics::CreateForTesting( + type, scroll_update_type, scroll_input_type, event_time, + &test_tick_clock_); + if (metrics) { + AdvanceNowByMs(3); + metrics->SetDispatchStageTimestamp( + EventMetrics::DispatchStage::kRendererCompositorStarted); + AdvanceNowByMs(3); + metrics->SetDispatchStageTimestamp( + EventMetrics::DispatchStage::kRendererCompositorFinished); + } + + return metrics; + } + + std::vector<base::TimeTicks> GetEventTimestamps( + const EventMetrics::List& events_metrics) { + std::vector<base::TimeTicks> event_times; + event_times.reserve(events_metrics.size()); + std::transform(events_metrics.cbegin(), events_metrics.cend(), + std::back_inserter(event_times), + [](const auto& event_metrics) { + return event_metrics->GetDispatchStageTimestamp( + EventMetrics::DispatchStage::kGenerated); + }); + return event_times; + } + // This should be defined before |pipeline_reporter_| so it is created before // and destroyed after that. base::SimpleTestTickClock test_tick_clock_; @@ -238,19 +273,16 @@ TEST_F(CompositorFrameReporterTest, EventLatencyTotalForPresentedFrameReported) { base::HistogramTester histogram_tester; - const base::TimeTicks event_time = Now(); std::unique_ptr<EventMetrics> event_metrics_ptrs[] = { - EventMetrics::Create(ui::ET_TOUCH_PRESSED, base::nullopt, event_time, - base::nullopt), - EventMetrics::Create(ui::ET_TOUCH_MOVED, base::nullopt, event_time, - base::nullopt), - EventMetrics::Create(ui::ET_TOUCH_MOVED, base::nullopt, event_time, - base::nullopt), + CreateEventMetrics(ui::ET_TOUCH_PRESSED, base::nullopt, base::nullopt), + CreateEventMetrics(ui::ET_TOUCH_MOVED, base::nullopt, base::nullopt), + CreateEventMetrics(ui::ET_TOUCH_MOVED, base::nullopt, base::nullopt), }; EXPECT_THAT(event_metrics_ptrs, Each(NotNull())); EventMetrics::List events_metrics( std::make_move_iterator(std::begin(event_metrics_ptrs)), std::make_move_iterator(std::end(event_metrics_ptrs))); + std::vector<base::TimeTicks> event_times = GetEventTimestamps(events_metrics); AdvanceNowByMs(3); pipeline_reporter_->StartStage( @@ -269,171 +301,46 @@ TEST_F(CompositorFrameReporterTest, Now()); pipeline_reporter_->SetEventsMetrics(std::move(events_metrics)); - AdvanceNowByMs(3); - const base::TimeTicks presentation_time = Now(); + const base::TimeTicks presentation_time = AdvanceNowByMs(3); pipeline_reporter_->TerminateFrame( CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame, presentation_time); pipeline_reporter_ = nullptr; - const int latency_ms = (presentation_time - event_time).InMicroseconds(); - histogram_tester.ExpectTotalCount("EventLatency.TouchPressed.TotalLatency", - 1); - histogram_tester.ExpectTotalCount("EventLatency.TouchMoved.TotalLatency", 2); - histogram_tester.ExpectTotalCount("EventLatency.TotalLatency", 3); - histogram_tester.ExpectBucketCount("EventLatency.TouchPressed.TotalLatency", - latency_ms, 1); - histogram_tester.ExpectBucketCount("EventLatency.TouchMoved.TotalLatency", - latency_ms, 2); - histogram_tester.ExpectBucketCount("EventLatency.TotalLatency", latency_ms, - 3); -} - -// Tests that when a frame is presented to the user, event latency breakdown -// metrics are reported properly. -TEST_F(CompositorFrameReporterTest, - EventLatencyBreakdownsForPresentedFrameReported) { - base::HistogramTester histogram_tester; - - const base::TimeTicks event_time = Now(); - std::unique_ptr<EventMetrics> event_metrics_ptrs[] = { - EventMetrics::Create(ui::ET_TOUCH_PRESSED, base::nullopt, event_time, - base::nullopt), + struct { + const char* name; + const base::HistogramBase::Count count; + } expected_counts[] = { + {"EventLatency.TouchPressed.TotalLatency", 1}, + {"EventLatency.TouchMoved.TotalLatency", 2}, + {"EventLatency.TotalLatency", 3}, }; - EXPECT_THAT(event_metrics_ptrs, Each(NotNull())); - EventMetrics::List events_metrics( - std::make_move_iterator(std::begin(event_metrics_ptrs)), - std::make_move_iterator(std::end(event_metrics_ptrs))); - - auto begin_impl_time = AdvanceNowByMs(2); - pipeline_reporter_->StartStage( - CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame, - begin_impl_time); - - auto begin_main_time = AdvanceNowByMs(3); - pipeline_reporter_->StartStage( - CompositorFrameReporter::StageType::kSendBeginMainFrameToCommit, - begin_main_time); - - auto begin_main_start_time = AdvanceNowByMs(4); - std::unique_ptr<BeginMainFrameMetrics> blink_breakdown = - BuildBlinkBreakdown(); - // Make a copy of the breakdown to use in verifying expectations in the end. - BeginMainFrameMetrics blink_breakdown_copy = *blink_breakdown; - pipeline_reporter_->SetBlinkBreakdown(std::move(blink_breakdown), - begin_main_start_time); - auto begin_commit_time = AdvanceNowByMs(5); - pipeline_reporter_->StartStage(CompositorFrameReporter::StageType::kCommit, - begin_commit_time); - - auto end_commit_time = AdvanceNowByMs(6); - pipeline_reporter_->StartStage( - CompositorFrameReporter::StageType::kEndCommitToActivation, - end_commit_time); - - auto begin_activation_time = AdvanceNowByMs(7); - pipeline_reporter_->StartStage( - CompositorFrameReporter::StageType::kActivation, begin_activation_time); - - auto end_activation_time = AdvanceNowByMs(8); - pipeline_reporter_->StartStage( - CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame, - end_activation_time); - - auto submit_time = AdvanceNowByMs(9); - pipeline_reporter_->StartStage( - CompositorFrameReporter::StageType:: - kSubmitCompositorFrameToPresentationCompositorFrame, - submit_time); - pipeline_reporter_->SetEventsMetrics(std::move(events_metrics)); - - AdvanceNowByMs(10); - viz::FrameTimingDetails viz_breakdown = BuildVizBreakdown(); - pipeline_reporter_->SetVizBreakdown(viz_breakdown); - pipeline_reporter_->TerminateFrame( - CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame, - viz_breakdown.presentation_feedback.timestamp); - - pipeline_reporter_ = nullptr; + for (const auto& expected_count : expected_counts) { + histogram_tester.ExpectTotalCount(expected_count.name, + expected_count.count); + } struct { const char* name; - const base::TimeDelta latency; + const base::HistogramBase::Sample latency_ms; } expected_latencies[] = { - {"EventLatency.TouchPressed.BrowserToRendererCompositor", - begin_impl_time - event_time}, - {"EventLatency.TouchPressed.BeginImplFrameToSendBeginMainFrame", - begin_main_time - begin_impl_time}, - {"EventLatency.TouchPressed.SendBeginMainFrameToCommit", - begin_commit_time - begin_main_time}, - {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.HandleInputEvents", - blink_breakdown_copy.handle_input_events}, - {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.Animate", - blink_breakdown_copy.animate}, - {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.StyleUpdate", - blink_breakdown_copy.style_update}, - {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.LayoutUpdate", - blink_breakdown_copy.layout_update}, - {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.CompositingInputs", - blink_breakdown_copy.compositing_inputs}, - {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.Prepaint", - blink_breakdown_copy.prepaint}, - {"EventLatency.TouchPressed.SendBeginMainFrameToCommit" - ".CompositingAssignments", - blink_breakdown_copy.compositing_assignments}, - {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.Paint", - blink_breakdown_copy.paint}, - {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.CompositeCommit", - blink_breakdown_copy.composite_commit}, - {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.UpdateLayers", - blink_breakdown_copy.update_layers}, - {"EventLatency.TouchPressed.SendBeginMainFrameToCommit." - "BeginMainSentToStarted", - begin_main_start_time - begin_main_time}, - {"EventLatency.TouchPressed.Commit", end_commit_time - begin_commit_time}, - {"EventLatency.TouchPressed.EndCommitToActivation", - begin_activation_time - end_commit_time}, - {"EventLatency.TouchPressed.Activation", - end_activation_time - begin_activation_time}, - {"EventLatency.TouchPressed.EndActivateToSubmitCompositorFrame", - submit_time - end_activation_time}, - {"EventLatency.TouchPressed." - "SubmitCompositorFrameToPresentationCompositorFrame", - viz_breakdown.presentation_feedback.timestamp - submit_time}, - {"EventLatency.TouchPressed." - "SubmitCompositorFrameToPresentationCompositorFrame." - "SubmitToReceiveCompositorFrame", - viz_breakdown.received_compositor_frame_timestamp - submit_time}, - {"EventLatency.TouchPressed." - "SubmitCompositorFrameToPresentationCompositorFrame." - "ReceivedCompositorFrameToStartDraw", - viz_breakdown.draw_start_timestamp - - viz_breakdown.received_compositor_frame_timestamp}, - {"EventLatency.TouchPressed." - "SubmitCompositorFrameToPresentationCompositorFrame." - "StartDrawToSwapStart", - viz_breakdown.swap_timings.swap_start - - viz_breakdown.draw_start_timestamp}, - {"EventLatency.TouchPressed." - "SubmitCompositorFrameToPresentationCompositorFrame.SwapStartToSwapEnd", - viz_breakdown.swap_timings.swap_end - - viz_breakdown.swap_timings.swap_start}, - {"EventLatency.TouchPressed." - "SubmitCompositorFrameToPresentationCompositorFrame." - "SwapEndToPresentationCompositorFrame", - viz_breakdown.presentation_feedback.timestamp - - viz_breakdown.swap_timings.swap_end}, {"EventLatency.TouchPressed.TotalLatency", - viz_breakdown.presentation_feedback.timestamp - event_time}, + (presentation_time - event_times[0]).InMicroseconds()}, + {"EventLatency.TouchMoved.TotalLatency", + (presentation_time - event_times[1]).InMicroseconds()}, + {"EventLatency.TouchMoved.TotalLatency", + (presentation_time - event_times[2]).InMicroseconds()}, + {"EventLatency.TotalLatency", + (presentation_time - event_times[0]).InMicroseconds()}, {"EventLatency.TotalLatency", - viz_breakdown.presentation_feedback.timestamp - event_time}, + (presentation_time - event_times[1]).InMicroseconds()}, + {"EventLatency.TotalLatency", + (presentation_time - event_times[2]).InMicroseconds()}, }; - for (const auto& expected_latency : expected_latencies) { - histogram_tester.ExpectTotalCount(expected_latency.name, 1); - histogram_tester.ExpectBucketCount( - expected_latency.name, expected_latency.latency.InMicroseconds(), 1); + histogram_tester.ExpectBucketCount(expected_latency.name, + expected_latency.latency_ms, 1); } } @@ -443,21 +350,21 @@ TEST_F(CompositorFrameReporterTest, EventLatencyScrollTotalForPresentedFrameReported) { base::HistogramTester histogram_tester; - const base::TimeTicks event_time = Now(); std::unique_ptr<EventMetrics> event_metrics_ptrs[] = { - EventMetrics::Create(ui::ET_GESTURE_SCROLL_BEGIN, base::nullopt, - event_time, ui::ScrollInputType::kWheel), - EventMetrics::Create(ui::ET_GESTURE_SCROLL_UPDATE, - EventMetrics::ScrollUpdateType::kStarted, event_time, - ui::ScrollInputType::kWheel), - EventMetrics::Create(ui::ET_GESTURE_SCROLL_UPDATE, - EventMetrics::ScrollUpdateType::kContinued, - event_time, ui::ScrollInputType::kWheel), + CreateEventMetrics(ui::ET_GESTURE_SCROLL_BEGIN, base::nullopt, + ui::ScrollInputType::kWheel), + CreateEventMetrics(ui::ET_GESTURE_SCROLL_UPDATE, + EventMetrics::ScrollUpdateType::kStarted, + ui::ScrollInputType::kWheel), + CreateEventMetrics(ui::ET_GESTURE_SCROLL_UPDATE, + EventMetrics::ScrollUpdateType::kContinued, + ui::ScrollInputType::kWheel), }; EXPECT_THAT(event_metrics_ptrs, Each(NotNull())); EventMetrics::List events_metrics( std::make_move_iterator(std::begin(event_metrics_ptrs)), std::make_move_iterator(std::end(event_metrics_ptrs))); + std::vector<base::TimeTicks> event_times = GetEventTimestamps(events_metrics); AdvanceNowByMs(3); pipeline_reporter_->StartStage( @@ -485,32 +392,48 @@ TEST_F(CompositorFrameReporterTest, pipeline_reporter_ = nullptr; - const int total_latency_ms = - (viz_breakdown.presentation_feedback.timestamp - event_time) - .InMicroseconds(); - const int swap_begin_latency_ms = - (viz_breakdown.swap_timings.swap_start - event_time).InMicroseconds(); struct { const char* name; - const int64_t latency_ms; - } expected_metrics[] = { - {"EventLatency.GestureScrollBegin.Wheel.TotalLatency", total_latency_ms}, + const base::HistogramBase::Count count; + } expected_counts[] = { + {"EventLatency.GestureScrollBegin.Wheel.TotalLatency", 1}, + {"EventLatency.GestureScrollBegin.Wheel.TotalLatencyToSwapBegin", 1}, + {"EventLatency.FirstGestureScrollUpdate.Wheel.TotalLatency", 1}, + {"EventLatency.FirstGestureScrollUpdate.Wheel.TotalLatencyToSwapBegin", + 1}, + {"EventLatency.GestureScrollUpdate.Wheel.TotalLatency", 1}, + {"EventLatency.GestureScrollUpdate.Wheel.TotalLatencyToSwapBegin", 1}, + {"EventLatency.TotalLatency", 3}, + }; + for (const auto& expected_count : expected_counts) { + histogram_tester.ExpectTotalCount(expected_count.name, + expected_count.count); + } + + const base::TimeTicks presentation_time = + viz_breakdown.presentation_feedback.timestamp; + const base::TimeTicks swap_begin_time = viz_breakdown.swap_timings.swap_start; + struct { + const char* name; + const base::HistogramBase::Sample latency_ms; + } expected_latencies[] = { + {"EventLatency.GestureScrollBegin.Wheel.TotalLatency", + (presentation_time - event_times[0]).InMicroseconds()}, {"EventLatency.GestureScrollBegin.Wheel.TotalLatencyToSwapBegin", - swap_begin_latency_ms}, + (swap_begin_time - event_times[0]).InMicroseconds()}, {"EventLatency.FirstGestureScrollUpdate.Wheel.TotalLatency", - total_latency_ms}, + (presentation_time - event_times[1]).InMicroseconds()}, {"EventLatency.FirstGestureScrollUpdate.Wheel.TotalLatencyToSwapBegin", - swap_begin_latency_ms}, - {"EventLatency.GestureScrollUpdate.Wheel.TotalLatency", total_latency_ms}, + (swap_begin_time - event_times[1]).InMicroseconds()}, + {"EventLatency.GestureScrollUpdate.Wheel.TotalLatency", + (presentation_time - event_times[2]).InMicroseconds()}, {"EventLatency.GestureScrollUpdate.Wheel.TotalLatencyToSwapBegin", - swap_begin_latency_ms}, + (swap_begin_time - event_times[2]).InMicroseconds()}, }; - for (const auto& expected_metric : expected_metrics) { - histogram_tester.ExpectTotalCount(expected_metric.name, 1); - histogram_tester.ExpectBucketCount(expected_metric.name, - expected_metric.latency_ms, 1); + for (const auto& expected_latency : expected_latencies) { + histogram_tester.ExpectBucketCount(expected_latency.name, + expected_latency.latency_ms, 1); } - histogram_tester.ExpectTotalCount("EventLatency.TotalLatency", 3); } // Tests that when the frame is not presented to the user, event latency metrics @@ -519,14 +442,10 @@ TEST_F(CompositorFrameReporterTest, EventLatencyForDidNotPresentFrameNotReported) { base::HistogramTester histogram_tester; - const base::TimeTicks event_time = Now(); std::unique_ptr<EventMetrics> event_metrics_ptrs[] = { - EventMetrics::Create(ui::ET_TOUCH_PRESSED, base::nullopt, event_time, - base::nullopt), - EventMetrics::Create(ui::ET_TOUCH_MOVED, base::nullopt, event_time, - base::nullopt), - EventMetrics::Create(ui::ET_TOUCH_MOVED, base::nullopt, event_time, - base::nullopt), + CreateEventMetrics(ui::ET_TOUCH_PRESSED, base::nullopt, base::nullopt), + CreateEventMetrics(ui::ET_TOUCH_MOVED, base::nullopt, base::nullopt), + CreateEventMetrics(ui::ET_TOUCH_MOVED, base::nullopt, base::nullopt), }; EXPECT_THAT(event_metrics_ptrs, Each(NotNull())); EventMetrics::List events_metrics( diff --git a/chromium/cc/metrics/compositor_frame_reporting_controller.cc b/chromium/cc/metrics/compositor_frame_reporting_controller.cc index 2ff29e6ba97..dec573d13d9 100644 --- a/chromium/cc/metrics/compositor_frame_reporting_controller.cc +++ b/chromium/cc/metrics/compositor_frame_reporting_controller.cc @@ -6,6 +6,7 @@ #include <utility> +#include "base/trace_event/trace_event.h" #include "cc/metrics/compositor_frame_reporter.h" #include "cc/metrics/dropped_frame_counter.h" #include "cc/metrics/latency_ukm_reporter.h" @@ -221,6 +222,10 @@ void CompositorFrameReportingController::DidSubmitCompositorFrame( AdvanceReporterStage(PipelineStage::kBeginImplFrame, PipelineStage::kActivate); impl_reporter = std::move(reporters_[PipelineStage::kActivate]); + auto partial_update_decider = + HasOutstandingUpdatesFromMain(current_frame_id); + if (partial_update_decider) + impl_reporter->SetPartialUpdateDecider(partial_update_decider); } else if (CanSubmitMainFrame(current_frame_id)) { auto& reporter = reporters_[PipelineStage::kBeginMainFrame]; reporter->StartStage(StageType::kEndActivateToSubmitCompositorFrame, @@ -235,10 +240,6 @@ void CompositorFrameReportingController::DidSubmitCompositorFrame( if (reporter) { reporter->StartStage(StageType::kEndActivateToSubmitCompositorFrame, reporter->impl_frame_finish_time()); - // If the frame does not include any new updates from the main thread, - // then flag the frame as containing only partial updates. - if (!is_activated_frame_new) - reporter->set_has_partial_update(true); impl_reporter = std::move(reporter); } } @@ -306,20 +307,40 @@ void CompositorFrameReportingController::DidNotProduceFrame( // BeginMain stage, but the main-thread can make updates, which can be // submitted with the next frame. stage_reporter->OnDidNotProduceFrame(skip_reason); + if (skip_reason == FrameSkippedReason::kWaitingOnMain) + SetPartialUpdateDeciderWhenWaitingOnMain(stage_reporter); + break; } } +} - // If the compositor has no updates, and the main-thread has not responded to - // the begin-main-frame yet, then it is essentially a dropped frame. To handle - // this case, keep the reporter for the main-thread, but recreate a reporter - // for the dropped-frame. - if (skip_reason == FrameSkippedReason::kWaitingOnMain) { - auto reporter = RestoreReporterAtBeginImpl(id); - if (reporter) { - reporter->OnDidNotProduceFrame(skip_reason); - reporter->TerminateFrame(FrameTerminationStatus::kDidNotProduceFrame, - Now()); +void CompositorFrameReportingController:: + SetPartialUpdateDeciderWhenWaitingOnMain( + std::unique_ptr<CompositorFrameReporter>& stage_reporter) { + // If the compositor has no updates, and the main-thread has not responded + // to the begin-main-frame yet, then depending on main thread having + // update or not this would be a NoFrameProduced or a DroppedFrame. To + // handle this case , keep the reporter for the main-thread, but recreate + // a reporter for the current frame and link it to the reporter it depends + // on. + auto reporter = RestoreReporterAtBeginImpl(stage_reporter->frame_id()); + if (reporter) { + reporter->OnDidNotProduceFrame(FrameSkippedReason::kWaitingOnMain); + reporter->TerminateFrame(FrameTerminationStatus::kDidNotProduceFrame, + Now()); + stage_reporter->AdoptReporter(std::move(reporter)); + } else { + // The stage_reporter in this case was waiting for main, so needs to + // be adopted by the reporter which is waiting on Main thread's work + auto partial_update_decider = + HasOutstandingUpdatesFromMain(stage_reporter->frame_id()); + if (partial_update_decider) { + stage_reporter->SetPartialUpdateDecider(partial_update_decider); + stage_reporter->OnDidNotProduceFrame(FrameSkippedReason::kWaitingOnMain); + stage_reporter->TerminateFrame( + FrameTerminationStatus::kDidNotProduceFrame, Now()); + partial_update_decider->AdoptReporter(std::move(stage_reporter)); } } } @@ -362,8 +383,8 @@ void CompositorFrameReportingController::DidPresentCompositorFrame( // the original reporter, so that the cloned reporter stays alive until the // original reporter is terminated, and the cloned reporter's 'partial // update' flag can be unset if necessary. - if (reporter->has_partial_update()) { - auto orig_reporter = reporter->cloned_from(); + if (reporter->MightHavePartialUpdate()) { + auto orig_reporter = reporter->partial_update_decider(); if (orig_reporter) orig_reporter->AdoptReporter(std::move(reporter)); } @@ -409,12 +430,27 @@ void CompositorFrameReportingController::RemoveActiveTracker( void CompositorFrameReportingController::SetThreadAffectsSmoothness( FrameSequenceMetrics::ThreadType thread_type, bool affects_smoothness) { + auto current_smooth_thread = GetSmoothThread(); + if (thread_type == FrameSequenceMetrics::ThreadType::kCompositor) { is_compositor_thread_driving_smoothness_ = affects_smoothness; } else { DCHECK_EQ(thread_type, FrameSequenceMetrics::ThreadType::kMain); is_main_thread_driving_smoothness_ = affects_smoothness; } + + // keep the history for the last 3 seconds. + if (!smooth_thread_history_.empty()) { + auto expired_smooth_thread = smooth_thread_history_.lower_bound( + Now() - base::TimeDelta::FromSeconds(3))--; + smooth_thread_history_.erase(smooth_thread_history_.begin(), + expired_smooth_thread); + } + + // Only trackes the history if there is a change in smooth_thread_ + if (current_smooth_thread != GetSmoothThread()) { + smooth_thread_history_.insert(std::make_pair(Now(), current_smooth_thread)); + } } void CompositorFrameReportingController::AdvanceReporterStage( @@ -487,6 +523,37 @@ CompositorFrameReportingController::GetSmoothThread() const { : SmoothThread::kSmoothNone; } +CompositorFrameReporter::SmoothThread +CompositorFrameReportingController::GetSmoothThreadAtTime( + base::TimeTicks timestamp) const { + if (smooth_thread_history_.lower_bound(timestamp) == + smooth_thread_history_.end()) + return GetSmoothThread(); + return smooth_thread_history_.lower_bound(timestamp)->second; +} + +base::WeakPtr<CompositorFrameReporter> +CompositorFrameReportingController::HasOutstandingUpdatesFromMain( + const viz::BeginFrameId& id) const { + // Any unterminated reporter in the 'main frame', or 'commit' stages, then + // that indicates some pending updates from the main thread. + { + const auto& reporter = reporters_[PipelineStage::kBeginMainFrame]; + if (reporter && reporter->frame_id() < id && + !reporter->did_abort_main_frame()) { + return reporter->GetWeakPtr(); + } + } + { + const auto& reporter = reporters_[PipelineStage::kCommit]; + if (reporter && reporter->frame_id() < id) { + DCHECK(!reporter->did_abort_main_frame()); + return reporter->GetWeakPtr(); + } + } + return {}; +} + void CompositorFrameReportingController::CreateReportersForDroppedFrames( const viz::BeginFrameArgs& old_args, const viz::BeginFrameArgs& new_args) const { @@ -512,8 +579,8 @@ void CompositorFrameReportingController::CreateReportersForDroppedFrames( viz::BeginFrameArgs::NORMAL); auto reporter = std::make_unique<CompositorFrameReporter>( active_trackers_, args, latency_ukm_reporter_.get(), - should_report_metrics_, GetSmoothThread(), layer_tree_host_id_, - dropped_frame_counter_); + should_report_metrics_, GetSmoothThreadAtTime(timestamp), + layer_tree_host_id_, dropped_frame_counter_); reporter->set_tick_clock(tick_clock_); reporter->StartStage(StageType::kBeginImplFrameToSendBeginMainFrame, timestamp); diff --git a/chromium/cc/metrics/compositor_frame_reporting_controller.h b/chromium/cc/metrics/compositor_frame_reporting_controller.h index 7b62bc3bc67..9054c0a325a 100644 --- a/chromium/cc/metrics/compositor_frame_reporting_controller.h +++ b/chromium/cc/metrics/compositor_frame_reporting_controller.h @@ -5,6 +5,7 @@ #ifndef CC_METRICS_COMPOSITOR_FRAME_REPORTING_CONTROLLER_H_ #define CC_METRICS_COMPOSITOR_FRAME_REPORTING_CONTROLLER_H_ +#include <map> #include <memory> #include <vector> @@ -82,6 +83,9 @@ class CC_EXPORT CompositorFrameReportingController { void SetThreadAffectsSmoothness(FrameSequenceMetrics::ThreadType thread_type, bool affects_smoothness); + bool is_main_thread_driving_smoothness() const { + return is_main_thread_driving_smoothness_; + } void set_tick_clock(const base::TickClock* tick_clock) { DCHECK(tick_clock); @@ -118,6 +122,14 @@ class CC_EXPORT CompositorFrameReportingController { std::unique_ptr<CompositorFrameReporter> RestoreReporterAtBeginImpl( const viz::BeginFrameId& id); CompositorFrameReporter::SmoothThread GetSmoothThread() const; + CompositorFrameReporter::SmoothThread GetSmoothThreadAtTime( + base::TimeTicks timestamp) const; + + // Checks whether there are reporters containing updates from the main + // thread, and returns a weak-ptr to that reporter (if any). Otherwise returns + // null. + base::WeakPtr<CompositorFrameReporter> HasOutstandingUpdatesFromMain( + const viz::BeginFrameId& id) const; // If the display-compositor skips over some frames (e.g. when the gpu is // busy, or the client is non-responsive), then it will not issue any @@ -129,6 +141,11 @@ class CC_EXPORT CompositorFrameReportingController { const viz::BeginFrameArgs& old_args, const viz::BeginFrameArgs& new_args) const; + // The arg is a reference to the unique_ptr, because depending on the state + // that reporter is in, its ownership might be pass or not. + void SetPartialUpdateDeciderWhenWaitingOnMain( + std::unique_ptr<CompositorFrameReporter>& reporter); + const bool should_report_metrics_; const int layer_tree_host_id_; @@ -139,6 +156,10 @@ class CC_EXPORT CompositorFrameReportingController { bool is_compositor_thread_driving_smoothness_ = false; bool is_main_thread_driving_smoothness_ = false; + // Sorted history of smooththread. Element i indicating the smooththread from + // timestamp of element i-1 until timestamp of element i. + std::map<base::TimeTicks, CompositorFrameReporter::SmoothThread> + smooth_thread_history_; // The latency reporter passed to each CompositorFrameReporter. Owned here // because it must be common among all reporters. diff --git a/chromium/cc/metrics/compositor_frame_reporting_controller_unittest.cc b/chromium/cc/metrics/compositor_frame_reporting_controller_unittest.cc index c964ecdd8bb..8f13ac70623 100644 --- a/chromium/cc/metrics/compositor_frame_reporting_controller_unittest.cc +++ b/chromium/cc/metrics/compositor_frame_reporting_controller_unittest.cc @@ -55,6 +55,39 @@ class TestCompositorFrameReportingController reporters()[i] = nullptr; } } + + size_t GetBlockingReportersCount() { + size_t count = 0; + const PipelineStage kStages[] = { + PipelineStage::kBeginImplFrame, + PipelineStage::kBeginMainFrame, + PipelineStage::kCommit, + PipelineStage::kActivate, + }; + for (auto stage : kStages) { + auto& reporter = reporters()[stage]; + if (reporter && reporter->GetPartialUpdateDependentsCount() > 0) { + ++count; + } + } + return count; + } + + size_t GetBlockedReportersCount() { + size_t count = 0; + const PipelineStage kStages[] = { + PipelineStage::kBeginImplFrame, + PipelineStage::kBeginMainFrame, + PipelineStage::kCommit, + PipelineStage::kActivate, + }; + for (auto stage : kStages) { + auto& reporter = reporters()[stage]; + if (reporter) + count += reporter->GetPartialUpdateDependentsCount(); + } + return count; + } }; class CompositorFrameReportingControllerTest : public testing::Test { @@ -186,6 +219,39 @@ class CompositorFrameReportingControllerTest : public testing::Test { return test_tick_clock_.NowTicks(); } + std::unique_ptr<EventMetrics> CreateEventMetrics( + ui::EventType type, + base::Optional<EventMetrics::ScrollUpdateType> scroll_update_type, + base::Optional<ui::ScrollInputType> scroll_input_type) { + const base::TimeTicks event_time = AdvanceNowByMs(10); + AdvanceNowByMs(10); + std::unique_ptr<EventMetrics> metrics = EventMetrics::CreateForTesting( + type, scroll_update_type, scroll_input_type, event_time, + &test_tick_clock_); + if (metrics) { + AdvanceNowByMs(10); + metrics->SetDispatchStageTimestamp( + EventMetrics::DispatchStage::kRendererCompositorStarted); + AdvanceNowByMs(10); + metrics->SetDispatchStageTimestamp( + EventMetrics::DispatchStage::kRendererCompositorFinished); + } + return metrics; + } + + std::vector<base::TimeTicks> GetEventTimestamps( + const EventMetrics::List& events_metrics) { + std::vector<base::TimeTicks> event_times; + event_times.reserve(events_metrics.size()); + std::transform(events_metrics.cbegin(), events_metrics.cend(), + std::back_inserter(event_times), + [](const auto& event_metrics) { + return event_metrics->GetDispatchStageTimestamp( + EventMetrics::DispatchStage::kGenerated); + }); + return event_times; + } + protected: // This should be defined before |reporting_controller_| so it is created // before and destroyed after that. @@ -380,31 +446,6 @@ TEST_F(CompositorFrameReportingControllerTest, "CompositorLatency.DroppedFrame.EndActivateToSubmitCompositorFrame", 0); } -TEST_F(CompositorFrameReportingControllerTest, ImplFrameCausedNoDamage) { - base::HistogramTester histogram_tester; - - SimulateBeginImplFrame(); - reporting_controller_.OnFinishImplFrame(args_.frame_id); - reporting_controller_.DidNotProduceFrame(args_.frame_id, - FrameSkippedReason::kNoDamage); - SimulateBeginImplFrame(); - histogram_tester.ExpectTotalCount( - "CompositorLatency.DroppedFrame.BeginImplFrameToSendBeginMainFrame", 0); - histogram_tester.ExpectBucketCount( - "CompositorLatency.Type", - CompositorFrameReporter::FrameReportType::kDroppedFrame, 0); - - reporting_controller_.OnFinishImplFrame(args_.frame_id); - reporting_controller_.DidNotProduceFrame(args_.frame_id, - FrameSkippedReason::kWaitingOnMain); - SimulateBeginImplFrame(); - histogram_tester.ExpectTotalCount( - "CompositorLatency.DroppedFrame.BeginImplFrameToSendBeginMainFrame", 1); - histogram_tester.ExpectBucketCount( - "CompositorLatency.Type", - CompositorFrameReporter::FrameReportType::kDroppedFrame, 1); -} - TEST_F(CompositorFrameReportingControllerTest, MainFrameCausedNoDamage) { base::HistogramTester histogram_tester; viz::BeginFrameId current_id_1(1, 1); @@ -1059,19 +1100,16 @@ TEST_F(CompositorFrameReportingControllerTest, EventLatencyTotalForPresentedFrameReported) { base::HistogramTester histogram_tester; - const base::TimeTicks event_time = AdvanceNowByMs(10); std::unique_ptr<EventMetrics> event_metrics_ptrs[] = { - EventMetrics::Create(ui::ET_TOUCH_PRESSED, base::nullopt, event_time, - base::nullopt), - EventMetrics::Create(ui::ET_TOUCH_MOVED, base::nullopt, event_time, - base::nullopt), - EventMetrics::Create(ui::ET_TOUCH_MOVED, base::nullopt, event_time, - base::nullopt), + CreateEventMetrics(ui::ET_TOUCH_PRESSED, base::nullopt, base::nullopt), + CreateEventMetrics(ui::ET_TOUCH_MOVED, base::nullopt, base::nullopt), + CreateEventMetrics(ui::ET_TOUCH_MOVED, base::nullopt, base::nullopt), }; EXPECT_THAT(event_metrics_ptrs, Each(NotNull())); EventMetrics::List events_metrics( std::make_move_iterator(std::begin(event_metrics_ptrs)), std::make_move_iterator(std::end(event_metrics_ptrs))); + std::vector<base::TimeTicks> event_times = GetEventTimestamps(events_metrics); // Submit a compositor frame and notify CompositorFrameReporter of the events // affecting the frame. @@ -1085,131 +1123,39 @@ TEST_F(CompositorFrameReportingControllerTest, reporting_controller_.DidPresentCompositorFrame(*next_token_, details); // Verify that EventLatency histograms are recorded. - const int64_t latency_ms = (presentation_time - event_time).InMicroseconds(); - histogram_tester.ExpectTotalCount("EventLatency.TouchPressed.TotalLatency", - 1); - histogram_tester.ExpectTotalCount("EventLatency.TouchMoved.TotalLatency", 2); - histogram_tester.ExpectTotalCount("EventLatency.TotalLatency", 3); - histogram_tester.ExpectBucketCount("EventLatency.TouchPressed.TotalLatency", - latency_ms, 1); - histogram_tester.ExpectBucketCount("EventLatency.TouchMoved.TotalLatency", - latency_ms, 2); - histogram_tester.ExpectBucketCount("EventLatency.TotalLatency", latency_ms, - 3); -} - -// Tests that EventLatency breakdown histograms are reported properly when a -// frame is presented to the user. -TEST_F(CompositorFrameReportingControllerTest, - EventLatencyBreakdownsForPresentedFrameReported) { - base::HistogramTester histogram_tester; - - const base::TimeTicks event_time = AdvanceNowByMs(10); - std::unique_ptr<EventMetrics> event_metrics_ptrs[] = { - EventMetrics::Create(ui::ET_TOUCH_PRESSED, base::nullopt, event_time, - base::nullopt), + struct { + const char* name; + const base::HistogramBase::Count count; + } expected_counts[] = { + {"EventLatency.TouchPressed.TotalLatency", 1}, + {"EventLatency.TouchMoved.TotalLatency", 2}, + {"EventLatency.TotalLatency", 3}, }; - EXPECT_THAT(event_metrics_ptrs, Each(NotNull())); - EventMetrics::List events_metrics( - std::make_move_iterator(std::begin(event_metrics_ptrs)), - std::make_move_iterator(std::end(event_metrics_ptrs))); - - // Do a commit with a breakdown of blink stages. - std::unique_ptr<BeginMainFrameMetrics> blink_breakdown = - BuildBlinkBreakdown(); - // Make a copy of the breakdown to use in verifying expectations in the end. - BeginMainFrameMetrics blink_breakdown_copy = *blink_breakdown; - SimulateCommit(std::move(blink_breakdown)); - - // Submit a compositor frame and notify CompositorFrameReporter of the events - // affecting the frame. - ++next_token_; - SimulateSubmitCompositorFrame(*next_token_, {std::move(events_metrics), {}}); - - // Present the submitted compositor frame to the user. - AdvanceNowByMs(10); - viz::FrameTimingDetails viz_breakdown = BuildVizBreakdown(); - reporting_controller_.DidPresentCompositorFrame(*next_token_, viz_breakdown); + for (const auto& expected_count : expected_counts) { + histogram_tester.ExpectTotalCount(expected_count.name, + expected_count.count); + } - // Verify that EventLatency histograms are recorded. struct { const char* name; - const base::TimeDelta latency; + const base::HistogramBase::Sample latency_ms; } expected_latencies[] = { - {"EventLatency.TouchPressed.BrowserToRendererCompositor", - begin_impl_time_ - event_time}, - {"EventLatency.TouchPressed.BeginImplFrameToSendBeginMainFrame", - begin_main_time_ - begin_impl_time_}, - {"EventLatency.TouchPressed.SendBeginMainFrameToCommit", - begin_commit_time_ - begin_main_time_}, - {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.HandleInputEvents", - blink_breakdown_copy.handle_input_events}, - {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.Animate", - blink_breakdown_copy.animate}, - {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.StyleUpdate", - blink_breakdown_copy.style_update}, - {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.LayoutUpdate", - blink_breakdown_copy.layout_update}, - {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.CompositingInputs", - blink_breakdown_copy.compositing_inputs}, - {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.Prepaint", - blink_breakdown_copy.prepaint}, - {"EventLatency.TouchPressed.SendBeginMainFrameToCommit." - "CompositingAssignments", - blink_breakdown_copy.compositing_assignments}, - {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.Paint", - blink_breakdown_copy.paint}, - {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.CompositeCommit", - blink_breakdown_copy.composite_commit}, - {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.UpdateLayers", - blink_breakdown_copy.update_layers}, - {"EventLatency.TouchPressed.SendBeginMainFrameToCommit." - "BeginMainSentToStarted", - begin_main_start_time_ - begin_main_time_}, - {"EventLatency.TouchPressed.Commit", - end_commit_time_ - begin_commit_time_}, - {"EventLatency.TouchPressed.EndCommitToActivation", - begin_activation_time_ - end_commit_time_}, - {"EventLatency.TouchPressed.Activation", - end_activation_time_ - begin_activation_time_}, - {"EventLatency.TouchPressed.EndActivateToSubmitCompositorFrame", - submit_time_ - end_activation_time_}, - {"EventLatency.TouchPressed." - "SubmitCompositorFrameToPresentationCompositorFrame", - viz_breakdown.presentation_feedback.timestamp - submit_time_}, - {"EventLatency.TouchPressed." - "SubmitCompositorFrameToPresentationCompositorFrame." - "SubmitToReceiveCompositorFrame", - viz_breakdown.received_compositor_frame_timestamp - submit_time_}, - {"EventLatency.TouchPressed." - "SubmitCompositorFrameToPresentationCompositorFrame." - "ReceivedCompositorFrameToStartDraw", - viz_breakdown.draw_start_timestamp - - viz_breakdown.received_compositor_frame_timestamp}, - {"EventLatency.TouchPressed." - "SubmitCompositorFrameToPresentationCompositorFrame." - "StartDrawToSwapStart", - viz_breakdown.swap_timings.swap_start - - viz_breakdown.draw_start_timestamp}, - {"EventLatency.TouchPressed." - "SubmitCompositorFrameToPresentationCompositorFrame.SwapStartToSwapEnd", - viz_breakdown.swap_timings.swap_end - - viz_breakdown.swap_timings.swap_start}, - {"EventLatency.TouchPressed." - "SubmitCompositorFrameToPresentationCompositorFrame." - "SwapEndToPresentationCompositorFrame", - viz_breakdown.presentation_feedback.timestamp - - viz_breakdown.swap_timings.swap_end}, {"EventLatency.TouchPressed.TotalLatency", - viz_breakdown.presentation_feedback.timestamp - event_time}, + (presentation_time - event_times[0]).InMicroseconds()}, + {"EventLatency.TouchMoved.TotalLatency", + (presentation_time - event_times[1]).InMicroseconds()}, + {"EventLatency.TouchMoved.TotalLatency", + (presentation_time - event_times[2]).InMicroseconds()}, + {"EventLatency.TotalLatency", + (presentation_time - event_times[0]).InMicroseconds()}, {"EventLatency.TotalLatency", - viz_breakdown.presentation_feedback.timestamp - event_time}, + (presentation_time - event_times[1]).InMicroseconds()}, + {"EventLatency.TotalLatency", + (presentation_time - event_times[2]).InMicroseconds()}, }; - for (const auto& expected_latency : expected_latencies) { - histogram_tester.ExpectTotalCount(expected_latency.name, 1); - histogram_tester.ExpectBucketCount( - expected_latency.name, expected_latency.latency.InMicroseconds(), 1); + histogram_tester.ExpectBucketCount(expected_latency.name, + expected_latency.latency_ms, 1); } } @@ -1219,21 +1165,21 @@ TEST_F(CompositorFrameReportingControllerTest, EventLatencyScrollTotalForPresentedFrameReported) { base::HistogramTester histogram_tester; - const base::TimeTicks event_time = AdvanceNowByMs(10); std::unique_ptr<EventMetrics> event_metrics_ptrs[] = { - EventMetrics::Create(ui::ET_GESTURE_SCROLL_BEGIN, base::nullopt, - event_time, ui::ScrollInputType::kWheel), - EventMetrics::Create(ui::ET_GESTURE_SCROLL_UPDATE, - EventMetrics::ScrollUpdateType::kStarted, event_time, - ui::ScrollInputType::kWheel), - EventMetrics::Create(ui::ET_GESTURE_SCROLL_UPDATE, - EventMetrics::ScrollUpdateType::kContinued, - event_time, ui::ScrollInputType::kWheel), + CreateEventMetrics(ui::ET_GESTURE_SCROLL_BEGIN, base::nullopt, + ui::ScrollInputType::kWheel), + CreateEventMetrics(ui::ET_GESTURE_SCROLL_UPDATE, + EventMetrics::ScrollUpdateType::kStarted, + ui::ScrollInputType::kWheel), + CreateEventMetrics(ui::ET_GESTURE_SCROLL_UPDATE, + EventMetrics::ScrollUpdateType::kContinued, + ui::ScrollInputType::kWheel), }; EXPECT_THAT(event_metrics_ptrs, Each(NotNull())); EventMetrics::List events_metrics( std::make_move_iterator(std::begin(event_metrics_ptrs)), std::make_move_iterator(std::end(event_metrics_ptrs))); + std::vector<base::TimeTicks> event_times = GetEventTimestamps(events_metrics); // Submit a compositor frame and notify CompositorFrameReporter of the events // affecting the frame. @@ -1250,31 +1196,48 @@ TEST_F(CompositorFrameReportingControllerTest, reporting_controller_.DidPresentCompositorFrame(*next_token_, details); // Verify that EventLatency histograms are recorded. - const int64_t total_latency_ms = - (details.presentation_feedback.timestamp - event_time).InMicroseconds(); - const int64_t swap_begin_latency_ms = - (details.swap_timings.swap_start - event_time).InMicroseconds(); struct { const char* name; - const int64_t latency_ms; - } expected_metrics[] = { - {"EventLatency.GestureScrollBegin.Wheel.TotalLatency", total_latency_ms}, + const base::HistogramBase::Count count; + } expected_counts[] = { + {"EventLatency.GestureScrollBegin.Wheel.TotalLatency", 1}, + {"EventLatency.GestureScrollBegin.Wheel.TotalLatencyToSwapBegin", 1}, + {"EventLatency.FirstGestureScrollUpdate.Wheel.TotalLatency", 1}, + {"EventLatency.FirstGestureScrollUpdate.Wheel.TotalLatencyToSwapBegin", + 1}, + {"EventLatency.GestureScrollUpdate.Wheel.TotalLatency", 1}, + {"EventLatency.GestureScrollUpdate.Wheel.TotalLatencyToSwapBegin", 1}, + {"EventLatency.TotalLatency", 3}, + }; + for (const auto& expected_count : expected_counts) { + histogram_tester.ExpectTotalCount(expected_count.name, + expected_count.count); + } + + const base::TimeTicks presentation_time = + details.presentation_feedback.timestamp; + const base::TimeTicks swap_begin_time = details.swap_timings.swap_start; + struct { + const char* name; + const base::HistogramBase::Sample latency_ms; + } expected_latencies[] = { + {"EventLatency.GestureScrollBegin.Wheel.TotalLatency", + (presentation_time - event_times[0]).InMicroseconds()}, {"EventLatency.GestureScrollBegin.Wheel.TotalLatencyToSwapBegin", - swap_begin_latency_ms}, + (swap_begin_time - event_times[0]).InMicroseconds()}, {"EventLatency.FirstGestureScrollUpdate.Wheel.TotalLatency", - total_latency_ms}, + (presentation_time - event_times[1]).InMicroseconds()}, {"EventLatency.FirstGestureScrollUpdate.Wheel.TotalLatencyToSwapBegin", - swap_begin_latency_ms}, - {"EventLatency.GestureScrollUpdate.Wheel.TotalLatency", total_latency_ms}, + (swap_begin_time - event_times[1]).InMicroseconds()}, + {"EventLatency.GestureScrollUpdate.Wheel.TotalLatency", + (presentation_time - event_times[2]).InMicroseconds()}, {"EventLatency.GestureScrollUpdate.Wheel.TotalLatencyToSwapBegin", - swap_begin_latency_ms}, + (swap_begin_time - event_times[2]).InMicroseconds()}, }; - for (const auto& expected_metric : expected_metrics) { - histogram_tester.ExpectTotalCount(expected_metric.name, 1); - histogram_tester.ExpectBucketCount(expected_metric.name, - expected_metric.latency_ms, 1); + for (const auto& expected_latency : expected_latencies) { + histogram_tester.ExpectBucketCount(expected_latency.name, + expected_latency.latency_ms, 1); } - histogram_tester.ExpectTotalCount("EventLatency.TotalLatency", 3); } // Tests that EventLatency histograms are not reported when the frame is dropped @@ -1283,14 +1246,10 @@ TEST_F(CompositorFrameReportingControllerTest, EventLatencyForDidNotPresentFrameNotReported) { base::HistogramTester histogram_tester; - const base::TimeTicks event_time = AdvanceNowByMs(10); std::unique_ptr<EventMetrics> event_metrics_ptrs[] = { - EventMetrics::Create(ui::ET_TOUCH_PRESSED, base::nullopt, event_time, - base::nullopt), - EventMetrics::Create(ui::ET_TOUCH_MOVED, base::nullopt, event_time, - base::nullopt), - EventMetrics::Create(ui::ET_TOUCH_MOVED, base::nullopt, event_time, - base::nullopt), + CreateEventMetrics(ui::ET_TOUCH_PRESSED, base::nullopt, base::nullopt), + CreateEventMetrics(ui::ET_TOUCH_MOVED, base::nullopt, base::nullopt), + CreateEventMetrics(ui::ET_TOUCH_MOVED, base::nullopt, base::nullopt), }; EXPECT_THAT(event_metrics_ptrs, Each(NotNull())); EventMetrics::List events_metrics( @@ -1319,6 +1278,10 @@ TEST_F(CompositorFrameReportingControllerTest, TEST_F(CompositorFrameReportingControllerTest, NewMainUpdateIsNotPartialUpdate) { + // Start a frame with main-thread update. Submit the frame (and present) + // before the main-thread responds. This creates two reporters: R1C and R1M + // (R1C for the submitted frame with updates from compositor-thread, and R1M + // for the pending main-thread frame). SimulateBeginMainFrame(); reporting_controller_.OnFinishImplFrame(current_id_); reporting_controller_.DidSubmitCompositorFrame(1u, current_id_, {}, {}); @@ -1326,6 +1289,7 @@ TEST_F(CompositorFrameReportingControllerTest, details.presentation_feedback.timestamp = AdvanceNowByMs(10); reporting_controller_.DidPresentCompositorFrame(1u, details); + // The main-thread responds now, triggering a commit and activation. reporting_controller_.WillCommit(); reporting_controller_.DidCommit(); reporting_controller_.WillActivate(); @@ -1333,6 +1297,9 @@ TEST_F(CompositorFrameReportingControllerTest, const auto previous_id = current_id_; + // Start a new frame with main-thread update. Submit the frame (and present) + // before the main-thread responds. This also again creates two reporters: R2C + // and R2M. SimulateBeginMainFrame(); reporting_controller_.OnFinishImplFrame(current_id_); reporting_controller_.DidSubmitCompositorFrame(1u, current_id_, previous_id, @@ -1340,8 +1307,13 @@ TEST_F(CompositorFrameReportingControllerTest, details.presentation_feedback.timestamp = AdvanceNowByMs(10); reporting_controller_.DidPresentCompositorFrame(1u, details); - EXPECT_EQ(3u, dropped_counter.total_frames()); + // In total, two frames have been completed: R1C, and R1M. + // R2C has been presented, but it is blocked on R2M to know whether R2C + // contains partial update, or complete updates. So it is kept alive. + EXPECT_EQ(2u, dropped_counter.total_frames()); EXPECT_EQ(1u, dropped_counter.total_main_dropped()); + EXPECT_EQ(1u, reporting_controller_.GetBlockingReportersCount()); + EXPECT_EQ(1u, reporting_controller_.GetBlockedReportersCount()); reporting_controller_.ResetReporters(); reporting_controller_.SetDroppedFrameCounter(nullptr); @@ -1409,5 +1381,115 @@ TEST_F(CompositorFrameReportingControllerTest, EXPECT_EQ(kSkipFramesActual, dropped_counter.total_compositor_dropped()); } +TEST_F(CompositorFrameReportingControllerTest, + CompositorFrameBlockedOnMainFrameWithNoDamage) { + viz::BeginFrameId current_id_1(1, 1); + viz::BeginFrameArgs args_1 = SimulateBeginFrameArgs(current_id_1); + + viz::BeginFrameId current_id_2(1, 2); + viz::BeginFrameArgs args_2 = SimulateBeginFrameArgs(current_id_2); + + viz::BeginFrameId current_id_3(1, 3); + viz::BeginFrameArgs args_3 = SimulateBeginFrameArgs(current_id_3); + + viz::BeginFrameId current_id_4(1, 4); + viz::BeginFrameArgs args_4 = SimulateBeginFrameArgs(current_id_4); + + reporting_controller_.WillBeginImplFrame(args_1); + reporting_controller_.WillBeginMainFrame(args_1); + reporting_controller_.OnFinishImplFrame(current_id_1); + EXPECT_EQ(0u, dropped_counter.total_compositor_dropped()); + reporting_controller_.DidNotProduceFrame(args_1.frame_id, + FrameSkippedReason::kWaitingOnMain); + + reporting_controller_.WillBeginImplFrame(args_2); + reporting_controller_.OnFinishImplFrame(args_2.frame_id); + reporting_controller_.DidNotProduceFrame(args_2.frame_id, + FrameSkippedReason::kWaitingOnMain); + + reporting_controller_.WillBeginImplFrame(args_3); + reporting_controller_.OnFinishImplFrame(args_3.frame_id); + reporting_controller_.DidNotProduceFrame(args_3.frame_id, + FrameSkippedReason::kWaitingOnMain); + + EXPECT_EQ(1u, reporting_controller_.GetBlockingReportersCount()); + EXPECT_EQ(3u, reporting_controller_.GetBlockedReportersCount()); + + // All frames are waiting for the main frame + EXPECT_EQ(0u, dropped_counter.total_main_dropped()); + EXPECT_EQ(0u, dropped_counter.total_compositor_dropped()); + EXPECT_EQ(0u, dropped_counter.total_frames()); + + reporting_controller_.BeginMainFrameAborted(args_1.frame_id); + reporting_controller_.DidNotProduceFrame(args_1.frame_id, + FrameSkippedReason::kNoDamage); + EXPECT_EQ(0u, dropped_counter.total_compositor_dropped()); + + // New reporters replace older reporters + reporting_controller_.WillBeginImplFrame(args_4); + reporting_controller_.WillBeginMainFrame(args_4); + + EXPECT_EQ(4u, dropped_counter.total_frames()); + EXPECT_EQ(0u, dropped_counter.total_main_dropped()); + EXPECT_EQ(0u, dropped_counter.total_compositor_dropped()); +} + +TEST_F(CompositorFrameReportingControllerTest, + SkippedFramesFromDisplayCompositorHaveSmoothThread) { + auto thread_type_compositor = FrameSequenceMetrics::ThreadType::kCompositor; + reporting_controller_.SetThreadAffectsSmoothness(thread_type_compositor, + true); + dropped_counter.OnFcpReceived(); + + // Submit and present two compositor frames. + SimulatePresentCompositorFrame(); + EXPECT_EQ(1u, dropped_counter.total_frames()); + EXPECT_EQ(0u, dropped_counter.total_main_dropped()); + EXPECT_EQ(0u, dropped_counter.total_compositor_dropped()); + + SimulatePresentCompositorFrame(); + EXPECT_EQ(2u, dropped_counter.total_frames()); + EXPECT_EQ(0u, dropped_counter.total_main_dropped()); + EXPECT_EQ(0u, dropped_counter.total_compositor_dropped()); + + // Now skip over a few frames, and submit + present another frame. + const uint32_t kSkipFrames_1 = 5; + for (uint32_t i = 0; i < kSkipFrames_1; ++i) + IncrementCurrentId(); + SimulatePresentCompositorFrame(); + EXPECT_EQ(3u + kSkipFrames_1, dropped_counter.total_frames()); + EXPECT_EQ(0u, dropped_counter.total_main_dropped()); + EXPECT_EQ(kSkipFrames_1, dropped_counter.total_compositor_dropped()); + EXPECT_EQ(kSkipFrames_1, dropped_counter.total_smoothness_dropped()); + + // Now skip over a few frames which are not affecting smoothness. + reporting_controller_.SetThreadAffectsSmoothness(thread_type_compositor, + false); + const uint32_t kSkipFrames_2 = 7; + for (uint32_t i = 0; i < kSkipFrames_2; ++i) + IncrementCurrentId(); + SimulatePresentCompositorFrame(); // Present another frame. + EXPECT_EQ(4u + kSkipFrames_1 + kSkipFrames_2, dropped_counter.total_frames()); + EXPECT_EQ(0u, dropped_counter.total_main_dropped()); + EXPECT_EQ(kSkipFrames_1 + kSkipFrames_2, + dropped_counter.total_compositor_dropped()); + EXPECT_EQ(kSkipFrames_1, dropped_counter.total_smoothness_dropped()); + + // Now skip over a few frames more frames which are affecting smoothness. + reporting_controller_.SetThreadAffectsSmoothness(thread_type_compositor, + true); + const uint32_t kSkipFrames_3 = 10; + for (uint32_t i = 0; i < kSkipFrames_3; ++i) + IncrementCurrentId(); + SimulatePresentCompositorFrame(); // Present another frame. + EXPECT_EQ(5u + kSkipFrames_1 + kSkipFrames_2 + kSkipFrames_3, + dropped_counter.total_frames()); + EXPECT_EQ(0u, dropped_counter.total_main_dropped()); + EXPECT_EQ(kSkipFrames_1 + kSkipFrames_2 + kSkipFrames_3, + dropped_counter.total_compositor_dropped()); + EXPECT_EQ(kSkipFrames_1 + kSkipFrames_3, + dropped_counter.total_smoothness_dropped()); +} + } // namespace } // namespace cc diff --git a/chromium/cc/metrics/compositor_timing_history.cc b/chromium/cc/metrics/compositor_timing_history.cc index 9a427c71a1e..9dd8e1aa0a4 100644 --- a/chromium/cc/metrics/compositor_timing_history.cc +++ b/chromium/cc/metrics/compositor_timing_history.cc @@ -34,9 +34,7 @@ class CompositorTimingHistory::UMAReporter { virtual void AddInvalidationToReadyToActivateDuration( base::TimeDelta duration, TreePriority priority) = 0; - virtual void AddPrepareTilesDuration(base::TimeDelta duration) = 0; virtual void AddDrawDuration(base::TimeDelta duration) = 0; - virtual void AddSubmitToAckLatency(base::TimeDelta duration) = 0; // crbug.com/758439: the following functions are used to report timing in // certain conditions targeting blink / compositor animations. @@ -323,20 +321,10 @@ class RendererUMAReporter : public CompositorTimingHistory::UMAReporter { priority); } - void AddPrepareTilesDuration(base::TimeDelta duration) override { - UMA_HISTOGRAM_CUSTOM_TIMES_DURATION( - "Scheduling.Renderer.PrepareTilesDuration", duration); - } - void AddDrawDuration(base::TimeDelta duration) override { UMA_HISTOGRAM_CUSTOM_TIMES_DURATION("Scheduling.Renderer.DrawDuration", duration); } - - void AddSubmitToAckLatency(base::TimeDelta duration) override { - UMA_HISTOGRAM_CUSTOM_TIMES_DURATION("Scheduling.Renderer.SwapToAckLatency", - duration); - } }; class BrowserUMAReporter : public CompositorTimingHistory::UMAReporter { @@ -370,20 +358,10 @@ class BrowserUMAReporter : public CompositorTimingHistory::UMAReporter { priority); } - void AddPrepareTilesDuration(base::TimeDelta duration) override { - UMA_HISTOGRAM_CUSTOM_TIMES_DURATION( - "Scheduling.Browser.PrepareTilesDuration", duration); - } - void AddDrawDuration(base::TimeDelta duration) override { UMA_HISTOGRAM_CUSTOM_TIMES_DURATION("Scheduling.Browser.DrawDuration", duration); } - - void AddSubmitToAckLatency(base::TimeDelta duration) override { - UMA_HISTOGRAM_CUSTOM_TIMES_DURATION("Scheduling.Browser.SwapToAckLatency", - duration); - } }; class NullUMAReporter : public CompositorTimingHistory::UMAReporter { @@ -398,9 +376,7 @@ class NullUMAReporter : public CompositorTimingHistory::UMAReporter { void AddInvalidationToReadyToActivateDuration( base::TimeDelta duration, TreePriority priority) override {} - void AddPrepareTilesDuration(base::TimeDelta duration) override {} void AddDrawDuration(base::TimeDelta duration) override {} - void AddSubmitToAckLatency(base::TimeDelta duration) override {} }; } // namespace @@ -535,12 +511,6 @@ base::TimeDelta CompositorTimingHistory::DrawDurationEstimate() const { return draw_duration_history_.Percentile(kDrawEstimationPercentile); } -void CompositorTimingHistory::DidCreateAndInitializeLayerTreeFrameSink() { - // After we get a new output surface, we won't get a spurious - // CompositorFrameAck from the old output surface. - submit_start_time_ = base::TimeTicks(); -} - void CompositorTimingHistory::WillBeginImplFrame( const viz::BeginFrameArgs& args, base::TimeTicks now) { @@ -690,7 +660,6 @@ void CompositorTimingHistory::DidPrepareTiles() { DCHECK_NE(base::TimeTicks(), prepare_tiles_start_time_); base::TimeDelta prepare_tiles_duration = Now() - prepare_tiles_start_time_; - uma_reporter_->AddPrepareTilesDuration(prepare_tiles_duration); if (enabled_) prepare_tiles_duration_history_.InsertSample(prepare_tiles_duration); @@ -808,11 +777,9 @@ void CompositorTimingHistory::DidSubmitCompositorFrame( const viz::BeginFrameId& current_frame_id, const viz::BeginFrameId& last_activated_frame_id, EventMetricsSet events_metrics) { - DCHECK_EQ(base::TimeTicks(), submit_start_time_); compositor_frame_reporting_controller_->DidSubmitCompositorFrame( frame_token, current_frame_id, last_activated_frame_id, std::move(events_metrics)); - submit_start_time_ = Now(); } void CompositorTimingHistory::DidNotProduceFrame( @@ -821,13 +788,6 @@ void CompositorTimingHistory::DidNotProduceFrame( compositor_frame_reporting_controller_->DidNotProduceFrame(id, skip_reason); } -void CompositorTimingHistory::DidReceiveCompositorFrameAck() { - DCHECK_NE(base::TimeTicks(), submit_start_time_); - base::TimeDelta submit_to_ack_duration = Now() - submit_start_time_; - uma_reporter_->AddSubmitToAckLatency(submit_to_ack_duration); - submit_start_time_ = base::TimeTicks(); -} - void CompositorTimingHistory::DidPresentCompositorFrame( uint32_t frame_token, const viz::FrameTimingDetails& details) { diff --git a/chromium/cc/metrics/compositor_timing_history.h b/chromium/cc/metrics/compositor_timing_history.h index 93a06913241..15b1ff0050f 100644 --- a/chromium/cc/metrics/compositor_timing_history.h +++ b/chromium/cc/metrics/compositor_timing_history.h @@ -70,7 +70,6 @@ class CC_EXPORT CompositorTimingHistory { // State that affects when events should be expected/recorded/reported. void SetRecordingEnabled(bool enabled); - void DidCreateAndInitializeLayerTreeFrameSink(); // Events to be timed. void WillBeginImplFrame(const viz::BeginFrameArgs& args, @@ -99,7 +98,6 @@ class CC_EXPORT CompositorTimingHistory { EventMetricsSet events_metrics); void DidNotProduceFrame(const viz::BeginFrameId& id, FrameSkippedReason skip_reason); - void DidReceiveCompositorFrameAck(); void DidPresentCompositorFrame(uint32_t frame_token, const viz::FrameTimingDetails& details); void WillInvalidateOnImplSide(); @@ -157,7 +155,6 @@ class CC_EXPORT CompositorTimingHistory { base::TimeTicks prepare_tiles_start_time_; base::TimeTicks activate_start_time_; base::TimeTicks draw_start_time_; - base::TimeTicks submit_start_time_; bool pending_tree_is_impl_side_; diff --git a/chromium/cc/metrics/dropped_frame_counter.cc b/chromium/cc/metrics/dropped_frame_counter.cc index bc9e116bcf8..357acc6e903 100644 --- a/chromium/cc/metrics/dropped_frame_counter.cc +++ b/chromium/cc/metrics/dropped_frame_counter.cc @@ -4,7 +4,13 @@ #include "cc/metrics/dropped_frame_counter.h" +#include <algorithm> +#include <cmath> + #include "base/bind.h" +#include "base/logging.h" +#include "base/metrics/histogram.h" +#include "base/metrics/histogram_macros.h" #include "base/trace_event/trace_event.h" #include "cc/metrics/frame_sorter.h" #include "cc/metrics/total_frame_counter.h" @@ -12,6 +18,53 @@ namespace cc { +using SlidingWindowHistogram = DroppedFrameCounter::SlidingWindowHistogram; + +void SlidingWindowHistogram::AddPercentDroppedFrame( + double percent_dropped_frame, + size_t count) { + DCHECK_GE(percent_dropped_frame, 0.0); + DCHECK_GE(100.0, percent_dropped_frame); + histogram_bins_[static_cast<int>(std::round(percent_dropped_frame))] += count; + total_count_ += count; +} + +uint32_t SlidingWindowHistogram::GetPercentDroppedFramePercentile( + double percentile) const { + if (total_count_ == 0) + return 0; + DCHECK_GE(percentile, 0.0); + DCHECK_GE(1.0, percentile); + int current_index = 100; // Last bin in historgam + uint32_t skipped_counter = histogram_bins_[current_index]; // Last bin values + double samples_to_skip = ((1 - percentile) * total_count_); + // We expect this method to calculate higher end percentiles such 95 and as a + // result we count from the last bin to find the correct bin. + while (skipped_counter < samples_to_skip && current_index > 0) { + current_index--; + skipped_counter += histogram_bins_[current_index]; + } + return current_index; +} + +void SlidingWindowHistogram::Clear() { + std::fill(std::begin(histogram_bins_), std::end(histogram_bins_), 0); + total_count_ = 0; +} + +std::ostream& SlidingWindowHistogram::Dump(std::ostream& stream) const { + for (size_t i = 0; i < base::size(histogram_bins_); ++i) { + stream << i << ": " << histogram_bins_[i] << std::endl; + } + return stream << "Total: " << total_count_; +} + +std::ostream& operator<<( + std::ostream& stream, + const DroppedFrameCounter::SlidingWindowHistogram& histogram) { + return histogram.Dump(stream); +} + DroppedFrameCounter::DroppedFrameCounter() : frame_sorter_(base::BindRepeating(&DroppedFrameCounter::NotifyFrameResult, base::Unretained(this))) {} @@ -44,22 +97,100 @@ void DroppedFrameCounter::AddDroppedFrame() { ++total_dropped_; } -void DroppedFrameCounter::ResetFrameSorter() { +void DroppedFrameCounter::ResetPendingFrames(base::TimeTicks timestamp) { + // Before resetting the pending frames, update the measurements for the + // sliding windows. + if (!latest_sliding_window_start_.is_null()) { + const auto report_until = timestamp - kSlidingWindowInterval; + // Report the sliding window metrics for frames that have already been + // completed (and some of which may have been dropped). + while (!sliding_window_.empty()) { + const auto& args = sliding_window_.front().first; + latest_sliding_window_start_ = args.frame_time; + latest_sliding_window_interval_ = args.interval; + bool was_dropped = sliding_window_.front().second; + if (was_dropped) { + DCHECK_GT(dropped_frame_count_in_window_, 0u); + --dropped_frame_count_in_window_; + } + sliding_window_.pop(); + if (latest_sliding_window_start_ > report_until) + break; + double percent_dropped_frame = std::min( + (dropped_frame_count_in_window_ * 100.0) / total_frames_in_window_, + 100.0); + sliding_window_histogram_.AddPercentDroppedFrame(percent_dropped_frame, + /*count=*/1); + } + if (sliding_window_.empty()) { + DCHECK_EQ(dropped_frame_count_in_window_, 0u); + } + + // Report no dropped frames for the sliding windows spanning the rest of the + // time. + if (latest_sliding_window_start_ < report_until) { + const auto difference = report_until - latest_sliding_window_start_; + const size_t count = + std::ceil(difference / latest_sliding_window_interval_); + if (count > 0) + sliding_window_histogram_.AddPercentDroppedFrame(0., count); + } + } + + dropped_frame_count_in_window_ = 0; + sliding_window_ = {}; + latest_sliding_window_start_ = {}; + latest_sliding_window_interval_ = {}; frame_sorter_.Reset(); } -void DroppedFrameCounter::OnBeginFrame(const viz::BeginFrameArgs& args) { - if (fcp_received_) +void DroppedFrameCounter::OnBeginFrame(const viz::BeginFrameArgs& args, + bool is_scroll_active) { + // Remember when scrolling starts/ends. Do this even if fcp has not happened + // yet. + if (!is_scroll_active) { + scroll_start_.reset(); + } else if (!scroll_start_.has_value()) { + ScrollStartInfo info = {args.frame_time, args.frame_id}; + scroll_start_ = info; + } + + if (fcp_received_) { frame_sorter_.AddNewFrame(args); + if (is_scroll_active) { + DCHECK(scroll_start_.has_value()); + scroll_start_per_frame_[args.frame_id] = *scroll_start_; + } + } } void DroppedFrameCounter::OnEndFrame(const viz::BeginFrameArgs& args, bool is_dropped) { + if (!args.interval.is_zero()) + total_frames_in_window_ = kSlidingWindowInterval / args.interval; + if (is_dropped) { if (fcp_received_) ++total_smoothness_dropped_; ReportFrames(); } + auto iter = scroll_start_per_frame_.find(args.frame_id); + if (iter != scroll_start_per_frame_.end()) { + ScrollStartInfo& scroll_start = iter->second; + if (args.frame_id.source_id == scroll_start.frame_id.source_id) { + UMA_HISTOGRAM_CUSTOM_TIMES( + "Graphics.Smoothness.Diagnostic.DroppedFrameAfterScrollStart.Time", + (args.frame_time - scroll_start.timestamp), + base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromSeconds(4), + 50); + UMA_HISTOGRAM_CUSTOM_COUNTS( + "Graphics.Smoothness.Diagnostic.DroppedFrameAfterScrollStart.Frames", + (args.frame_id.sequence_number - + scroll_start.frame_id.sequence_number), + 1, 250, 50); + } + scroll_start_per_frame_.erase(iter); + } if (fcp_received_) frame_sorter_.AddFrameResult(args, is_dropped); @@ -70,20 +201,52 @@ void DroppedFrameCounter::ReportFrames() { total_counter_->ComputeTotalVisibleFrames(base::TimeTicks::Now()); TRACE_EVENT2("cc,benchmark", "SmoothnessDroppedFrame", "total", total_frames, "smoothness", total_smoothness_dropped_); + UMA_HISTOGRAM_PERCENTAGE( + "Graphics.Smoothness.MaxPercentDroppedFrames_1sWindow", + sliding_window_max_percent_dropped_); + + uint32_t sliding_window_95pct_percent_dropped = + SlidingWindow95PercentilePercentDropped(); + UMA_HISTOGRAM_PERCENTAGE( + "Graphics.Smoothness.95pctPercentDroppedFrames_1sWindow", + sliding_window_95pct_percent_dropped); + + DCHECK_LE( + sliding_window_95pct_percent_dropped, + static_cast<uint32_t>(std::round(sliding_window_max_percent_dropped_))); + + // Emit trace event with most recent smoothness calculation. This matches + // the smoothness metrics displayed on HeadsUpDisplay. + TRACE_EVENT2("cc,benchmark", "SmoothnessDroppedFrame::MostRecentCalculation", + "worst_smoothness", sliding_window_max_percent_dropped_, + "95_percentile_smoothness", + sliding_window_95pct_percent_dropped); if (ukm_smoothness_data_ && total_frames > 0) { UkmSmoothnessData smoothness_data; smoothness_data.avg_smoothness = static_cast<double>(total_smoothness_dropped_) * 100 / total_frames; - - ukm_smoothness_data_->seq_lock.WriteBegin(); - device::OneWriterSeqLock::AtomicWriterMemcpy(&ukm_smoothness_data_->data, - &smoothness_data, - sizeof(UkmSmoothnessData)); - ukm_smoothness_data_->seq_lock.WriteEnd(); + smoothness_data.worst_smoothness = sliding_window_max_percent_dropped_; + smoothness_data.percentile_95 = sliding_window_95pct_percent_dropped; + smoothness_data.time_max_delta = time_max_delta_; + ukm_smoothness_data_->Write(smoothness_data); } } +double DroppedFrameCounter::GetMostRecentAverageSmoothness() const { + if (ukm_smoothness_data_) + return ukm_smoothness_data_->data.avg_smoothness; + + return -1.f; +} + +double DroppedFrameCounter::GetMostRecent95PercentileSmoothness() const { + if (ukm_smoothness_data_) + return ukm_smoothness_data_->data.percentile_95; + + return -1.f; +} + void DroppedFrameCounter::SetUkmSmoothnessDestination( UkmSmoothnessDataShared* smoothness_data) { ukm_smoothness_data_ = smoothness_data; @@ -94,18 +257,93 @@ void DroppedFrameCounter::Reset() { total_partial_ = 0; total_dropped_ = 0; total_smoothness_dropped_ = 0; + sliding_window_max_percent_dropped_ = 0; + dropped_frame_count_in_window_ = 0; fcp_received_ = false; + sliding_window_ = {}; + latest_sliding_window_start_ = {}; + sliding_window_histogram_.Clear(); ring_buffer_.Clear(); frame_sorter_.Reset(); + time_max_delta_ = {}; +} + +base::TimeDelta DroppedFrameCounter::ComputeCurrentWindowSize() const { + if (sliding_window_.empty()) + return {}; + return sliding_window_.back().first.frame_time + + sliding_window_.back().first.interval - + sliding_window_.front().first.frame_time; } void DroppedFrameCounter::NotifyFrameResult(const viz::BeginFrameArgs& args, bool is_dropped) { - // TODO(crbug.com/1115141) The implementation of smoothness metrics. + // Entirely disregard the frames with interval larger than the window -- + // these are violating the assumptions in the below code and should + // only occur with external frame control, where dropped frame stats + // are not relevant. + if (args.interval >= kSlidingWindowInterval) + return; + + sliding_window_.push({args, is_dropped}); + + if (ComputeCurrentWindowSize() < kSlidingWindowInterval) { + if (is_dropped) + ++dropped_frame_count_in_window_; + return; + } + + DCHECK_GE(dropped_frame_count_in_window_, 0u); + DCHECK_GE(sliding_window_.size(), dropped_frame_count_in_window_); + + const auto max_sliding_window_start = + args.frame_time - kSlidingWindowInterval; + const auto max_difference = args.interval * 1.5; + while (ComputeCurrentWindowSize() > kSlidingWindowInterval) { + const auto removed_args = sliding_window_.front().first; + const auto removed_was_dropped = sliding_window_.front().second; + if (removed_was_dropped) { + DCHECK_GT(dropped_frame_count_in_window_, 0u); + --dropped_frame_count_in_window_; + } + sliding_window_.pop(); + DCHECK(!sliding_window_.empty()); + + auto dropped = dropped_frame_count_in_window_; + if (ComputeCurrentWindowSize() <= kSlidingWindowInterval && is_dropped) + ++dropped; + + // If two consecutive 'completed' frames are far apart from each other (in + // time), then report the 'dropped frame count' for the sliding window(s) in + // between. Note that the window-size still needs to be at least + // kSlidingWindowInterval. + const auto& remaining_oldest_args = sliding_window_.front().first; + const auto last_timestamp = + std::min(remaining_oldest_args.frame_time, max_sliding_window_start); + const auto difference = last_timestamp - removed_args.frame_time; + const size_t count = + difference > max_difference ? std::ceil(difference / args.interval) : 1; + double percent_dropped_frame = + std::min((dropped * 100.0) / total_frames_in_window_, 100.0); + sliding_window_histogram_.AddPercentDroppedFrame(percent_dropped_frame, + count); + + if (percent_dropped_frame > sliding_window_max_percent_dropped_) { + time_max_delta_ = args.frame_time - time_fcp_received_; + sliding_window_max_percent_dropped_ = percent_dropped_frame; + } + + latest_sliding_window_start_ = last_timestamp; + latest_sliding_window_interval_ = remaining_oldest_args.interval; + } + + if (is_dropped) + ++dropped_frame_count_in_window_; } void DroppedFrameCounter::OnFcpReceived() { fcp_received_ = true; + time_fcp_received_ = base::TimeTicks::Now(); } } // namespace cc diff --git a/chromium/cc/metrics/dropped_frame_counter.h b/chromium/cc/metrics/dropped_frame_counter.h index 17e9386a508..7126c32c46d 100644 --- a/chromium/cc/metrics/dropped_frame_counter.h +++ b/chromium/cc/metrics/dropped_frame_counter.h @@ -6,14 +6,16 @@ #define CC_METRICS_DROPPED_FRAME_COUNTER_H_ #include <stddef.h> +#include <queue> +#include <utility> #include "base/containers/ring_buffer.h" #include "cc/cc_export.h" #include "cc/metrics/frame_sorter.h" +#include "cc/metrics/ukm_smoothness_data.h" namespace cc { class TotalFrameCounter; -struct UkmSmoothnessDataShared; // This class maintains a counter for produced/dropped frames, and can be used // to estimate the recent throughput. @@ -25,6 +27,20 @@ class CC_EXPORT DroppedFrameCounter { kFrameStateComplete }; + class CC_EXPORT SlidingWindowHistogram { + public: + void AddPercentDroppedFrame(double percent_dropped_frame, size_t count = 1); + uint32_t GetPercentDroppedFramePercentile(double percentile) const; + void Clear(); + std::ostream& Dump(std::ostream& stream) const; + + uint32_t total_count() const { return total_count_; } + + private: + uint32_t histogram_bins_[101] = {0}; + uint32_t total_count_ = 0; + }; + DroppedFrameCounter(); ~DroppedFrameCounter(); @@ -39,6 +55,9 @@ class CC_EXPORT DroppedFrameCounter { uint32_t GetAverageThroughput() const; + double GetMostRecentAverageSmoothness() const; + double GetMostRecent95PercentileSmoothness() const; + typedef base::RingBuffer<FrameState, 180> RingBufferType; RingBufferType::Iterator begin() const { return ring_buffer_.Begin(); } RingBufferType::Iterator end() const { return ring_buffer_.End(); } @@ -48,7 +67,7 @@ class CC_EXPORT DroppedFrameCounter { void AddDroppedFrame(); void ReportFrames(); - void OnBeginFrame(const viz::BeginFrameArgs& args); + void OnBeginFrame(const viz::BeginFrameArgs& args, bool is_scroll_active); void OnEndFrame(const viz::BeginFrameArgs& args, bool is_dropped); void SetUkmSmoothnessDestination(UkmSmoothnessDataShared* smoothness_data); void OnFcpReceived(); @@ -56,16 +75,42 @@ class CC_EXPORT DroppedFrameCounter { // Reset is used on navigation, which resets frame statistics as well as // frame sorter. void Reset(); - // ResetFrameSorter is used when we need to keep track of frame statistics - // but not to track the frames prior to reset in frame sorter. - void ResetFrameSorter(); + + // ResetPendingFrames is used when we need to keep track of frame statistics, + // but should no longer wait for the pending frames (e.g. connection to + // gpu-process was reset, or the page became invisible, etc.). The pending + // frames are not considered to be dropped. + void ResetPendingFrames(base::TimeTicks timestamp); void set_total_counter(TotalFrameCounter* total_counter) { total_counter_ = total_counter; } + double sliding_window_max_percent_dropped() const { + return sliding_window_max_percent_dropped_; + } + + uint32_t SlidingWindow95PercentilePercentDropped() const { + return sliding_window_histogram_.GetPercentDroppedFramePercentile(0.95); + } + + const SlidingWindowHistogram* GetSlidingWindowHistogram() const { + return &sliding_window_histogram_; + } + private: void NotifyFrameResult(const viz::BeginFrameArgs& args, bool is_dropped); + base::TimeDelta ComputeCurrentWindowSize() const; + + const base::TimeDelta kSlidingWindowInterval = + base::TimeDelta::FromSeconds(1); + std::queue<std::pair<const viz::BeginFrameArgs, bool>> sliding_window_; + uint32_t dropped_frame_count_in_window_ = 0; + double total_frames_in_window_ = 60.0; + SlidingWindowHistogram sliding_window_histogram_; + + base::TimeTicks latest_sliding_window_start_; + base::TimeDelta latest_sliding_window_interval_; RingBufferType ring_buffer_; size_t total_frames_ = 0; @@ -73,12 +118,28 @@ class CC_EXPORT DroppedFrameCounter { size_t total_dropped_ = 0; size_t total_smoothness_dropped_ = 0; bool fcp_received_ = false; - + double sliding_window_max_percent_dropped_ = 0; + base::TimeTicks time_fcp_received_; + base::TimeDelta time_max_delta_; UkmSmoothnessDataShared* ukm_smoothness_data_ = nullptr; FrameSorter frame_sorter_; TotalFrameCounter* total_counter_ = nullptr; + + struct ScrollStartInfo { + // The timestamp of when the scroll started. + base::TimeTicks timestamp; + + // The vsync corresponding to the scroll-start. + viz::BeginFrameId frame_id; + }; + base::Optional<ScrollStartInfo> scroll_start_; + std::map<viz::BeginFrameId, ScrollStartInfo> scroll_start_per_frame_; }; +CC_EXPORT std::ostream& operator<<( + std::ostream&, + const DroppedFrameCounter::SlidingWindowHistogram&); + } // namespace cc #endif // CC_METRICS_DROPPED_FRAME_COUNTER_H_ diff --git a/chromium/cc/metrics/dropped_frame_counter_unittest.cc b/chromium/cc/metrics/dropped_frame_counter_unittest.cc index 99d5c35a35a..881eff48090 100644 --- a/chromium/cc/metrics/dropped_frame_counter_unittest.cc +++ b/chromium/cc/metrics/dropped_frame_counter_unittest.cc @@ -4,8 +4,11 @@ #include "cc/metrics/dropped_frame_counter.h" +#include <vector> + #include "base/synchronization/lock.h" #include "base/synchronization/waitable_event.h" +#include "base/time/time.h" #include "cc/animation/animation_host.h" #include "cc/test/fake_content_layer_client.h" #include "cc/test/fake_picture_layer.h" @@ -247,5 +250,251 @@ class DroppedFrameCounterMainDropsSmoothnessTest // TODO(crbug.com/1115376) Disabled for flakiness. // MULTI_THREAD_TEST_F(DroppedFrameCounterMainDropsSmoothnessTest); +class DroppedFrameCounterTest : public testing::Test { + public: + DroppedFrameCounterTest() { + dropped_frame_counter_.set_total_counter(&total_frame_counter_); + dropped_frame_counter_.OnFcpReceived(); + } + ~DroppedFrameCounterTest() override = default; + + // For each boolean in frame_states produces a frame + void SimulateFrameSequence(std::vector<bool> frame_states, int repeat) { + for (int i = 0; i < repeat; i++) { + for (auto is_dropped : frame_states) { + viz::BeginFrameArgs args_ = SimulateBeginFrameArgs(); + dropped_frame_counter_.OnBeginFrame(args_, /*is_scroll_active=*/false); + dropped_frame_counter_.OnEndFrame(args_, is_dropped); + sequence_number_++; + frame_time_ += interval_; + } + } + } + + void AdvancetimeByIntervals(int interval_count) { + frame_time_ += interval_ * interval_count; + } + + double MaxPercentDroppedFrame() { + return dropped_frame_counter_.sliding_window_max_percent_dropped(); + } + + double PercentDroppedFrame95Percentile() { + return dropped_frame_counter_.SlidingWindow95PercentilePercentDropped(); + } + + double GetTotalFramesInWindow() { + return base::TimeDelta::FromSeconds(1) / interval_; + } + + void SetInterval(base::TimeDelta interval) { interval_ = interval; } + + base::TimeTicks GetNextFrameTime() const { return frame_time_ + interval_; } + + public: + DroppedFrameCounter dropped_frame_counter_; + + private: + TotalFrameCounter total_frame_counter_; + uint64_t sequence_number_ = 1; + uint64_t source_id_ = 1; + const base::TickClock* tick_clock_ = base::DefaultTickClock::GetInstance(); + base::TimeTicks frame_time_ = tick_clock_->NowTicks(); + base::TimeDelta interval_ = + base::TimeDelta::FromMicroseconds(16667); // 16.667 ms + + viz::BeginFrameArgs SimulateBeginFrameArgs() { + viz::BeginFrameId current_id_(source_id_, sequence_number_); + viz::BeginFrameArgs args = viz::BeginFrameArgs(); + args.frame_id = current_id_; + args.frame_time = frame_time_; + args.interval = interval_; + return args; + } +}; + +TEST_F(DroppedFrameCounterTest, SimplePattern1) { + // 2 out of every 3 frames are dropped (In total 80 frames out of 120). + SimulateFrameSequence({true, true, true, false, true, false}, 20); + + // The max is the following window: + // 16 * <sequence> + {true, true, true, false + // Which means a max of 67 dropped frames. + EXPECT_EQ(std::round(MaxPercentDroppedFrame()), 67); + EXPECT_EQ(PercentDroppedFrame95Percentile(), 67); // all values are in the + // 67th bucket, and as a result 95th percentile is also 67. +} + +TEST_F(DroppedFrameCounterTest, SimplePattern2) { + // 1 out of every 5 frames are dropped (In total 24 frames out of 120). + SimulateFrameSequence({false, false, false, false, true}, 24); + + double expected_percent_dropped_frame = (12 / GetTotalFramesInWindow()) * 100; + EXPECT_FLOAT_EQ(MaxPercentDroppedFrame(), expected_percent_dropped_frame); + EXPECT_EQ(PercentDroppedFrame95Percentile(), 20); // all values are in the + // 20th bucket, and as a result 95th percentile is also 20. +} + +TEST_F(DroppedFrameCounterTest, IncompleteWindow) { + // There are only 5 frames submitted and both Max and 95pct should report + // zero. + SimulateFrameSequence({false, false, false, false, true}, 1); + EXPECT_EQ(MaxPercentDroppedFrame(), 0.0); + EXPECT_EQ(PercentDroppedFrame95Percentile(), 0); +} + +TEST_F(DroppedFrameCounterTest, MaxPercentDroppedChanges) { + // First 60 frames have 20% dropped. + SimulateFrameSequence({false, false, false, false, true}, 12); + + double expected_percent_dropped_frame1 = + (12 / GetTotalFramesInWindow()) * 100; + EXPECT_EQ(MaxPercentDroppedFrame(), expected_percent_dropped_frame1); + EXPECT_FLOAT_EQ(PercentDroppedFrame95Percentile(), 20); // There is only one + // element in the histogram and that is 20. + + // 30 new frames are added that have 18 dropped frames. + // and the 30 frame before that had 6 dropped frames. + // So in total in the window has 24 frames dropped out of 60 frames. + SimulateFrameSequence({false, false, true, true, true}, 6); + double expected_percent_dropped_frame2 = + (24 / GetTotalFramesInWindow()) * 100; + EXPECT_FLOAT_EQ(MaxPercentDroppedFrame(), expected_percent_dropped_frame2); + + // 30 new frames are added that have 24 dropped frames. + // and the 30 frame before that had 18 dropped frames. + // So in total in the window has 42 frames dropped out of 60 frames. + SimulateFrameSequence({false, true, true, true, true}, 6); + double expected_percent_dropped_frame3 = + (42 / GetTotalFramesInWindow()) * 100; + EXPECT_FLOAT_EQ(MaxPercentDroppedFrame(), expected_percent_dropped_frame3); + + // Percent dropped frame of window increases gradually to 70%. + // 1 value exist when we reach 60 frames and 1 value thereafter for each + // frame added. So there 61 values in histogram. Last value is 70 (2 sampels) + // and then 67 with 1 sample, which would be the 95th percentile. + EXPECT_EQ(PercentDroppedFrame95Percentile(), 67); +} + +TEST_F(DroppedFrameCounterTest, MaxPercentDroppedWithIdleFrames) { + // First 20 frames have 4 frames dropped (20%). + SimulateFrameSequence({false, false, false, false, true}, 4); + + // Then no frames are added for 20 intervals. + AdvancetimeByIntervals(20); + + // Then 20 frames have 16 frames dropped (60%). + SimulateFrameSequence({false, false, true, true, true}, 4); + + // So in total, there are 40 frames in the 1 second window with 16 dropped + // frames (40% in total). + double expected_percent_dropped_frame = (16 / GetTotalFramesInWindow()) * 100; + EXPECT_FLOAT_EQ(MaxPercentDroppedFrame(), expected_percent_dropped_frame); +} + +TEST_F(DroppedFrameCounterTest, NoCrashForIntervalLargerThanWindow) { + SetInterval(base::TimeDelta::FromMilliseconds(1000)); + SimulateFrameSequence({false, false}, 1); +} + +TEST_F(DroppedFrameCounterTest, Percentile95WithIdleFrames) { + // Test scenario: + // . 4s of 20% dropped frames. + // . 96s of idle time. + // The 96%ile dropped-frame metric should be 0. + + // Set an interval that rounds up nicely with 1 second. + constexpr auto kInterval = base::TimeDelta::FromMilliseconds(10); + constexpr size_t kFps = base::TimeDelta::FromSeconds(1) / kInterval; + static_assert( + kFps % 5 == 0, + "kFps must be a multiple of 5 because this test depends on it."); + SetInterval(kInterval); + + const auto* histogram = dropped_frame_counter_.GetSlidingWindowHistogram(); + + // First 4 seconds with 20% dropped frames. + SimulateFrameSequence({false, false, false, false, true}, (kFps / 5) * 4); + EXPECT_EQ(histogram->GetPercentDroppedFramePercentile(0.95), 20u); + + // Then no frames are added for 97s. Note that this 1s more than 96 seconds, + // because the last second remains in the sliding window. + AdvancetimeByIntervals(kFps * 97); + + // A single frame to flush the pipeline. + SimulateFrameSequence({false}, 1); + + EXPECT_EQ(histogram->total_count(), 100u * kFps); + EXPECT_EQ(histogram->GetPercentDroppedFramePercentile(0.96), 0u); + EXPECT_GT(histogram->GetPercentDroppedFramePercentile(0.97), 0u); +} + +TEST_F(DroppedFrameCounterTest, Percentile95WithIdleFramesWhileHidden) { + // The test scenario is the same as |Percentile95WithIdleFrames| test: + // . 4s of 20% dropped frames. + // . 96s of idle time. + // However, the 96s of idle time happens *after* the page becomes invisible + // (e.g. after a tab-switch). In this case, the idle time *should not* + // contribute to the sliding window. + + // Set an interval that rounds up nicely with 1 second. + constexpr auto kInterval = base::TimeDelta::FromMilliseconds(10); + constexpr size_t kFps = base::TimeDelta::FromSeconds(1) / kInterval; + static_assert( + kFps % 5 == 0, + "kFps must be a multiple of 5 because this test depends on it."); + SetInterval(kInterval); + + const auto* histogram = dropped_frame_counter_.GetSlidingWindowHistogram(); + + // First 4 seconds with 20% dropped frames. + SimulateFrameSequence({false, false, false, false, true}, (kFps / 5) * 4); + EXPECT_EQ(histogram->GetPercentDroppedFramePercentile(0.95), 20u); + + // Hide the page (thus resetting the pending frames), then idle for 96s before + // producing a single frame. + dropped_frame_counter_.ResetPendingFrames(GetNextFrameTime()); + AdvancetimeByIntervals(kFps * 97); + + // A single frame to flush the pipeline. + SimulateFrameSequence({false}, 1); + + EXPECT_EQ(histogram->GetPercentDroppedFramePercentile(0.95), 20u); +} + +TEST_F(DroppedFrameCounterTest, Percentile95WithIdleFramesThenHide) { + // The test scenario is the same as |Percentile95WithIdleFramesWhileHidden|: + // . 4s of 20% dropped frames. + // . 96s of idle time. + // However, the 96s of idle time happens *before* the page becomes invisible + // (e.g. after a tab-switch). In this case, the idle time *should* + // contribute to the sliding window. + + // Set an interval that rounds up nicely with 1 second. + constexpr auto kInterval = base::TimeDelta::FromMilliseconds(10); + constexpr size_t kFps = base::TimeDelta::FromSeconds(1) / kInterval; + static_assert( + kFps % 5 == 0, + "kFps must be a multiple of 5 because this test depends on it."); + SetInterval(kInterval); + + const auto* histogram = dropped_frame_counter_.GetSlidingWindowHistogram(); + + // First 4 seconds with 20% dropped frames. + SimulateFrameSequence({false, false, false, false, true}, (kFps / 5) * 4); + EXPECT_EQ(histogram->GetPercentDroppedFramePercentile(0.95), 20u); + + // Idle for 96s before hiding the page. + AdvancetimeByIntervals(kFps * 97); + dropped_frame_counter_.ResetPendingFrames(GetNextFrameTime()); + AdvancetimeByIntervals(kFps * 97); + + // A single frame to flush the pipeline. + SimulateFrameSequence({false}, 1); + + EXPECT_EQ(histogram->GetPercentDroppedFramePercentile(0.96), 0u); + EXPECT_GT(histogram->GetPercentDroppedFramePercentile(0.97), 0u); +} + } // namespace } // namespace cc diff --git a/chromium/cc/metrics/event_metrics.cc b/chromium/cc/metrics/event_metrics.cc index b4cac6065cc..b331dc0a0f5 100644 --- a/chromium/cc/metrics/event_metrics.cc +++ b/chromium/cc/metrics/event_metrics.cc @@ -4,12 +4,14 @@ #include "cc/metrics/event_metrics.h" +#include <algorithm> #include <utility> #include "base/check.h" #include "base/memory/ptr_util.h" #include "base/notreached.h" #include "base/stl_util.h" +#include "base/time/default_tick_clock.h" namespace cc { namespace { @@ -48,6 +50,10 @@ constexpr struct { EVENT_TYPE(FirstGestureScrollUpdate, ui::ET_GESTURE_SCROLL_UPDATE, EventMetrics::ScrollUpdateType::kStarted), + EVENT_TYPE(MouseDragged, ui::ET_MOUSE_DRAGGED), + EVENT_TYPE(GesturePinchBegin, ui::ET_GESTURE_PINCH_BEGIN), + EVENT_TYPE(GesturePinchEnd, ui::ET_GESTURE_PINCH_END), + EVENT_TYPE(GesturePinchUpdate, ui::ET_GESTURE_PINCH_UPDATE), #undef EVENT_TYPE }; static_assert(base::size(kInterestingEvents) == @@ -106,11 +112,85 @@ base::Optional<EventMetrics::ScrollType> ToScrollType( } // namespace +// static std::unique_ptr<EventMetrics> EventMetrics::Create( ui::EventType type, base::Optional<ScrollUpdateType> scroll_update_type, - base::TimeTicks time_stamp, - base::Optional<ui::ScrollInputType> scroll_input_type) { + base::Optional<ui::ScrollInputType> scroll_input_type, + base::TimeTicks timestamp) { + // TODO(crbug.com/1157090): We expect that `timestamp` is not null, but there + // seems to be some tests that are emitting events with null timestamp. We + // should investigate and try to fix those cases and add a `DCHECK` here to + // assert `timestamp` is not null. + + std::unique_ptr<EventMetrics> metrics = + CreateInternal(type, scroll_update_type, scroll_input_type, timestamp, + base::DefaultTickClock::GetInstance()); + if (!metrics) + return nullptr; + + metrics->SetDispatchStageTimestamp( + DispatchStage::kArrivedInRendererCompositor); + return metrics; +} + +// static +std::unique_ptr<EventMetrics> EventMetrics::CreateForTesting( + ui::EventType type, + base::Optional<ScrollUpdateType> scroll_update_type, + base::Optional<ui::ScrollInputType> scroll_input_type, + base::TimeTicks timestamp, + const base::TickClock* tick_clock) { + DCHECK(!timestamp.is_null()); + + std::unique_ptr<EventMetrics> metrics = CreateInternal( + type, scroll_update_type, scroll_input_type, timestamp, tick_clock); + if (!metrics) + return nullptr; + + metrics->SetDispatchStageTimestamp( + DispatchStage::kArrivedInRendererCompositor); + return metrics; +} + +// static +std::unique_ptr<EventMetrics> EventMetrics::CreateFromExisting( + ui::EventType type, + base::Optional<ScrollUpdateType> scroll_update_type, + base::Optional<ui::ScrollInputType> scroll_input_type, + DispatchStage last_dispatch_stage, + const EventMetrics* existing) { + std::unique_ptr<EventMetrics> metrics = CreateInternal( + type, scroll_update_type, scroll_input_type, base::TimeTicks(), + existing ? existing->tick_clock_ : base::DefaultTickClock::GetInstance()); + if (!metrics) + return nullptr; + + // Since the new event is of an interesting type, we expect the existing event + // to be of an interesting type, too; which means `existing` should not be + // nullptr. However, some tests that are not interested in reporting metrics, + // don't create metrics objects even for events of interesting types. Return + // nullptr if that's the case. + if (!existing) + return nullptr; + + // Use timestamps of all stages (including "Generated" stage) up to + // `last_dispatch_stage` from `existing`. + for (size_t stage_index = static_cast<size_t>(DispatchStage::kGenerated); + stage_index <= static_cast<size_t>(last_dispatch_stage); stage_index++) { + metrics->dispatch_stage_timestamps_[stage_index] = + existing->dispatch_stage_timestamps_[stage_index]; + } + return metrics; +} + +// static +std::unique_ptr<EventMetrics> EventMetrics::CreateInternal( + ui::EventType type, + base::Optional<ScrollUpdateType> scroll_update_type, + base::Optional<ui::ScrollInputType> scroll_input_type, + base::TimeTicks timestamp, + const base::TickClock* tick_clock) { // `scroll_update_type` should be set for and only for // `ui::ET_GESTURE_SCROLL_UPDATE`. DCHECK(type == ui::ET_GESTURE_SCROLL_UPDATE && scroll_update_type || @@ -119,14 +199,21 @@ std::unique_ptr<EventMetrics> EventMetrics::Create( ToInterestingEventType(type, scroll_update_type); if (!interesting_type) return nullptr; - return base::WrapUnique(new EventMetrics(*interesting_type, time_stamp, - ToScrollType(scroll_input_type))); + return base::WrapUnique(new EventMetrics(*interesting_type, + ToScrollType(scroll_input_type), + timestamp, tick_clock)); } EventMetrics::EventMetrics(EventType type, - base::TimeTicks time_stamp, - base::Optional<ScrollType> scroll_type) - : type_(type), time_stamp_(time_stamp), scroll_type_(scroll_type) {} + base::Optional<ScrollType> scroll_type, + base::TimeTicks timestamp, + const base::TickClock* tick_clock) + : type_(type), scroll_type_(scroll_type), tick_clock_(tick_clock) { + dispatch_stage_timestamps_[static_cast<int>(DispatchStage::kGenerated)] = + timestamp; +} + +EventMetrics::~EventMetrics() = default; const char* EventMetrics::GetTypeName() const { return kInterestingEvents[static_cast<int>(type_)].name; @@ -138,9 +225,47 @@ const char* EventMetrics::GetScrollTypeName() const { return kScrollTypes[static_cast<int>(*scroll_type_)].name; } +void EventMetrics::SetDispatchStageTimestamp(DispatchStage stage) { + DCHECK(dispatch_stage_timestamps_[static_cast<size_t>(stage)].is_null()); + + dispatch_stage_timestamps_[static_cast<size_t>(stage)] = + tick_clock_->NowTicks(); +} + +base::TimeTicks EventMetrics::GetDispatchStageTimestamp( + DispatchStage stage) const { + return dispatch_stage_timestamps_[static_cast<size_t>(stage)]; +} + +void EventMetrics::ResetToDispatchStage(DispatchStage stage) { + for (size_t stage_index = static_cast<size_t>(stage) + 1; + stage_index <= static_cast<size_t>(DispatchStage::kMaxValue); + stage_index++) { + dispatch_stage_timestamps_[stage_index] = base::TimeTicks(); + } +} + +bool EventMetrics::ShouldReportScrollingTotalLatency() const { + return type_ == EventType::kGestureScrollBegin || + type_ == EventType::kGestureScrollEnd || + type_ == EventType::kFirstGestureScrollUpdate || + type_ == EventType::kGestureScrollUpdate; +} + +std::unique_ptr<EventMetrics> EventMetrics::Clone() const { + auto clone = base::WrapUnique( + new EventMetrics(type_, scroll_type_, base::TimeTicks(), tick_clock_)); + std::copy(std::begin(dispatch_stage_timestamps_), + std::end(dispatch_stage_timestamps_), + std::begin(clone->dispatch_stage_timestamps_)); + return clone; +} + bool EventMetrics::operator==(const EventMetrics& other) const { - return std::tie(type_, time_stamp_, scroll_type_) == - std::tie(other.type_, other.time_stamp_, other.scroll_type_); + return type_ == other.type_ && scroll_type_ == other.scroll_type_ && + std::equal(std::begin(dispatch_stage_timestamps_), + std::end(dispatch_stage_timestamps_), + std::begin(other.dispatch_stage_timestamps_)); } // EventMetricsSet diff --git a/chromium/cc/metrics/event_metrics.h b/chromium/cc/metrics/event_metrics.h index 6350452616f..d9306946733 100644 --- a/chromium/cc/metrics/event_metrics.h +++ b/chromium/cc/metrics/event_metrics.h @@ -9,6 +9,7 @@ #include <vector> #include "base/optional.h" +#include "base/time/tick_clock.h" #include "base/time/time.h" #include "cc/cc_export.h" #include "ui/events/types/event_type.h" @@ -49,7 +50,11 @@ class CC_EXPORT EventMetrics { kGestureTapUnconfirmed, kGestureTwoFingerTap, kFirstGestureScrollUpdate, - kMaxValue = kFirstGestureScrollUpdate, + kMouseDragged, + kGesturePinchBegin, + kGesturePinchEnd, + kGesturePinchUpdate, + kMaxValue = kGesturePinchUpdate, }; // Type of scroll events. This list should be in the same order as values of @@ -70,44 +75,105 @@ class CC_EXPORT EventMetrics { kMaxValue = kContinued, }; - // Returns a new instance if |type| is an event type we are interested in. + // Stages of event dispatch in different processes/threads. + enum class DispatchStage { + kGenerated, + kArrivedInRendererCompositor, + kRendererCompositorStarted, + kRendererCompositorFinished, + kRendererMainStarted, + kRendererMainFinished, + kMaxValue = kRendererMainFinished, + }; + + // Returns a new instance if the event is of a type we are interested in. // Otherwise, returns nullptr. static std::unique_ptr<EventMetrics> Create( ui::EventType type, base::Optional<ScrollUpdateType> scroll_update_type, - base::TimeTicks time_stamp, - base::Optional<ui::ScrollInputType> scroll_input_type); + base::Optional<ui::ScrollInputType> scroll_input_type, + base::TimeTicks timestamp); + + // Similar to `Create()` with an extra `base::TickClock` to use in tests. + static std::unique_ptr<EventMetrics> CreateForTesting( + ui::EventType type, + base::Optional<ScrollUpdateType> scroll_update_type, + base::Optional<ui::ScrollInputType> scroll_input_type, + base::TimeTicks timestamp, + const base::TickClock* tick_clock); + + // Used to create an instance for an event generated based on an existing + // event. If the new event is of an interesting type, we expect that the + // existing event is also of an interesting type in which case `existing` is + // not nullptr and timestamps (up to and including `last_dispatch_stage`) and + // tick clock from `existing` will be used for the new metrics object. If the + // new event is not an interesting one, return value would be nullptr. + static std::unique_ptr<EventMetrics> CreateFromExisting( + ui::EventType type, + base::Optional<ScrollUpdateType> scroll_update_type, + base::Optional<ui::ScrollInputType> scroll_input_type, + DispatchStage last_dispatch_stage, + const EventMetrics* existing); EventMetrics(const EventMetrics&) = delete; EventMetrics& operator=(const EventMetrics&) = delete; + ~EventMetrics(); + EventType type() const { return type_; } // Returns a string representing event type. const char* GetTypeName() const; - base::TimeTicks time_stamp() const { return time_stamp_; } - const base::Optional<ScrollType>& scroll_type() const { return scroll_type_; } // Returns a string representing input type for a scroll event. Should only be // called for scroll events. const char* GetScrollTypeName() const; + void SetDispatchStageTimestamp(DispatchStage stage); + base::TimeTicks GetDispatchStageTimestamp(DispatchStage stage) const; + + // Resets the metrics object to dispatch stage `stage` by setting timestamps + // of dispatch stages after `stage` to null timestamp, + void ResetToDispatchStage(DispatchStage stage); + + // Determines whether TotalLatencyToSwapBegin metric should be reported for + // this event or not. This metric is only desired for gesture-scroll events. + bool ShouldReportScrollingTotalLatency() const; + + std::unique_ptr<EventMetrics> Clone() const; + // Used in tests to check expectations on EventMetrics objects. bool operator==(const EventMetrics& other) const; private: + static std::unique_ptr<EventMetrics> CreateInternal( + ui::EventType type, + base::Optional<ScrollUpdateType> scroll_update_type, + base::Optional<ui::ScrollInputType> scroll_input_type, + base::TimeTicks timestamp, + const base::TickClock* tick_clock); + EventMetrics(EventType type, - base::TimeTicks time_stamp, - base::Optional<ScrollType> scroll_type); + base::Optional<ScrollType> scroll_type, + base::TimeTicks timestamp, + const base::TickClock* tick_clock); EventType type_; - base::TimeTicks time_stamp_; // Only available for scroll events and represents the type of input device // for the event. base::Optional<ScrollType> scroll_type_; + + const base::TickClock* const tick_clock_; + + // Timestamps of different stages of event dispatch. Timestamps are set as the + // event moves forward in the pipeline. In the end, some stages might not have + // a timestamp which means the event did not pass those stages. + base::TimeTicks + dispatch_stage_timestamps_[static_cast<int>(DispatchStage::kMaxValue) + + 1]; }; // Struct storing event metrics from both main and impl threads. diff --git a/chromium/cc/metrics/events_metrics_manager_unittest.cc b/chromium/cc/metrics/events_metrics_manager_unittest.cc index 5e3f72b8b68..8c7c450cf52 100644 --- a/chromium/cc/metrics/events_metrics_manager_unittest.cc +++ b/chromium/cc/metrics/events_metrics_manager_unittest.cc @@ -9,6 +9,7 @@ #include "base/bind.h" #include "base/stl_util.h" +#include "base/test/simple_test_tick_clock.h" #include "cc/metrics/event_metrics.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -51,15 +52,16 @@ class EventsMetricsManagerTest : public testing::Test { ~EventsMetricsManagerTest() override = default; protected: - base::TimeTicks now() const { return now_; } - - base::TimeTicks AdvanceNowByMs(int ms) { - now_ += base::TimeDelta::FromMilliseconds(ms); - return now_; + std::unique_ptr<EventMetrics> CreateEventMetrics(ui::EventType type) { + test_tick_clock_.Advance(base::TimeDelta::FromMicroseconds(10)); + base::TimeTicks event_time = test_tick_clock_.NowTicks(); + test_tick_clock_.Advance(base::TimeDelta::FromMicroseconds(10)); + return EventMetrics::CreateForTesting(type, base::nullopt, base::nullopt, + event_time, &test_tick_clock_); } EventsMetricsManager manager_; - base::TimeTicks now_ = base::TimeTicks::Now(); + base::SimpleTestTickClock test_tick_clock_; }; // Tests that EventMetrics are saved only if they have an event type we are @@ -75,27 +77,19 @@ TEST_F(EventsMetricsManagerTest, EventsMetricsSaved) { std::pair<std::unique_ptr<EventMetrics>, Behavior> events[] = { // An interesting event type for which SaveActiveEventMetrics() is not // called. - {EventMetrics::Create(ui::ET_MOUSE_PRESSED, base::nullopt, - AdvanceNowByMs(1), base::nullopt), - Behavior::kDoNotSave}, + {CreateEventMetrics(ui::ET_MOUSE_PRESSED), Behavior::kDoNotSave}, // An interesting event type for which SaveActiveEventMetrics() is called // inside its monitor scope. - {EventMetrics::Create(ui::ET_MOUSE_PRESSED, base::nullopt, - AdvanceNowByMs(1), base::nullopt), - Behavior::kSaveInsideScope}, + {CreateEventMetrics(ui::ET_MOUSE_PRESSED), Behavior::kSaveInsideScope}, // An interesting event type for which SaveActiveEventMetrics() is called // after its monitor scope is finished. - {EventMetrics::Create(ui::ET_MOUSE_PRESSED, base::nullopt, - AdvanceNowByMs(1), base::nullopt), - Behavior::kSaveOutsideScope}, + {CreateEventMetrics(ui::ET_MOUSE_PRESSED), Behavior::kSaveOutsideScope}, // A non-interesting event type for which SaveActiveEventMetrics() is // called inside its monitor scope. - {EventMetrics::Create(ui::ET_MOUSE_MOVED, base::nullopt, - AdvanceNowByMs(1), base::nullopt), - Behavior::kSaveInsideScope}, + {CreateEventMetrics(ui::ET_MOUSE_MOVED), Behavior::kSaveInsideScope}, }; EXPECT_NE(events[0].first, nullptr); EXPECT_NE(events[1].first, nullptr); @@ -134,22 +128,16 @@ TEST_F(EventsMetricsManagerTest, EventsMetricsSaved) { // different configurations. TEST_F(EventsMetricsManagerTest, NestedEventsMetrics) { struct { - ui::EventType type; - base::TimeTicks timestamp; - } events[] = { - {ui::ET_MOUSE_PRESSED, AdvanceNowByMs(1)}, - {ui::ET_MOUSE_RELEASED, AdvanceNowByMs(1)}, - }; - - struct { - // Index of event to use for the outer scope. -1 if no event should be used. - int outer_event; + // Type of event to use for the outer scope. `ui::EventType::ET_UNKNOWN` if + // no event should be used. + ui::EventType outer_event_type; // Whether to save the outer scope metrics before starting the inner scope. bool save_outer_metrics_before_inner; - // Index of event to use for the inner scope. -1 if no event should be used. - int inner_event; + // Type of event to use for the inner scope. `ui::EventType::ET_UNKNOWN` if + // no event should be used. + ui::EventType inner_event_type; // Whether to save the inner scope metrics. bool save_inner_metrics; @@ -159,45 +147,45 @@ TEST_F(EventsMetricsManagerTest, NestedEventsMetrics) { } configs[] = { // Config #0. { - /*outer_metrics=*/0, + /*outer_event_type=*/ui::EventType::ET_MOUSE_PRESSED, /*save_outer_metrics_before_inner=*/true, - /*inner_metrics=*/1, + /*inner_event_type=*/ui::EventType::ET_MOUSE_RELEASED, /*save_inner_metrics=*/true, /*save_outer_metrics_after_inner=*/false, }, // Config #1. { - /*outer_metrics=*/0, + /*outer_event_type=*/ui::EventType::ET_MOUSE_PRESSED, /*save_outer_metrics_before_inner=*/false, - /*inner_metrics=*/1, + /*inner_event_type=*/ui::EventType::ET_MOUSE_RELEASED, /*save_inner_metrics=*/true, /*save_outer_metrics_after_inner=*/true, }, // Config #2. { - /*outer_metrics=*/0, + /*outer_event_type=*/ui::EventType::ET_MOUSE_PRESSED, /*save_outer_metrics_before_inner=*/true, - /*inner_metrics=*/1, + /*inner_event_type=*/ui::EventType::ET_MOUSE_RELEASED, /*save_inner_metrics=*/true, /*save_outer_metrics_after_inner=*/true, }, // Config #3. { - /*outer_metrics=*/0, + /*outer_event_type=*/ui::EventType::ET_MOUSE_PRESSED, /*save_outer_metrics_before_inner=*/false, - /*inner_metrics=*/-1, + /*inner_event_type=*/ui::EventType::ET_UNKNOWN, /*save_inner_metrics=*/false, /*save_outer_metrics_after_inner=*/true, }, // Config #4. { - /*outer_metrics=*/-1, + /*outer_event_type=*/ui::EventType::ET_UNKNOWN, /*save_outer_metrics_before_inner=*/false, - /*inner_metrics=*/0, + /*inner_event_type=*/ui::EventType::ET_MOUSE_PRESSED, /*save_inner_metrics=*/true, /*save_outer_metrics_after_inner=*/false, }, @@ -209,10 +197,8 @@ TEST_F(EventsMetricsManagerTest, NestedEventsMetrics) { { // Start outer scope. std::unique_ptr<EventMetrics> outer_metrics; - if (config.outer_event != -1) { - auto& event = events[config.outer_event]; - outer_metrics = EventMetrics::Create(event.type, base::nullopt, - event.timestamp, base::nullopt); + if (config.outer_event_type != ui::EventType::ET_UNKNOWN) { + outer_metrics = CreateEventMetrics(config.outer_event_type); DCHECK_NE(outer_metrics, nullptr); expected_saved_metrics.push_back(outer_metrics.get()); } @@ -223,10 +209,8 @@ TEST_F(EventsMetricsManagerTest, NestedEventsMetrics) { { // Start inner scope. std::unique_ptr<EventMetrics> inner_metrics; - if (config.inner_event != -1) { - auto& event = events[config.inner_event]; - inner_metrics = EventMetrics::Create(event.type, base::nullopt, - event.timestamp, base::nullopt); + if (config.inner_event_type != ui::EventType::ET_UNKNOWN) { + inner_metrics = CreateEventMetrics(config.inner_event_type); DCHECK_NE(inner_metrics, nullptr); expected_saved_metrics.push_back(inner_metrics.get()); } diff --git a/chromium/cc/metrics/frame_sequence_metrics.cc b/chromium/cc/metrics/frame_sequence_metrics.cc index e79edb0a3c0..1d4e00a04df 100644 --- a/chromium/cc/metrics/frame_sequence_metrics.cc +++ b/chromium/cc/metrics/frame_sequence_metrics.cc @@ -119,7 +119,7 @@ FrameSequenceMetrics::FrameSequenceMetrics(FrameSequenceTrackerType type, FrameSequenceMetrics::~FrameSequenceMetrics() = default; void FrameSequenceMetrics::ReportLeftoverData() { - if (HasDataLeftForReporting()) + if (HasDataLeftForReporting() || type_ == FrameSequenceTrackerType::kCustom) ReportMetrics(); } @@ -228,6 +228,7 @@ void FrameSequenceMetrics::ReportMetrics() { main_throughput_ = {}; impl_throughput_ = {}; + jank_reporter_->Reset(); frames_checkerboarded_ = 0; return; } diff --git a/chromium/cc/metrics/frame_sequence_tracker.cc b/chromium/cc/metrics/frame_sequence_tracker.cc index 4b603a222dc..9576ad3b63d 100644 --- a/chromium/cc/metrics/frame_sequence_tracker.cc +++ b/chromium/cc/metrics/frame_sequence_tracker.cc @@ -78,6 +78,11 @@ FrameSequenceTracker::FrameSequenceTracker( throughput_ukm_reporter)) { DCHECK_LT(type, FrameSequenceTrackerType::kMaxType); DCHECK(type != FrameSequenceTrackerType::kCustom); + // TODO(crbug.com/1158439): remove the trace event once the validation is + // completed. + TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP1( + "cc,benchmark", "TrackerValidation", TRACE_ID_LOCAL(this), + base::TimeTicks::Now(), "name", GetFrameSequenceTrackerTypeName(type)); } FrameSequenceTracker::FrameSequenceTracker( @@ -92,6 +97,9 @@ FrameSequenceTracker::FrameSequenceTracker( } FrameSequenceTracker::~FrameSequenceTracker() { + TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP0( + "cc,benchmark", "TrackerValidation", TRACE_ID_LOCAL(this), + base::TimeTicks::Now()); CleanUp(); } @@ -441,13 +449,12 @@ void FrameSequenceTracker::ReportFramePresented( uint32_t impl_frames_ontime = 0; uint32_t main_frames_ontime = 0; - const auto& vsync_interval = + const auto vsync_interval = (feedback.interval.is_zero() ? viz::BeginFrameArgs::DefaultInterval() - : feedback.interval) * - 1.5; + : feedback.interval); DCHECK(!vsync_interval.is_zero()) << TRACKER_DCHECK_MSG; base::TimeTicks safe_deadline_for_frame = - last_frame_presentation_timestamp_ + vsync_interval; + last_frame_presentation_timestamp_ + vsync_interval * 1.5; const bool was_presented = !feedback.failed(); if (was_presented && submitted_frame_since_last_presentation) { @@ -470,7 +477,7 @@ void FrameSequenceTracker::ReportFramePresented( } metrics()->ComputeJank(FrameSequenceMetrics::ThreadType::kCompositor, - frame_token, feedback.timestamp, feedback.interval); + frame_token, feedback.timestamp, vsync_interval); } if (was_presented) { @@ -492,8 +499,7 @@ void FrameSequenceTracker::ReportFramePresented( } metrics()->ComputeJank(FrameSequenceMetrics::ThreadType::kMain, - frame_token, feedback.timestamp, - feedback.interval); + frame_token, feedback.timestamp, vsync_interval); } if (main_frames_.size() < size_before_erase) { if (!last_frame_presentation_timestamp_.is_null() && diff --git a/chromium/cc/metrics/frame_sequence_tracker.h b/chromium/cc/metrics/frame_sequence_tracker.h index 2c4fb649bb9..387a50b71f3 100644 --- a/chromium/cc/metrics/frame_sequence_tracker.h +++ b/chromium/cc/metrics/frame_sequence_tracker.h @@ -250,7 +250,6 @@ class CC_EXPORT FrameSequenceTracker { // only when the last impl-frame is ended (ReportFrameEnd). bool is_inside_frame_ = false; - #if DCHECK_IS_ON() // This stringstream represents a sequence of frame reporting activities on // the current tracker. Each letter can be one of the following: diff --git a/chromium/cc/metrics/frame_sequence_tracker_collection.cc b/chromium/cc/metrics/frame_sequence_tracker_collection.cc index 7d982152838..22c7941a8ac 100644 --- a/chromium/cc/metrics/frame_sequence_tracker_collection.cc +++ b/chromium/cc/metrics/frame_sequence_tracker_collection.cc @@ -363,7 +363,7 @@ void FrameSequenceTrackerCollection::RecreateTrackers( } ActiveFrameSequenceTrackers -FrameSequenceTrackerCollection::FrameSequenceTrackerActiveTypes() { +FrameSequenceTrackerCollection::FrameSequenceTrackerActiveTypes() const { ActiveFrameSequenceTrackers encoded_types = 0; for (const auto& key : frame_trackers_) { auto thread_type = key.first.first; diff --git a/chromium/cc/metrics/frame_sequence_tracker_collection.h b/chromium/cc/metrics/frame_sequence_tracker_collection.h index d1b5fa53dd2..9761e937f47 100644 --- a/chromium/cc/metrics/frame_sequence_tracker_collection.h +++ b/chromium/cc/metrics/frame_sequence_tracker_collection.h @@ -105,7 +105,7 @@ class CC_EXPORT FrameSequenceTrackerCollection { // Return the type of each active frame tracker, encoded into a 16 bit // integer with the bit at each position corresponding to the enum value of // each type. - ActiveFrameSequenceTrackers FrameSequenceTrackerActiveTypes(); + ActiveFrameSequenceTrackers FrameSequenceTrackerActiveTypes() const; FrameSequenceTracker* GetRemovalTrackerForTesting( FrameSequenceTrackerType type); diff --git a/chromium/cc/metrics/frame_sequence_tracker_unittest.cc b/chromium/cc/metrics/frame_sequence_tracker_unittest.cc index ba8f2b1f7e2..908b0edd950 100644 --- a/chromium/cc/metrics/frame_sequence_tracker_unittest.cc +++ b/chromium/cc/metrics/frame_sequence_tracker_unittest.cc @@ -353,6 +353,82 @@ TEST_F(FrameSequenceTrackerTest, TestNotifyFramePresented) { EXPECT_EQ(NumberOfRemovalTrackers(), 0u); } +TEST_F(FrameSequenceTrackerTest, TestJankWithZeroIntervalInFeedback) { + // Test if jank can be correctly counted if presentation feedback reports + // zero frame interval. + const uint64_t source = 1; + uint64_t sequence = 1; + uint64_t frame_token = sequence; + const char* histogram_name = + "Graphics.Smoothness.Jank.Compositor.TouchScroll"; + const base::TimeDelta zero_interval = base::TimeDelta::FromMilliseconds(0); + base::HistogramTester histogram_tester; + + CreateNewTracker(); + base::TimeTicks args_timestamp = base::TimeTicks::Now(); + auto args = CreateBeginFrameArgs(source, sequence, args_timestamp); + + // Frame 1 + collection_.NotifyBeginImplFrame(args); + collection_.NotifySubmitFrame(sequence, false, viz::BeginFrameAck(args, true), + args); + collection_.NotifyFrameEnd(args, args); + + collection_.NotifyFramePresented( + frame_token, + /*feedback=*/{args_timestamp, zero_interval, 0}); + + // Frame 2 + ++sequence; + ++frame_token; + args_timestamp += base::TimeDelta::FromMillisecondsD(16.67); + args = CreateBeginFrameArgs(source, sequence, args_timestamp); + collection_.NotifyBeginImplFrame(args); + collection_.NotifySubmitFrame(sequence, false, viz::BeginFrameAck(args, true), + args); + collection_.NotifyFrameEnd(args, args); + collection_.NotifyFramePresented( + frame_token, + /*feedback=*/{args_timestamp, zero_interval, 0}); + + // Frame 3: There is one jank (frame interval incremented from 16.67ms + // to 30.0ms) + ++sequence; + ++frame_token; + args_timestamp += base::TimeDelta::FromMillisecondsD(30.0); + args = CreateBeginFrameArgs(source, sequence, args_timestamp); + collection_.NotifyBeginImplFrame(args); + collection_.NotifySubmitFrame(sequence, false, viz::BeginFrameAck(args, true), + args); + collection_.NotifyFrameEnd(args, args); + collection_.NotifyFramePresented( + frame_token, + /*feedback=*/{args_timestamp, zero_interval, 0}); + + // Frame 4: There is no jank since the increment from 30ms to 31ms is too + // small. This tests if |NotifyFramePresented| can correctly handle the + // situation when the frame interval reported in presentation feedback is 0. + ++sequence; + ++frame_token; + args_timestamp += base::TimeDelta::FromMillisecondsD(31.0); + args = CreateBeginFrameArgs(source, sequence, args_timestamp); + collection_.NotifyBeginImplFrame(args); + collection_.NotifySubmitFrame(sequence, false, viz::BeginFrameAck(args, true), + args); + collection_.NotifyFrameEnd(args, args); + collection_.NotifyFramePresented( + frame_token, + /*feedback=*/{args_timestamp, zero_interval, 0}); + ImplThroughput().frames_expected = 100u; + ReportMetrics(); + histogram_tester.ExpectTotalCount( + "Graphics.Smoothness.Jank.Compositor.TouchScroll", 1u); + + // There should be only one jank for frame 3. + EXPECT_THAT(histogram_tester.GetAllSamples(histogram_name), + testing::ElementsAre(base::Bucket(1, 1))); +} + // Base case for checkerboarding: present a single frame with checkerboarding, // followed by a non-checkerboard frame. TEST_F(FrameSequenceTrackerTest, CheckerboardingSimple) { @@ -1641,9 +1717,10 @@ TEST_F(FrameSequenceTrackerTest, CustomTrackers) { collection_.StopCustomSequence(2); EXPECT_EQ(2u, NumberOfCustomTrackers()); - // Tracker 2 has no data to report. + // Tracker 2 has zero expected frames. collection_.NotifyFramePresented(frame_token, {}); - EXPECT_EQ(0u, results.size()); + EXPECT_EQ(1u, results.size()); + EXPECT_EQ(0u, results[2].frames_expected); // Simple sequence of one frame. const char sequence[] = "b(1)B(0,1)s(1)S(1)e(1,0)P(1)"; @@ -1656,9 +1733,11 @@ TEST_F(FrameSequenceTrackerTest, CustomTrackers) { // Tracker 1 and 3 and should report. collection_.NotifyFramePresented(frame_token, {}); - EXPECT_EQ(2u, results.size()); + EXPECT_EQ(3u, results.size()); EXPECT_EQ(1u, results[1].frames_produced); EXPECT_EQ(1u, results[1].frames_expected); + EXPECT_EQ(0u, results[2].frames_produced); + EXPECT_EQ(0u, results[2].frames_expected); EXPECT_EQ(1u, results[3].frames_produced); EXPECT_EQ(1u, results[3].frames_expected); } diff --git a/chromium/cc/metrics/frame_sorter.cc b/chromium/cc/metrics/frame_sorter.cc index 4b7990c75f2..c8c494f65f4 100644 --- a/chromium/cc/metrics/frame_sorter.cc +++ b/chromium/cc/metrics/frame_sorter.cc @@ -124,7 +124,7 @@ void FrameSorter::FlushFrames() { DCHECK(!pending_frames_.empty()); size_t flushed_count = 0; while (!pending_frames_.empty()) { - const auto first = pending_frames_.front(); + const auto& first = pending_frames_.front(); auto& frame_state = frame_states_[first.frame_id]; if (!frame_state.IsComplete()) break; diff --git a/chromium/cc/metrics/jank_injector.cc b/chromium/cc/metrics/jank_injector.cc new file mode 100644 index 00000000000..12c13e41cc5 --- /dev/null +++ b/chromium/cc/metrics/jank_injector.cc @@ -0,0 +1,176 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/metrics/jank_injector.h" + +#include <map> +#include <string> +#include <utility> +#include <vector> + +#include "base/bind.h" +#include "base/debug/alias.h" +#include "base/feature_list.h" +#include "base/no_destructor.h" +#include "base/strings/string_split.h" +#include "base/trace_event/trace_event.h" +#include "cc/base/features.h" +#include "url/gurl.h" + +namespace cc { + +namespace { + +const char kJankInjectionAllowedURLs[] = "allowed_urls"; +const char kJankInjectionClusterSize[] = "cluster"; +const char kJankInjectionTargetPercent[] = "percent"; + +struct JankInjectionParams { + JankInjectionParams() = default; + ~JankInjectionParams() = default; + + JankInjectionParams(JankInjectionParams&&) = default; + JankInjectionParams& operator=(JankInjectionParams&&) = default; + + JankInjectionParams(const JankInjectionParams&) = delete; + JankInjectionParams& operator=(const JankInjectionParams&) = delete; + + // The jank injection code blocks the main thread for |jank_duration| amount + // of time. + base::TimeDelta jank_duration; + + // When |busy_loop| is set, blocks the main thread in a busy loop for + // |jank_duration|. Otherwise, sleeps for |jank_duration|. + bool busy_loop = true; +}; + +bool IsJankInjectionEnabled() { + static bool enabled = + base::FeatureList::IsEnabled(features::kJankInjectionAblationFeature); + return enabled; +} + +using AllowedURLsMap = std::map<std::string, std::vector<std::string>>; +// Returns a map of <host, <list of paths>> pairs. +AllowedURLsMap GetAllowedURLs() { + DCHECK(IsJankInjectionEnabled()); + AllowedURLsMap urls; + std::string url_list = base::GetFieldTrialParamValueByFeature( + features::kJankInjectionAblationFeature, kJankInjectionAllowedURLs); + for (auto& it : base::SplitString(url_list, ",", base::TRIM_WHITESPACE, + base::SPLIT_WANT_ALL)) { + GURL url = GURL(it); + urls[url.host()].emplace_back(url.path()); + } + return urls; +} + +bool IsJankInjectionEnabledForURL(const GURL& url) { + DCHECK(IsJankInjectionEnabled()); + static base::NoDestructor<AllowedURLsMap> allowed_urls(GetAllowedURLs()); + if (allowed_urls->empty()) + return false; + + const auto iter = allowed_urls->find(url.host()); + if (iter == allowed_urls->end()) + return false; + + const auto& paths = iter->second; + const auto& path = url.path_piece(); + return paths.end() != + std::find_if(paths.begin(), paths.end(), [path](const std::string& p) { + return base::StartsWith(path, p); + }); +} + +void RunJank(JankInjectionParams params) { + TRACE_EVENT0("cc,benchmark", "Injected Jank"); + if (params.busy_loop) { + // Do some useless work, and prevent any weird compiler optimization from + // doing anything here. + base::TimeTicks start = base::TimeTicks::Now(); + std::vector<base::TimeTicks> dummy; + while (base::TimeTicks::Now() - start < params.jank_duration) { + dummy.push_back(base::TimeTicks::Now()); + if (dummy.size() > 100) { + dummy.erase(dummy.begin()); + } + } + base::debug::Alias(&dummy); + } else { + base::PlatformThread::Sleep(params.jank_duration); + } +} + +} // namespace + +JankInjector::JankInjector() { + if (IsJankInjectionEnabled()) { + config_.target_dropped_frames_percent = + base::GetFieldTrialParamByFeatureAsInt( + features::kJankInjectionAblationFeature, + kJankInjectionTargetPercent, config_.target_dropped_frames_percent); + config_.dropped_frame_cluster_size = base::GetFieldTrialParamByFeatureAsInt( + features::kJankInjectionAblationFeature, kJankInjectionClusterSize, + config_.dropped_frame_cluster_size); + } +} + +JankInjector::~JankInjector() = default; + +bool JankInjector::IsEnabled(const GURL& url) { + return IsJankInjectionEnabled() && IsJankInjectionEnabledForURL(url); +} + +void JankInjector::ScheduleJankIfNeeded( + const viz::BeginFrameArgs& args, + base::SingleThreadTaskRunner* task_runner) { + if (ShouldJankCurrentFrame(args)) { + ScheduleJank(args, task_runner); + did_jank_last_time_ = true; + } else { + ++total_frames_; + did_jank_last_time_ = false; + } +} + +bool JankInjector::ShouldJankCurrentFrame( + const viz::BeginFrameArgs& args) const { + // If jank was injected during the previous frame, then do not inject jank + // again now. + if (did_jank_last_time_) + return false; + + // Do not jank during the first frame. + if (!total_frames_) + return false; + + auto current_jank = janked_frames_ * 100 / total_frames_; + // Do not drop any more frames if the injected jank is already above or at the + // target. + if (current_jank >= config_.target_dropped_frames_percent) + return false; + + // If janking now makes the dropped the frames goes beyond the target, then do + // not inject the jank yet. + auto next_jank = (janked_frames_ + config_.dropped_frame_cluster_size) * 100 / + (total_frames_ + config_.dropped_frame_cluster_size); + if (next_jank > config_.target_dropped_frames_percent) + return false; + + return true; +} + +void JankInjector::ScheduleJank(const viz::BeginFrameArgs& args, + base::SingleThreadTaskRunner* task_runner) { + JankInjectionParams params; + params.jank_duration = config_.dropped_frame_cluster_size * args.interval; + params.busy_loop = true; + task_runner->PostTask(FROM_HERE, base::BindOnce(&RunJank, std::move(params))); + + janked_frames_ += config_.dropped_frame_cluster_size; + total_frames_ += config_.dropped_frame_cluster_size; +} + +} // namespace cc diff --git a/chromium/cc/metrics/jank_injector.h b/chromium/cc/metrics/jank_injector.h new file mode 100644 index 00000000000..b27bf2143c9 --- /dev/null +++ b/chromium/cc/metrics/jank_injector.h @@ -0,0 +1,56 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CC_METRICS_JANK_INJECTOR_H_ +#define CC_METRICS_JANK_INJECTOR_H_ + +#include <memory> + +#include "base/single_thread_task_runner.h" +#include "cc/cc_export.h" +#include "components/viz/common/frame_sinks/begin_frame_args.h" + +class GURL; + +namespace cc { + +class CC_EXPORT JankInjector { + public: + struct CC_EXPORT JankConfig { + uint32_t target_dropped_frames_percent = 10; + + // How many consecutive frames to drop (when a frame is dropped). + uint32_t dropped_frame_cluster_size = 1; + }; + + // Jank injection. + JankInjector(); + ~JankInjector(); + + JankInjector(const JankInjector&) = delete; + JankInjector& operator=(const JankInjector&) = delete; + + static bool IsEnabled(const GURL& url); + + void ScheduleJankIfNeeded(const viz::BeginFrameArgs& args, + base::SingleThreadTaskRunner* task_runner); + + const JankConfig& config() const { return config_; } + + private: + bool ShouldJankCurrentFrame(const viz::BeginFrameArgs& args) const; + void ScheduleJank(const viz::BeginFrameArgs& args, + base::SingleThreadTaskRunner* task_runner); + void SignalJank(); + + JankConfig config_; + + uint64_t total_frames_ = 0; + uint64_t janked_frames_ = 0; + bool did_jank_last_time_ = false; +}; + +} // namespace cc + +#endif // CC_METRICS_JANK_INJECTOR_H_ diff --git a/chromium/cc/metrics/jank_injector_unittest.cc b/chromium/cc/metrics/jank_injector_unittest.cc new file mode 100644 index 00000000000..cbc50ccf272 --- /dev/null +++ b/chromium/cc/metrics/jank_injector_unittest.cc @@ -0,0 +1,61 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/metrics/jank_injector.h" + +#include <string> + +#include "base/test/scoped_feature_list.h" +#include "base/test/test_simple_task_runner.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace cc { +namespace { + +class JankInjectorTest : public testing::Test { + public: + JankInjectorTest() = default; + ~JankInjectorTest() override = default; +}; + +TEST_F(JankInjectorTest, Basic) { + base::test::ScopedFeatureList features; + features.InitFromCommandLine("JankInjectionAblation:cluster/4/percent/10", + std::string()); + + scoped_refptr<base::TestSimpleTaskRunner> task_runner( + new base::TestSimpleTaskRunner()); + + JankInjector injector; + const auto& config = injector.config(); + EXPECT_EQ(config.target_dropped_frames_percent, 10u); + EXPECT_EQ(config.dropped_frame_cluster_size, 4u); + + const uint32_t kSourceId = 1; + uint32_t sequence_number = 1; + constexpr base::TimeDelta kInterval = base::TimeDelta::FromMilliseconds(16); + base::TimeTicks frame_time = base::TimeTicks::Now(); + base::TimeTicks deadline = frame_time + kInterval; + + auto args = viz::BeginFrameArgs::Create( + BEGINFRAME_FROM_HERE, kSourceId, ++sequence_number, frame_time, deadline, + kInterval, viz::BeginFrameArgs::NORMAL); + // For the first frame, no janks scheduled. + injector.ScheduleJankIfNeeded(args, task_runner.get()); + EXPECT_FALSE(task_runner->HasPendingTask()); + + // Generate over 100 frames. This should cause jank to be injected 3 times. + for (uint32_t count = 0; count < 100; ++count) { + args.frame_time += kInterval; + args.deadline += kInterval; + ++args.frame_id.sequence_number; + injector.ScheduleJankIfNeeded(args, task_runner.get()); + } + + // Jank should be injected 3 times for the 100 frames. + EXPECT_EQ(task_runner->NumPendingTasks(), 3u); +} + +} // namespace +} // namespace cc diff --git a/chromium/cc/metrics/jank_metrics.cc b/chromium/cc/metrics/jank_metrics.cc index b6237766c7e..8922e14f484 100644 --- a/chromium/cc/metrics/jank_metrics.cc +++ b/chromium/cc/metrics/jank_metrics.cc @@ -246,6 +246,14 @@ void JankMetrics::ReportJankMetrics(int frames_expected) { GetMaxStaleHistogramName(tracker_type_), kStaleHistogramMin, kStaleHistogramMax, kStaleHistogramBucketCount, base::HistogramBase::kUmaTargetedHistogramFlag)); + + // Reset counts to avoid duplicated reporting. + Reset(); +} + +void JankMetrics::Reset() { + jank_count_ = 0; + max_staleness_ = {}; } void JankMetrics::Merge(std::unique_ptr<JankMetrics> jank_metrics) { diff --git a/chromium/cc/metrics/jank_metrics.h b/chromium/cc/metrics/jank_metrics.h index 6fea8f00d5c..91ca7fa3d5c 100644 --- a/chromium/cc/metrics/jank_metrics.h +++ b/chromium/cc/metrics/jank_metrics.h @@ -31,6 +31,9 @@ class CC_EXPORT JankMetrics { JankMetrics(const JankMetrics&) = delete; JankMetrics& operator=(const JankMetrics&) = delete; + void AddFrameWithNoUpdate(uint32_t sequence_number, + base::TimeDelta frame_interval); + // Check if a jank occurs based on the timestamps of recent presentations. // If there is a jank, increment |jank_count_| and log a trace event. // Graphics.Smoothness.Stale.* metrics are reported in this function. @@ -38,22 +41,24 @@ class CC_EXPORT JankMetrics { base::TimeTicks current_presentation_timestamp, base::TimeDelta frame_interval); - // Report Graphics.Smoothness.(Jank|MaxStale).* metrics. - void ReportJankMetrics(int frames_expected); + void AddSubmitFrame(uint32_t frame_token, uint32_t sequence_number); // Merge the current jank count with a previously unreported jank metrics. void Merge(std::unique_ptr<JankMetrics> jank_metrics); - void AddSubmitFrame(uint32_t frame_token, uint32_t sequence_number); + // Report Graphics.Smoothness.(Jank|MaxStale).* metrics. + void ReportJankMetrics(int frames_expected); - void AddFrameWithNoUpdate(uint32_t sequence_number, - base::TimeDelta frame_interval); + // Reset the internal jank count + void Reset(); + + int jank_count() const { return jank_count_; } + + base::TimeDelta max_staleness() const { return max_staleness_; } FrameSequenceMetrics::ThreadType thread_type() const { return effective_thread_; } - int jank_count() const { return jank_count_; } - private: // The type of the tracker this JankMetrics object is attached to. const FrameSequenceTrackerType tracker_type_; diff --git a/chromium/cc/metrics/jank_metrics_unittest.cc b/chromium/cc/metrics/jank_metrics_unittest.cc index b4c43bed2f1..dbc624e1ac5 100644 --- a/chromium/cc/metrics/jank_metrics_unittest.cc +++ b/chromium/cc/metrics/jank_metrics_unittest.cc @@ -415,8 +415,17 @@ TEST_F(JankMetricsTest, RAFMergeJanks) { SimulateFrameSequence(other_reporter.get(), seqs); jank_reporter.Merge(std::move(other_reporter)); + EXPECT_EQ(jank_reporter.jank_count(), 6); + EXPECT_TRUE( + jank_reporter.max_staleness() > base::TimeDelta::FromMilliseconds(33) && + jank_reporter.max_staleness() < base::TimeDelta::FromMilliseconds(34)); jank_reporter.ReportJankMetrics(100u); + // Jank / staleness values should be reset after reporting + EXPECT_EQ(jank_reporter.jank_count(), 0); + EXPECT_EQ(jank_reporter.max_staleness(), + base::TimeDelta::FromMilliseconds(0)); + // Expect 6 janks for "Main" (3 from each reporter) const char* metric = "Graphics.Smoothness.Jank.Main.RAF"; const char* invalid_metric = "Graphics.Smoothness.Jank.Compositor.RAF"; diff --git a/chromium/cc/metrics/latency_ukm_reporter.cc b/chromium/cc/metrics/latency_ukm_reporter.cc index 02c884e0e03..da8144ec98e 100644 --- a/chromium/cc/metrics/latency_ukm_reporter.cc +++ b/chromium/cc/metrics/latency_ukm_reporter.cc @@ -89,22 +89,30 @@ void LatencyUkmReporter::ReportCompositorLatencyUkm( CompositorFrameReporter::FrameReportType report_type, const std::vector<CompositorFrameReporter::StageData>& stage_history, const CompositorFrameReporter::ActiveTrackers& active_trackers, - const viz::FrameTimingDetails& viz_breakdown) { + const CompositorFrameReporter::ProcessedBlinkBreakdown& + processed_blink_breakdown, + const CompositorFrameReporter::ProcessedVizBreakdown& + processed_viz_breakdown) { if (ukm_manager_ && compositor_latency_sampling_controller_->ShouldRecordNextEvent()) { - ukm_manager_->RecordCompositorLatencyUKM(report_type, stage_history, - active_trackers, viz_breakdown); + ukm_manager_->RecordCompositorLatencyUKM( + report_type, stage_history, active_trackers, processed_blink_breakdown, + processed_viz_breakdown); } } void LatencyUkmReporter::ReportEventLatencyUkm( const EventMetrics::List& events_metrics, const std::vector<CompositorFrameReporter::StageData>& stage_history, - const viz::FrameTimingDetails& viz_breakdown) { + const CompositorFrameReporter::ProcessedBlinkBreakdown& + processed_blink_breakdown, + const CompositorFrameReporter::ProcessedVizBreakdown& + processed_viz_breakdown) { if (ukm_manager_ && event_latency_sampling_controller_->ShouldRecordNextEvent()) { ukm_manager_->RecordEventLatencyUKM(events_metrics, stage_history, - viz_breakdown); + processed_blink_breakdown, + processed_viz_breakdown); } } diff --git a/chromium/cc/metrics/latency_ukm_reporter.h b/chromium/cc/metrics/latency_ukm_reporter.h index 3d2f96f6f9e..827890a845e 100644 --- a/chromium/cc/metrics/latency_ukm_reporter.h +++ b/chromium/cc/metrics/latency_ukm_reporter.h @@ -11,7 +11,6 @@ #include "cc/cc_export.h" #include "cc/metrics/compositor_frame_reporter.h" #include "cc/metrics/event_metrics.h" -#include "components/viz/common/frame_timing_details.h" namespace cc { class UkmManager; @@ -30,12 +29,18 @@ class CC_EXPORT LatencyUkmReporter { CompositorFrameReporter::FrameReportType report_type, const std::vector<CompositorFrameReporter::StageData>& stage_history, const CompositorFrameReporter::ActiveTrackers& active_trackers, - const viz::FrameTimingDetails& viz_breakdown); + const CompositorFrameReporter::ProcessedBlinkBreakdown& + processed_blink_breakdown, + const CompositorFrameReporter::ProcessedVizBreakdown& + processed_viz_breakdown); void ReportEventLatencyUkm( const EventMetrics::List& events_metrics, const std::vector<CompositorFrameReporter::StageData>& stage_history, - const viz::FrameTimingDetails& viz_breakdown); + const CompositorFrameReporter::ProcessedBlinkBreakdown& + processed_blink_breakdown, + const CompositorFrameReporter::ProcessedVizBreakdown& + processed_viz_breakdown); void set_ukm_manager(UkmManager* manager) { ukm_manager_ = manager; } diff --git a/chromium/cc/metrics/shared_metrics_buffer.h b/chromium/cc/metrics/shared_metrics_buffer.h new file mode 100644 index 00000000000..5167a7715a6 --- /dev/null +++ b/chromium/cc/metrics/shared_metrics_buffer.h @@ -0,0 +1,46 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CC_METRICS_SHARED_METRICS_BUFFER_H_ +#define CC_METRICS_SHARED_METRICS_BUFFER_H_ + +#include "device/base/synchronization/one_writer_seqlock.h" + +namespace cc { +// The struct written in shared memory to transport metrics across +// processes. |data| is protected by the sequence-lock |seq_lock|. +// Note: This template copies data between processes. Any class that uses this +// template would need security review. +template <class T> +struct SharedMetricsBuffer { + device::OneWriterSeqLock seq_lock; + T data; + static_assert(std::is_trivially_copyable<T>::value, + "Metrics shared across processes need to be trivially " + "copyable, otherwise it is dangerous to copy it."); + + bool Read(T& out) const { + const uint32_t kMaxRetries = 5; + uint32_t retries = 0; + base::subtle::Atomic32 version; + do { + const uint32_t kMaxReadAttempts = 32; + version = seq_lock.ReadBegin(kMaxReadAttempts); + device::OneWriterSeqLock::AtomicReaderMemcpy(&out, &data, sizeof(T)); + } while (seq_lock.ReadRetry(version) && ++retries < kMaxRetries); + + // Consider the number of retries less than kMaxRetries as success. + return retries < kMaxRetries; + } + + void Write(const T& in) { + seq_lock.WriteBegin(); + device::OneWriterSeqLock::AtomicWriterMemcpy(&data, &in, sizeof(T)); + seq_lock.WriteEnd(); + } +}; + +} // namespace cc + +#endif // CC_METRICS_SHARED_METRICS_BUFFER_H_ diff --git a/chromium/cc/metrics/ukm_smoothness_data.h b/chromium/cc/metrics/ukm_smoothness_data.h index 2bb5118cd1b..eb74020c249 100644 --- a/chromium/cc/metrics/ukm_smoothness_data.h +++ b/chromium/cc/metrics/ukm_smoothness_data.h @@ -5,7 +5,7 @@ #ifndef CC_METRICS_UKM_SMOOTHNESS_DATA_H_ #define CC_METRICS_UKM_SMOOTHNESS_DATA_H_ -#include "device/base/synchronization/one_writer_seqlock.h" +#include "cc/metrics/shared_metrics_buffer.h" namespace cc { @@ -17,14 +17,10 @@ struct UkmSmoothnessData { double worst_smoothness = 0.0; double above_threshold = 0.0; double percentile_95 = 0.0; + base::TimeDelta time_max_delta = base::TimeDelta::FromMilliseconds(1); }; -// The struct written in shared memory to transport UkmSmoothnessData across -// processes. |data| is protected by the sequence-lock |seq_lock|. -struct UkmSmoothnessDataShared { - device::OneWriterSeqLock seq_lock; - struct UkmSmoothnessData data; -}; +using UkmSmoothnessDataShared = SharedMetricsBuffer<UkmSmoothnessData>; } // namespace cc diff --git a/chromium/cc/metrics/video_playback_roughness_reporter.cc b/chromium/cc/metrics/video_playback_roughness_reporter.cc index 69474ed3080..33ef9c517be 100644 --- a/chromium/cc/metrics/video_playback_roughness_reporter.cc +++ b/chromium/cc/metrics/video_playback_roughness_reporter.cc @@ -59,11 +59,11 @@ void VideoPlaybackRoughnessReporter::FrameSubmitted( FrameInfo info; info.token = token; - info.decode_time = frame.metadata()->decode_end_time; + info.decode_time = frame.metadata().decode_end_time; info.refresh_rate_hz = int{std::round(1.0 / render_interval.InSecondsF())}; info.size = frame.natural_size(); - info.intended_duration = frame.metadata()->wallclock_frame_duration; + info.intended_duration = frame.metadata().wallclock_frame_duration; if (info.intended_duration) { if (render_interval > info.intended_duration.value()) { // In videos with FPS higher than display refresh rate we acknowledge diff --git a/chromium/cc/metrics/video_playback_roughness_reporter_unittest.cc b/chromium/cc/metrics/video_playback_roughness_reporter_unittest.cc index f028506c2be..ffe36027105 100644 --- a/chromium/cc/metrics/video_playback_roughness_reporter_unittest.cc +++ b/chromium/cc/metrics/video_playback_roughness_reporter_unittest.cc @@ -39,7 +39,7 @@ class VideoPlaybackRoughnessReporterTest : public ::testing::Test { int frame_size = 100) { scoped_refptr<VideoFrame> result = media::VideoFrame::CreateColorFrame( gfx::Size(frame_size, frame_size), 0x80, 0x80, 0x80, base::TimeDelta()); - result->metadata()->wallclock_frame_duration = duration; + result->metadata().wallclock_frame_duration = duration; return result; } diff --git a/chromium/cc/metrics/web_vital_metrics.cc b/chromium/cc/metrics/web_vital_metrics.cc new file mode 100644 index 00000000000..1bb822de2dc --- /dev/null +++ b/chromium/cc/metrics/web_vital_metrics.cc @@ -0,0 +1,13 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/metrics/web_vital_metrics.h" + +namespace cc { + +constexpr WebVitalMetrics::MetricsInfo WebVitalMetrics::lcp_info; +constexpr WebVitalMetrics::MetricsInfo WebVitalMetrics::fid_info; +constexpr WebVitalMetrics::MetricsInfo WebVitalMetrics::cls_info; + +} // namespace cc diff --git a/chromium/cc/metrics/web_vital_metrics.h b/chromium/cc/metrics/web_vital_metrics.h new file mode 100644 index 00000000000..35b46b41268 --- /dev/null +++ b/chromium/cc/metrics/web_vital_metrics.h @@ -0,0 +1,56 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CC_METRICS_WEB_VITAL_METRICS_H_ +#define CC_METRICS_WEB_VITAL_METRICS_H_ + +#include <string> + +#include "base/time/time.h" +#include "cc/cc_export.h" + +namespace cc { + +// Web Vital metrics reported from blink to be displayed with cc's HUD display. +struct CC_EXPORT WebVitalMetrics { + bool has_lcp = false; + base::TimeDelta largest_contentful_paint; + bool has_fid = false; + base::TimeDelta first_input_delay; + bool has_cls = false; + double layout_shift = 0.f; + + WebVitalMetrics() = default; + WebVitalMetrics(const WebVitalMetrics& other) = default; + + bool HasValue() const { return has_lcp || has_fid || has_cls; } + + struct MetricsInfo { + double green_threshold; + double yellow_threshold; + enum Unit { kSecond, kMillisecond, kScore }; + Unit unit; + std::string UnitToString() const { + switch (unit) { + case Unit::kSecond: + return " s"; + case Unit::kMillisecond: + return " ms"; + case Unit::kScore: + default: + return ""; + } + } + }; + static constexpr MetricsInfo lcp_info = { + 2.5f, 4.f, WebVitalMetrics::MetricsInfo::Unit::kSecond}; + static constexpr MetricsInfo fid_info = { + 100, 300, WebVitalMetrics::MetricsInfo::Unit::kMillisecond}; + static constexpr MetricsInfo cls_info = { + 0.1f, 0.25f, WebVitalMetrics::MetricsInfo::Unit::kScore}; +}; + +} // namespace cc + +#endif // CC_METRICS_WEB_VITAL_METRICS_H_ diff --git a/chromium/cc/mojo_embedder/BUILD.gn b/chromium/cc/mojo_embedder/BUILD.gn index 63e0725c502..99b982c602f 100644 --- a/chromium/cc/mojo_embedder/BUILD.gn +++ b/chromium/cc/mojo_embedder/BUILD.gn @@ -16,6 +16,7 @@ cc_component("mojo_embedder") { deps = [ "//base", "//cc", + "//components/power_scheduler", "//components/viz/client", "//components/viz/common", "//mojo/public/cpp/bindings", diff --git a/chromium/cc/mojo_embedder/DEPS b/chromium/cc/mojo_embedder/DEPS index 325a68db50a..3006227caf0 100644 --- a/chromium/cc/mojo_embedder/DEPS +++ b/chromium/cc/mojo_embedder/DEPS @@ -1,4 +1,5 @@ include_rules = [ + "+components/power_scheduler", "+mojo/public/cpp/bindings", "+services/viz/public/mojom/compositing", ] diff --git a/chromium/cc/mojo_embedder/async_layer_tree_frame_sink.cc b/chromium/cc/mojo_embedder/async_layer_tree_frame_sink.cc index 452bb7371a9..467fac7f00f 100644 --- a/chromium/cc/mojo_embedder/async_layer_tree_frame_sink.cc +++ b/chromium/cc/mojo_embedder/async_layer_tree_frame_sink.cc @@ -13,6 +13,9 @@ #include "base/trace_event/trace_event.h" #include "cc/base/histograms.h" #include "cc/trees/layer_tree_frame_sink_client.h" +#include "components/power_scheduler/power_mode.h" +#include "components/power_scheduler/power_mode_arbiter.h" +#include "components/power_scheduler/power_mode_voter.h" #include "components/viz/common/features.h" #include "components/viz/common/frame_sinks/begin_frame_args.h" #include "components/viz/common/hit_test/hit_test_region_list.h" @@ -47,8 +50,10 @@ AsyncLayerTreeFrameSink::AsyncLayerTreeFrameSink( synthetic_begin_frame_source_( std::move(params->synthetic_begin_frame_source)), pipes_(std::move(params->pipes)), - wants_animate_only_begin_frames_( - params->wants_animate_only_begin_frames) { + wants_animate_only_begin_frames_(params->wants_animate_only_begin_frames), + animation_power_mode_voter_( + power_scheduler::PowerModeArbiter::GetInstance()->NewVoter( + "PowerModeVoter.Animation")) { DETACH_FROM_THREAD(thread_checker_); } @@ -123,11 +128,12 @@ void AsyncLayerTreeFrameSink::SubmitCompositorFrame( DCHECK(frame.metadata.begin_frame_ack.has_damage); DCHECK(frame.metadata.begin_frame_ack.frame_id.IsSequenceValid()); - TRACE_EVENT_WITH_FLOW1( + TRACE_EVENT_WITH_FLOW2( "viz,benchmark", "Graphics.Pipeline", TRACE_ID_GLOBAL(frame.metadata.begin_frame_ack.trace_id), TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "step", - "SubmitCompositorFrame"); + "SubmitCompositorFrame", "local_surface_id", + local_surface_id_.ToString()); if (local_surface_id_ == last_submitted_local_surface_id_) { DCHECK_EQ(last_submitted_device_scale_factor_, frame.device_scale_factor()); @@ -274,10 +280,15 @@ void AsyncLayerTreeFrameSink::ReclaimResources( void AsyncLayerTreeFrameSink::OnNeedsBeginFrames(bool needs_begin_frames) { DCHECK(compositor_frame_sink_ptr_); if (needs_begin_frames_ != needs_begin_frames) { - if (needs_begin_frames_) { - TRACE_EVENT_ASYNC_END0("cc,benchmark", "NeedsBeginFrames", this); + if (needs_begin_frames) { + TRACE_EVENT_NESTABLE_ASYNC_BEGIN0("cc,benchmark", "NeedsBeginFrames", + this); + animation_power_mode_voter_->VoteFor( + power_scheduler::PowerMode::kAnimation); } else { - TRACE_EVENT_ASYNC_BEGIN0("cc,benchmark", "NeedsBeginFrames", this); + TRACE_EVENT_NESTABLE_ASYNC_END0("cc,benchmark", "NeedsBeginFrames", this); + animation_power_mode_voter_->ResetVoteAfterTimeout( + power_scheduler::PowerModeVoter::kAnimationTimeout); } } needs_begin_frames_ = needs_begin_frames; diff --git a/chromium/cc/mojo_embedder/async_layer_tree_frame_sink.h b/chromium/cc/mojo_embedder/async_layer_tree_frame_sink.h index 7bfcf4680e6..c7efb10500a 100644 --- a/chromium/cc/mojo_embedder/async_layer_tree_frame_sink.h +++ b/chromium/cc/mojo_embedder/async_layer_tree_frame_sink.h @@ -14,6 +14,7 @@ #include "base/single_thread_task_runner.h" #include "cc/mojo_embedder/mojo_embedder_export.h" #include "cc/trees/layer_tree_frame_sink.h" +#include "components/power_scheduler/power_mode_voter.h" #include "components/viz/common/frame_sinks/begin_frame_source.h" #include "components/viz/common/frame_timing_details_map.h" #include "components/viz/common/gpu/context_provider.h" @@ -140,6 +141,8 @@ class CC_MOJO_EMBEDDER_EXPORT AsyncLayerTreeFrameSink float last_submitted_device_scale_factor_ = 1.f; gfx::Size last_submitted_size_in_pixels_; + std::unique_ptr<power_scheduler::PowerModeVoter> animation_power_mode_voter_; + base::WeakPtrFactory<AsyncLayerTreeFrameSink> weak_factory_{this}; }; diff --git a/chromium/cc/mojom/BUILD.gn b/chromium/cc/mojom/BUILD.gn index 1cf5167d715..fc4ab95cb54 100644 --- a/chromium/cc/mojom/BUILD.gn +++ b/chromium/cc/mojom/BUILD.gn @@ -23,6 +23,7 @@ mojom("mojom") { generate_java = true sources = [ "browser_controls_params.mojom", + "browser_controls_state.mojom", "overscroll_behavior.mojom", "render_frame_metadata.mojom", "touch_action.mojom", @@ -42,6 +43,16 @@ mojom("mojom") { { types = [ { + mojom = "cc.mojom.BrowserControlsState" + cpp = "::cc::BrowserControlsState" + }, + ] + traits_headers = [ "//cc/ipc/cc_param_traits_macros.h" ] + traits_public_deps = [ "//cc/ipc" ] + }, + { + types = [ + { mojom = "cc.mojom.TouchAction" cpp = "::cc::TouchAction" }, diff --git a/chromium/cc/mojom/browser_controls_state.mojom b/chromium/cc/mojom/browser_controls_state.mojom new file mode 100644 index 00000000000..d4f396cd67f --- /dev/null +++ b/chromium/cc/mojom/browser_controls_state.mojom @@ -0,0 +1,8 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +module cc.mojom; + +[Native] +enum BrowserControlsState; diff --git a/chromium/cc/mojom/render_frame_metadata.mojom b/chromium/cc/mojom/render_frame_metadata.mojom index 2db01c54bb6..3a087f778cd 100644 --- a/chromium/cc/mojom/render_frame_metadata.mojom +++ b/chromium/cc/mojom/render_frame_metadata.mojom @@ -10,6 +10,18 @@ import "services/viz/public/mojom/compositing/selection.mojom"; import "services/viz/public/mojom/compositing/vertical_scroll_direction.mojom"; import "ui/gfx/geometry/mojom/geometry.mojom"; +// Contains information to assist in making a decision about forwarding +// pointerevents to viz for use in a delegated ink trail. +struct DelegatedInkBrowserMetadata { + // Flag used to indicate the state of the hovering on the pointerevent that + // the delegated ink metadata was created from. If this state does not match + // the point under consideration to send to viz, it won't be sent. As soon + // as it matches again the point will be sent, regardless of if the renderer + // has processed the point that didn't match yet or not. It is true when + // hovering, false otherwise. + bool delegated_ink_is_hovering; +}; + // See components/viz/service/quads/render_frame_metadata.h struct RenderFrameMetadata { // The background color of a CompositorFrame. It can be used for filling the @@ -36,10 +48,12 @@ struct RenderFrameMetadata { // are the same). bool is_mobile_optimized; - // Flag used to notify the browser process to start or stop forwarding points - // to viz for use in a delegated ink trail. True the entire time points should - // be forwarded, and forwarding stops as soon as it is false again. - bool has_delegated_ink_metadata; + // Existence of this flag informs the browser process to start forwarding + // points to viz for use in a delegated ink trail. It contains more + // information to be used in making the forwarding decision. It exists the + // entire time points could be forwarded, and forwarding must stop as soon as + // it is null. + DelegatedInkBrowserMetadata? delegated_ink_metadata; // The device scale factor used to generate CompositorFrame. float device_scale_factor; diff --git a/chromium/cc/mojom/render_frame_metadata_mojom_traits.cc b/chromium/cc/mojom/render_frame_metadata_mojom_traits.cc index dd89563e802..b9eecfbf56d 100644 --- a/chromium/cc/mojom/render_frame_metadata_mojom_traits.cc +++ b/chromium/cc/mojom/render_frame_metadata_mojom_traits.cc @@ -14,6 +14,15 @@ namespace mojo { // static +bool StructTraits<cc::mojom::DelegatedInkBrowserMetadataDataView, + cc::DelegatedInkBrowserMetadata>:: + Read(cc::mojom::DelegatedInkBrowserMetadataDataView data, + cc::DelegatedInkBrowserMetadata* out) { + out->delegated_ink_is_hovering = data.delegated_ink_is_hovering(); + return true; +} + +// static bool StructTraits< cc::mojom::RenderFrameMetadataDataView, cc::RenderFrameMetadata>::Read(cc::mojom::RenderFrameMetadataDataView data, @@ -21,7 +30,6 @@ bool StructTraits< out->root_background_color = data.root_background_color(); out->is_scroll_offset_at_top = data.is_scroll_offset_at_top(); out->is_mobile_optimized = data.is_mobile_optimized(); - out->has_delegated_ink_metadata = data.has_delegated_ink_metadata(); out->device_scale_factor = data.device_scale_factor(); out->page_scale_factor = data.page_scale_factor(); out->external_page_scale_factor = data.external_page_scale_factor(); @@ -40,6 +48,7 @@ bool StructTraits< #endif return data.ReadRootScrollOffset(&out->root_scroll_offset) && data.ReadSelection(&out->selection) && + data.ReadDelegatedInkMetadata(&out->delegated_ink_metadata) && #if defined(OS_ANDROID) data.ReadScrollableViewportSize(&out->scrollable_viewport_size) && data.ReadRootLayerSize(&out->root_layer_size) && diff --git a/chromium/cc/mojom/render_frame_metadata_mojom_traits.h b/chromium/cc/mojom/render_frame_metadata_mojom_traits.h index ed13705d80c..66a3e7e5e19 100644 --- a/chromium/cc/mojom/render_frame_metadata_mojom_traits.h +++ b/chromium/cc/mojom/render_frame_metadata_mojom_traits.h @@ -17,6 +17,19 @@ namespace mojo { template <> struct COMPONENT_EXPORT(CC_SHARED_MOJOM_TRAITS) + StructTraits<cc::mojom::DelegatedInkBrowserMetadataDataView, + cc::DelegatedInkBrowserMetadata> { + static bool delegated_ink_is_hovering( + const cc::DelegatedInkBrowserMetadata& metadata) { + return metadata.delegated_ink_is_hovering; + } + + static bool Read(cc::mojom::DelegatedInkBrowserMetadataDataView data, + cc::DelegatedInkBrowserMetadata* out); +}; + +template <> +struct COMPONENT_EXPORT(CC_SHARED_MOJOM_TRAITS) StructTraits<cc::mojom::RenderFrameMetadataDataView, cc::RenderFrameMetadata> { static SkColor root_background_color( @@ -42,9 +55,9 @@ struct COMPONENT_EXPORT(CC_SHARED_MOJOM_TRAITS) return metadata.is_mobile_optimized; } - static bool has_delegated_ink_metadata( + static base::Optional<cc::DelegatedInkBrowserMetadata> delegated_ink_metadata( const cc::RenderFrameMetadata& metadata) { - return metadata.has_delegated_ink_metadata; + return metadata.delegated_ink_metadata; } static float device_scale_factor(const cc::RenderFrameMetadata& metadata) { diff --git a/chromium/cc/paint/decoded_draw_image.cc b/chromium/cc/paint/decoded_draw_image.cc index 9111136214f..8cc94f1de24 100644 --- a/chromium/cc/paint/decoded_draw_image.cc +++ b/chromium/cc/paint/decoded_draw_image.cc @@ -2,29 +2,32 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include <utility> - #include "cc/paint/decoded_draw_image.h" +#include <utility> + namespace cc { DecodedDrawImage::DecodedDrawImage(sk_sp<const SkImage> image, sk_sp<SkColorFilter> dark_mode_color_filter, const SkSize& src_rect_offset, const SkSize& scale_adjustment, - SkFilterQuality filter_quality) + SkFilterQuality filter_quality, + bool is_budgeted) : image_(std::move(image)), dark_mode_color_filter_(std::move(dark_mode_color_filter)), src_rect_offset_(src_rect_offset), scale_adjustment_(scale_adjustment), - filter_quality_(filter_quality) {} + filter_quality_(filter_quality), + is_budgeted_(is_budgeted) {} DecodedDrawImage::DecodedDrawImage(const gpu::Mailbox& mailbox, SkFilterQuality filter_quality) : mailbox_(mailbox), src_rect_offset_(SkSize::MakeEmpty()), scale_adjustment_(SkSize::Make(1.f, 1.f)), - filter_quality_(filter_quality) {} + filter_quality_(filter_quality), + is_budgeted_(true) {} DecodedDrawImage::DecodedDrawImage( base::Optional<uint32_t> transfer_cache_entry_id, @@ -32,20 +35,23 @@ DecodedDrawImage::DecodedDrawImage( const SkSize& src_rect_offset, const SkSize& scale_adjustment, SkFilterQuality filter_quality, - bool needs_mips) + bool needs_mips, + bool is_budgeted) : transfer_cache_entry_id_(transfer_cache_entry_id), dark_mode_color_filter_(std::move(dark_mode_color_filter)), src_rect_offset_(src_rect_offset), scale_adjustment_(scale_adjustment), filter_quality_(filter_quality), - transfer_cache_entry_needs_mips_(needs_mips) {} + transfer_cache_entry_needs_mips_(needs_mips), + is_budgeted_(is_budgeted) {} DecodedDrawImage::DecodedDrawImage() : DecodedDrawImage(nullptr, nullptr, SkSize::MakeEmpty(), SkSize::Make(1.f, 1.f), - kNone_SkFilterQuality) {} + kNone_SkFilterQuality, + true) {} DecodedDrawImage::DecodedDrawImage(const DecodedDrawImage&) = default; DecodedDrawImage::DecodedDrawImage(DecodedDrawImage&&) = default; diff --git a/chromium/cc/paint/decoded_draw_image.h b/chromium/cc/paint/decoded_draw_image.h index bde0f431033..e00bfeebcb4 100644 --- a/chromium/cc/paint/decoded_draw_image.h +++ b/chromium/cc/paint/decoded_draw_image.h @@ -30,14 +30,16 @@ class CC_PAINT_EXPORT DecodedDrawImage { sk_sp<SkColorFilter> dark_mode_color_filter, const SkSize& src_rect_offset, const SkSize& scale_adjustment, - SkFilterQuality filter_quality); + SkFilterQuality filter_quality, + bool is_budgeted); DecodedDrawImage(const gpu::Mailbox& mailbox, SkFilterQuality filter_quality); DecodedDrawImage(base::Optional<uint32_t> transfer_cache_entry_id, sk_sp<SkColorFilter> dark_mode_color_filter, const SkSize& src_rect_offset, const SkSize& scale_adjustment, SkFilterQuality filter_quality, - bool needs_mips); + bool needs_mips, + bool is_budgeted); DecodedDrawImage(const DecodedDrawImage& other); DecodedDrawImage(DecodedDrawImage&& other); DecodedDrawImage& operator=(const DecodedDrawImage&); @@ -63,6 +65,7 @@ class CC_PAINT_EXPORT DecodedDrawImage { bool transfer_cache_entry_needs_mips() const { return transfer_cache_entry_needs_mips_; } + bool is_budgeted() const { return is_budgeted_; } const gpu::Mailbox& mailbox() const { return mailbox_; } explicit operator bool() const { return image_ || transfer_cache_entry_id_ || !mailbox_.IsZero(); @@ -77,6 +80,7 @@ class CC_PAINT_EXPORT DecodedDrawImage { SkSize scale_adjustment_; SkFilterQuality filter_quality_; bool transfer_cache_entry_needs_mips_ = false; + bool is_budgeted_; }; } // namespace cc diff --git a/chromium/cc/paint/discardable_image_map.cc b/chromium/cc/paint/discardable_image_map.cc index c0ed8173d38..c5418313e8a 100644 --- a/chromium/cc/paint/discardable_image_map.cc +++ b/chromium/cc/paint/discardable_image_map.cc @@ -96,7 +96,7 @@ class DiscardableImageGenerator { // Prevent PaintOpBuffers from having side effects back into the canvas. SkAutoCanvasRestore save_restore(canvas, true); - PlaybackParams params(nullptr, canvas->getTotalMatrix()); + PlaybackParams params(nullptr, canvas->getLocalToDevice()); // TODO(khushalsagar): Optimize out save/restore blocks if there are no // images in the draw ops between them. for (auto* op : PaintOpBuffer::Iterator(buffer)) { @@ -139,10 +139,8 @@ class DiscardableImageGenerator { op_rect, ctm, image_op->flags.getFilterQuality()); } else if (op_type == PaintOpType::DrawImageRect) { auto* image_rect_op = static_cast<DrawImageRectOp*>(op); - SkMatrix matrix = ctm; - matrix.postConcat(SkMatrix::MakeRectToRect(image_rect_op->src, - image_rect_op->dst, - SkMatrix::kFill_ScaleToFit)); + SkMatrix matrix = + SkMatrix::RectToRect(image_rect_op->src, image_rect_op->dst) * ctm; AddImage(image_rect_op->image, image_rect_op->flags.useDarkModeForImage(), image_rect_op->src, op_rect, matrix, image_rect_op->flags.getFilterQuality()); @@ -196,8 +194,7 @@ class DiscardableImageGenerator { SkNoDrawCanvas canvas(scaled_tile_rect.width(), scaled_tile_rect.height()); - canvas.setMatrix(SkMatrix::MakeRectToRect( - shader->tile(), scaled_tile_rect, SkMatrix::kFill_ScaleToFit)); + canvas.setMatrix(SkMatrix::RectToRect(shader->tile(), scaled_tile_rect)); base::AutoReset<bool> auto_reset(&only_gather_animated_images_, true); size_t prev_image_set_size = image_set_.size(); GatherDiscardableImages(shader->paint_record().get(), &op_rect, &canvas); diff --git a/chromium/cc/paint/discardable_image_map_unittest.cc b/chromium/cc/paint/discardable_image_map_unittest.cc index 4675f2989bc..1b217d8e9c3 100644 --- a/chromium/cc/paint/discardable_image_map_unittest.cc +++ b/chromium/cc/paint/discardable_image_map_unittest.cc @@ -47,7 +47,7 @@ struct PositionScaleDrawImage { sk_sp<PaintOpBuffer> CreateRecording(const PaintImage& discardable_image, const gfx::Rect& visible_rect) { auto buffer = sk_make_sp<PaintOpBuffer>(); - buffer->push<DrawImageOp>(discardable_image, 0.f, 0.f, nullptr); + buffer->push<DrawImageOp>(discardable_image, 0.f, 0.f); return buffer; } @@ -122,10 +122,8 @@ TEST_F(DiscardableImageMapTest, GetDiscardableImagesInRectTest) { if ((x + y) & 1) { discardable_image[y][x] = CreateDiscardablePaintImage(gfx::Size(500, 500)); - PaintFlags flags; content_layer_client.add_draw_image( - discardable_image[y][x], gfx::Point(x * 512 + 6, y * 512 + 6), - flags); + discardable_image[y][x], gfx::Point(x * 512 + 6, y * 512 + 6)); } } } @@ -194,10 +192,9 @@ TEST_F(DiscardableImageMapTest, GetDiscardableImagesInRectNonZeroLayer) { if ((x + y) & 1) { discardable_image[y][x] = CreateDiscardablePaintImage(gfx::Size(500, 500)); - PaintFlags flags; content_layer_client.add_draw_image( discardable_image[y][x], - gfx::Point(1024 + x * 512 + 6, y * 512 + 6), flags); + gfx::Point(1024 + x * 512 + 6, y * 512 + 6)); } } } @@ -295,10 +292,8 @@ TEST_F(DiscardableImageMapTest, GetDiscardableImagesInRectOnePixelQuery) { if ((x + y) & 1) { discardable_image[y][x] = CreateDiscardablePaintImage(gfx::Size(500, 500)); - PaintFlags flags; content_layer_client.add_draw_image( - discardable_image[y][x], gfx::Point(x * 512 + 6, y * 512 + 6), - flags); + discardable_image[y][x], gfx::Point(x * 512 + 6, y * 512 + 6)); } } } @@ -334,9 +329,7 @@ TEST_F(DiscardableImageMapTest, GetDiscardableImagesInRectMassiveImage) { PaintImage discardable_image = CreateDiscardablePaintImage(gfx::Size(1 << 25, 1 << 25), nullptr, false /* allocate_encoded_memory */); - PaintFlags flags; - content_layer_client.add_draw_image(discardable_image, gfx::Point(0, 0), - flags); + content_layer_client.add_draw_image(discardable_image, gfx::Point(0, 0)); scoped_refptr<DisplayItemList> display_list = content_layer_client.PaintContentsToDisplayList(); @@ -416,13 +409,13 @@ TEST_F(DiscardableImageMapTest, RestoreSavedTransformedLayers) { PaintImage discardable_image3 = CreateDiscardablePaintImage(gfx::Size(25, 25)); display_list->push<TranslateOp>(25, 25); - display_list->push<DrawImageOp>(discardable_image1, 0.f, 0.f, nullptr); + display_list->push<DrawImageOp>(discardable_image1, 0.f, 0.f); display_list->push<SaveLayerOp>(nullptr, &paint); display_list->push<TranslateOp>(100, 100); - display_list->push<DrawImageOp>(discardable_image2, 0.f, 0.f, nullptr); + display_list->push<DrawImageOp>(discardable_image2, 0.f, 0.f); display_list->push<RestoreOp>(); display_list->push<TranslateOp>(0, 100); - display_list->push<DrawImageOp>(discardable_image3, 0.f, 0.f, nullptr); + display_list->push<DrawImageOp>(discardable_image3, 0.f, 0.f); display_list->EndPaintOfUnpaired(visible_rect); display_list->Finalize(); @@ -471,9 +464,7 @@ TEST_F(DiscardableImageMapTest, GetDiscardableImagesInRectMaxImage) { PaintImage discardable_image = CreateDiscardablePaintImage( gfx::Size(dimension, dimension), no_color_space, false /* allocate_encoded_memory */); - PaintFlags flags; - content_layer_client.add_draw_image(discardable_image, gfx::Point(42, 42), - flags); + content_layer_client.add_draw_image(discardable_image, gfx::Point(42, 42)); scoped_refptr<DisplayItemList> display_list = content_layer_client.PaintContentsToDisplayList(); @@ -511,13 +502,10 @@ TEST_F(DiscardableImageMapTest, GetDiscardableImagesInRectMaxImageMaxLayer) { gfx::Size(dimension, dimension), no_color_space, false /* allocate_encoded_memory */); - PaintFlags flags; - content_layer_client.add_draw_image(discardable_image1, gfx::Point(0, 0), - flags); - content_layer_client.add_draw_image(discardable_image2, gfx::Point(10000, 0), - flags); + content_layer_client.add_draw_image(discardable_image1, gfx::Point(0, 0)); + content_layer_client.add_draw_image(discardable_image2, gfx::Point(10000, 0)); content_layer_client.add_draw_image(discardable_image3, - gfx::Point(-10000, 500), flags); + gfx::Point(-10000, 500)); scoped_refptr<DisplayItemList> display_list = content_layer_client.PaintContentsToDisplayList(); @@ -560,13 +548,10 @@ TEST_F(DiscardableImageMapTest, GetDiscardableImagesRectInBounds) { PaintImage long_discardable_image = CreateDiscardablePaintImage(gfx::Size(10000, 100)); - PaintFlags flags; - content_layer_client.add_draw_image(discardable_image1, gfx::Point(-10, -11), - flags); - content_layer_client.add_draw_image(discardable_image2, gfx::Point(950, 951), - flags); + content_layer_client.add_draw_image(discardable_image1, gfx::Point(-10, -11)); + content_layer_client.add_draw_image(discardable_image2, gfx::Point(950, 951)); content_layer_client.add_draw_image(long_discardable_image, - gfx::Point(-100, 500), flags); + gfx::Point(-100, 500)); scoped_refptr<DisplayItemList> display_list = content_layer_client.PaintContentsToDisplayList(); @@ -701,7 +686,7 @@ TEST_F(DiscardableImageMapTest, GathersDiscardableImagesFromNestedOps) { auto internal_record = sk_make_sp<PaintOpBuffer>(); PaintImage discardable_image = CreateDiscardablePaintImage(gfx::Size(100, 100)); - internal_record->push<DrawImageOp>(discardable_image, 0.f, 0.f, nullptr); + internal_record->push<DrawImageOp>(discardable_image, 0.f, 0.f); // This |discardable_image2| is in a DisplayItemList that gets added // to the root buffer. @@ -711,7 +696,7 @@ TEST_F(DiscardableImageMapTest, GathersDiscardableImagesFromNestedOps) { scoped_refptr<DisplayItemList> display_list = new DisplayItemList(DisplayItemList::kToBeReleasedAsPaintOpBuffer); display_list->StartPaint(); - display_list->push<DrawImageOp>(discardable_image2, 100.f, 100.f, nullptr); + display_list->push<DrawImageOp>(discardable_image2, 100.f, 100.f); display_list->EndPaintOfUnpaired(gfx::Rect(100, 100, 100, 100)); display_list->Finalize(); @@ -750,12 +735,10 @@ TEST_F(DiscardableImageMapTest, GathersAnimatedImages) { PaintImage animation_loop_infinite = CreateAnimatedImage(image_size, frames, 1u); - PaintFlags flags; - content_layer_client.add_draw_image(static_image, gfx::Point(0, 0), flags); - content_layer_client.add_draw_image(animated_loop_none, gfx::Point(100, 100), - flags); + content_layer_client.add_draw_image(static_image, gfx::Point(0, 0)); + content_layer_client.add_draw_image(animated_loop_none, gfx::Point(100, 100)); content_layer_client.add_draw_image(animation_loop_infinite, - gfx::Point(200, 200), flags); + gfx::Point(200, 200)); scoped_refptr<DisplayItemList> display_list = content_layer_client.PaintContentsToDisplayList(); @@ -796,10 +779,9 @@ TEST_F(DiscardableImageMapTest, GathersPaintWorklets) { base::MakeRefCounted<TestPaintWorkletInput>(gfx::SizeF(image_size)); PaintImage paint_worklet_image = CreatePaintWorkletPaintImage(input); - PaintFlags flags; - content_layer_client.add_draw_image(static_image, gfx::Point(0, 0), flags); - content_layer_client.add_draw_image(paint_worklet_image, gfx::Point(100, 100), - flags); + content_layer_client.add_draw_image(static_image, gfx::Point(0, 0)); + content_layer_client.add_draw_image(paint_worklet_image, + gfx::Point(100, 100)); scoped_refptr<DisplayItemList> display_list = content_layer_client.PaintContentsToDisplayList(); @@ -823,13 +805,13 @@ TEST_F(DiscardableImageMapTest, CapturesImagesInPaintRecordShaders) { shader_record->push<ScaleOp>(2.0f, 2.0f); PaintImage static_image = CreateDiscardablePaintImage(gfx::Size(100, 100)); - shader_record->push<DrawImageOp>(static_image, 0.f, 0.f, nullptr); + shader_record->push<DrawImageOp>(static_image, 0.f, 0.f); std::vector<FrameMetadata> frames = { FrameMetadata(true, base::TimeDelta::FromMilliseconds(1)), FrameMetadata(true, base::TimeDelta::FromMilliseconds(1))}; PaintImage animated_image = CreateAnimatedImage(gfx::Size(100, 100), frames); - shader_record->push<DrawImageOp>(animated_image, 0.f, 0.f, nullptr); + shader_record->push<DrawImageOp>(animated_image, 0.f, 0.f); gfx::Rect visible_rect(500, 500); scoped_refptr<DisplayItemList> display_list = new DisplayItemList(); @@ -870,13 +852,13 @@ TEST_F(DiscardableImageMapTest, CapturesImagesInPaintFilters) { auto filter_record = sk_make_sp<PaintOpBuffer>(); PaintImage static_image = CreateDiscardablePaintImage(gfx::Size(100, 100)); - filter_record->push<DrawImageOp>(static_image, 0.f, 0.f, nullptr); + filter_record->push<DrawImageOp>(static_image, 0.f, 0.f); std::vector<FrameMetadata> frames = { FrameMetadata(true, base::TimeDelta::FromMilliseconds(1)), FrameMetadata(true, base::TimeDelta::FromMilliseconds(1))}; PaintImage animated_image = CreateAnimatedImage(gfx::Size(100, 100), frames); - filter_record->push<DrawImageOp>(animated_image, 0.f, 0.f, nullptr); + filter_record->push<DrawImageOp>(animated_image, 0.f, 0.f); gfx::Rect visible_rect(500, 500); scoped_refptr<DisplayItemList> display_list = new DisplayItemList(); @@ -943,7 +925,7 @@ TEST_F(DiscardableImageMapTest, EmbeddedShaderWithAnimatedImages) { FrameMetadata(true, base::TimeDelta::FromMilliseconds(1)), FrameMetadata(true, base::TimeDelta::FromMilliseconds(1))}; PaintImage animated_image = CreateAnimatedImage(gfx::Size(100, 100), frames); - shader_record->push<DrawImageOp>(animated_image, 0.f, 0.f, nullptr); + shader_record->push<DrawImageOp>(animated_image, 0.f, 0.f); auto shader_with_image = PaintShader::MakePaintRecord( shader_record, tile, SkTileMode::kClamp, SkTileMode::kClamp, nullptr); @@ -993,12 +975,9 @@ TEST_F(DiscardableImageMapTest, DecodingModeHintsBasic) { FakeContentLayerClient content_layer_client; content_layer_client.set_bounds(visible_rect.size()); - content_layer_client.add_draw_image(unspecified_image, gfx::Point(0, 0), - PaintFlags()); - content_layer_client.add_draw_image(async_image, gfx::Point(10, 10), - PaintFlags()); - content_layer_client.add_draw_image(sync_image, gfx::Point(20, 20), - PaintFlags()); + content_layer_client.add_draw_image(unspecified_image, gfx::Point(0, 0)); + content_layer_client.add_draw_image(async_image, gfx::Point(10, 10)); + content_layer_client.add_draw_image(sync_image, gfx::Point(20, 20)); scoped_refptr<DisplayItemList> display_list = content_layer_client.PaintContentsToDisplayList(); display_list->GenerateDiscardableImagesMetadata(); @@ -1058,18 +1037,12 @@ TEST_F(DiscardableImageMapTest, DecodingModeHintsDuplicates) { FakeContentLayerClient content_layer_client; content_layer_client.set_bounds(visible_rect.size()); - content_layer_client.add_draw_image(unspecified_image1, gfx::Point(0, 0), - PaintFlags()); - content_layer_client.add_draw_image(async_image1, gfx::Point(10, 10), - PaintFlags()); - content_layer_client.add_draw_image(unspecified_image2, gfx::Point(20, 20), - PaintFlags()); - content_layer_client.add_draw_image(sync_image2, gfx::Point(30, 30), - PaintFlags()); - content_layer_client.add_draw_image(async_image3, gfx::Point(40, 40), - PaintFlags()); - content_layer_client.add_draw_image(sync_image3, gfx::Point(50, 50), - PaintFlags()); + content_layer_client.add_draw_image(unspecified_image1, gfx::Point(0, 0)); + content_layer_client.add_draw_image(async_image1, gfx::Point(10, 10)); + content_layer_client.add_draw_image(unspecified_image2, gfx::Point(20, 20)); + content_layer_client.add_draw_image(sync_image2, gfx::Point(30, 30)); + content_layer_client.add_draw_image(async_image3, gfx::Point(40, 40)); + content_layer_client.add_draw_image(sync_image3, gfx::Point(50, 50)); scoped_refptr<DisplayItemList> display_list = content_layer_client.PaintContentsToDisplayList(); display_list->GenerateDiscardableImagesMetadata(); @@ -1100,9 +1073,8 @@ TEST_F(DiscardableImageMapTest, TracksImageRegions) { FrameMetadata(true, base::TimeDelta::FromMilliseconds(1)), }; auto image = CreateAnimatedImage(gfx::Size(100, 100), frames); - PaintFlags flags; - content_layer_client.add_draw_image(image, gfx::Point(0, 0), flags); - content_layer_client.add_draw_image(image, gfx::Point(400, 400), flags); + content_layer_client.add_draw_image(image, gfx::Point(0, 0)); + content_layer_client.add_draw_image(image, gfx::Point(400, 400)); scoped_refptr<DisplayItemList> display_list = content_layer_client.PaintContentsToDisplayList(); @@ -1146,8 +1118,7 @@ TEST_F(DiscardableImageMapTest, HighBitDepth) { const DiscardableImageMap& image_map = display_list->discardable_image_map(); EXPECT_FALSE(image_map.contains_hbd_images()); - content_layer_client.add_draw_image(discardable_image, gfx::Point(0, 0), - PaintFlags()); + content_layer_client.add_draw_image(discardable_image, gfx::Point(0, 0)); display_list = content_layer_client.PaintContentsToDisplayList(); display_list->GenerateDiscardableImagesMetadata(); const DiscardableImageMap& image_map2 = display_list->discardable_image_map(); @@ -1169,8 +1140,7 @@ TEST_F(DiscardableImageMapTest, ContentColorUsage) { // Adding a SRGB image should remain SRGB. PaintImage discardable_image_srgb = CreateDiscardablePaintImage( kSize, gfx::ColorSpace::CreateSRGB().ToSkColorSpace()); - content_layer_client.add_draw_image(discardable_image_srgb, gfx::Point(0, 0), - PaintFlags()); + content_layer_client.add_draw_image(discardable_image_srgb, gfx::Point(0, 0)); display_list = content_layer_client.PaintContentsToDisplayList(); display_list->GenerateDiscardableImagesMetadata(); EXPECT_EQ(display_list->discardable_image_map().content_color_usage(), @@ -1179,8 +1149,7 @@ TEST_F(DiscardableImageMapTest, ContentColorUsage) { // Adding a WCG image should switch to WCG. PaintImage discardable_image_wcg = CreateDiscardablePaintImage( kSize, gfx::ColorSpace::CreateDisplayP3D65().ToSkColorSpace()); - content_layer_client.add_draw_image(discardable_image_wcg, gfx::Point(0, 0), - PaintFlags()); + content_layer_client.add_draw_image(discardable_image_wcg, gfx::Point(0, 0)); display_list = content_layer_client.PaintContentsToDisplayList(); display_list->GenerateDiscardableImagesMetadata(); EXPECT_EQ(display_list->discardable_image_map().content_color_usage(), @@ -1189,8 +1158,7 @@ TEST_F(DiscardableImageMapTest, ContentColorUsage) { // Adding a HDR image should switch to HDR. PaintImage discardable_image_hdr = CreateDiscardablePaintImage( kSize, gfx::ColorSpace::CreateHDR10().ToSkColorSpace()); - content_layer_client.add_draw_image(discardable_image_hdr, gfx::Point(0, 0), - PaintFlags()); + content_layer_client.add_draw_image(discardable_image_hdr, gfx::Point(0, 0)); display_list = content_layer_client.PaintContentsToDisplayList(); display_list->GenerateDiscardableImagesMetadata(); EXPECT_EQ(display_list->discardable_image_map().content_color_usage(), @@ -1218,8 +1186,7 @@ TEST_P(DiscardableImageMapColorSpaceTest, ColorSpace) { EXPECT_EQ(image_map.content_color_usage(), gfx::ContentColorUsage::kSRGB); EXPECT_FALSE(image_map.contains_hbd_images()); - content_layer_client.add_draw_image(discardable_image, gfx::Point(0, 0), - PaintFlags()); + content_layer_client.add_draw_image(discardable_image, gfx::Point(0, 0)); display_list = content_layer_client.PaintContentsToDisplayList(); display_list->GenerateDiscardableImagesMetadata(); const DiscardableImageMap& image_map2 = display_list->discardable_image_map(); diff --git a/chromium/cc/paint/display_item_list.cc b/chromium/cc/paint/display_item_list.cc index aa047aba8f5..02301698f46 100644 --- a/chromium/cc/paint/display_item_list.cc +++ b/chromium/cc/paint/display_item_list.cc @@ -67,14 +67,6 @@ void IterateTextContentByOffsets(const PaintOpBuffer& buffer, ++index; } } - -bool RotationEquivalentToAxisFlip(const SkMatrix& matrix) { - float skew_x = matrix.getSkewX(); - float skew_y = matrix.getSkewY(); - return ((skew_x == 1.f || skew_x == -1.f) && - (skew_y == 1.f || skew_y == -1.f)); -} - } // namespace DisplayItemList::DisplayItemList(UsageHint usage_hint) @@ -253,7 +245,7 @@ void DisplayItemList::AddToValue(base::trace_event::TracedValue* state, if (include_items) { state->BeginArray("items"); - PlaybackParams params(nullptr, SkMatrix::I()); + PlaybackParams params(nullptr, SkM44()); std::map<size_t, gfx::Rect> visual_rects = rtree_.GetAllBoundsForTracing(); for (const PaintOp* op : PaintOpBuffer::Iterator(&paint_op_buffer_)) { state->BeginDictionary(); @@ -386,7 +378,7 @@ DisplayItemList::GetDirectlyCompositedImageResult( // Images that respect orientation will have 5 paint operations: // (1) Save // (2) Translate - // (3) Concat (rotation matrix) + // (3) Concat (with a transformation that preserves axis alignment) // (4) DrawImageRect // (5) Restore // Detect these the paint op buffer and disqualify the layer as a directly @@ -403,21 +395,21 @@ DisplayItemList::GetDirectlyCompositedImageResult( break; } case PaintOpType::Concat: { - // We only expect a single rotation. If we see another one, then this - // image won't be eligible for directly compositing. + // We only expect a single transformation. If we see another one, then + // this image won't be eligible for directly compositing. if (transpose_image_size) return base::nullopt; const ConcatOp* concat_op = static_cast<const ConcatOp*>(op); - if (concat_op->matrix.hasPerspective() || - !concat_op->matrix.preservesAxisAlignment()) + if (!MathUtil::SkM44Preserves2DAxisAlignment(concat_op->matrix)) return base::nullopt; - // If the rotation is not an axis flip, we'll need to transpose the - // width and height dimensions to account for the same transform - // applying when the layer bounds were calculated. - transpose_image_size = - RotationEquivalentToAxisFlip(concat_op->matrix); + // If the image has been rotated +/-90 degrees we'll need to transpose + // the width and height dimensions to account for the same transform + // applying when the layer bounds were calculated. Since we already + // know that the transformation preserves axis alignment, we only + // need to confirm that this is not a scaling operation. + transpose_image_size = (concat_op->matrix.rc(0, 0) == 0); break; } case PaintOpType::DrawImageRect: diff --git a/chromium/cc/paint/display_item_list_unittest.cc b/chromium/cc/paint/display_item_list_unittest.cc index 2a3889b7a64..707ffc441e2 100644 --- a/chromium/cc/paint/display_item_list_unittest.cc +++ b/chromium/cc/paint/display_item_list_unittest.cc @@ -297,7 +297,7 @@ TEST_F(DisplayItemListTest, TransformPairedRange) { { list->StartPaint(); list->push<SaveOp>(); - list->push<ConcatOp>(static_cast<SkMatrix>(transform.matrix())); + list->push<ConcatOp>(transform.GetMatrixAsSkM44()); list->EndPaintOfPairedBegin(); } @@ -511,7 +511,7 @@ TEST_F(DisplayItemListTest, AsValueWithOps) { { list->StartPaint(); list->push<SaveOp>(); - list->push<ConcatOp>(static_cast<SkMatrix>(transform.matrix())); + list->push<ConcatOp>(transform.GetMatrixAsSkM44()); list->EndPaintOfPairedBegin(); } @@ -855,7 +855,7 @@ TEST_F(DisplayItemListTest, AppendVisualRectTwoBlocksTwoDrawings) { { list->StartPaint(); list->push<SaveOp>(); - list->push<ConcatOp>(SkMatrix::I()); + list->push<ConcatOp>(SkM44()); list->EndPaintOfPairedBegin(); } @@ -918,7 +918,7 @@ TEST_F(DisplayItemListTest, { list->StartPaint(); list->push<SaveOp>(); - list->push<ConcatOp>(SkMatrix::I()); + list->push<ConcatOp>(SkM44()); list->EndPaintOfPairedBegin(); } @@ -981,7 +981,7 @@ TEST_F(DisplayItemListTest, { list->StartPaint(); list->push<SaveOp>(); - list->push<ConcatOp>(SkMatrix::I()); + list->push<ConcatOp>(SkM44()); list->EndPaintOfPairedBegin(); } @@ -1044,7 +1044,7 @@ TEST_F(DisplayItemListTest, { list->StartPaint(); list->push<SaveOp>(); - list->push<ConcatOp>(SkMatrix::I()); + list->push<ConcatOp>(SkM44()); list->EndPaintOfPairedBegin(); } diff --git a/chromium/cc/paint/filter_operation.cc b/chromium/cc/paint/filter_operation.cc index c92ce18b3af..dae02259d51 100644 --- a/chromium/cc/paint/filter_operation.cc +++ b/chromium/cc/paint/filter_operation.cc @@ -59,7 +59,7 @@ FilterOperation::FilterOperation(FilterType type, float amount) FilterOperation::FilterOperation(FilterType type, float amount, - SkBlurImageFilter::TileMode tile_mode) + SkTileMode tile_mode) : type_(type), amount_(amount), outer_threshold_(0), diff --git a/chromium/cc/paint/filter_operation.h b/chromium/cc/paint/filter_operation.h index 7e3b17b2064..c989741e2dd 100644 --- a/chromium/cc/paint/filter_operation.h +++ b/chromium/cc/paint/filter_operation.h @@ -14,7 +14,7 @@ #include "cc/paint/paint_filter.h" #include "third_party/skia/include/core/SkColor.h" #include "third_party/skia/include/core/SkScalar.h" -#include "third_party/skia/include/effects/SkBlurImageFilter.h" +#include "third_party/skia/include/core/SkTileMode.h" #include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/rect.h" @@ -98,7 +98,7 @@ class CC_PAINT_EXPORT FilterOperation { return shape_; } - SkBlurImageFilter::TileMode blur_tile_mode() const { + SkTileMode blur_tile_mode() const { DCHECK_EQ(type_, BLUR); return blur_tile_mode_; } @@ -137,8 +137,7 @@ class CC_PAINT_EXPORT FilterOperation { static FilterOperation CreateBlurFilter( float amount, - SkBlurImageFilter::TileMode tile_mode = - SkBlurImageFilter::kClampToBlack_TileMode) { + SkTileMode tile_mode = SkTileMode::kDecal) { return FilterOperation(BLUR, amount, tile_mode); } @@ -227,7 +226,7 @@ class CC_PAINT_EXPORT FilterOperation { shape_ = shape; } - void set_blur_tile_mode(SkBlurImageFilter::TileMode tile_mode) { + void set_blur_tile_mode(SkTileMode tile_mode) { DCHECK_EQ(type_, BLUR); blur_tile_mode_ = tile_mode; } @@ -255,9 +254,7 @@ class CC_PAINT_EXPORT FilterOperation { private: FilterOperation(FilterType type, float amount); - FilterOperation(FilterType type, - float amount, - SkBlurImageFilter::TileMode tile_mode); + FilterOperation(FilterType type, float amount, SkTileMode tile_mode); FilterOperation(FilterType type, const gfx::Point& offset, @@ -286,7 +283,7 @@ class CC_PAINT_EXPORT FilterOperation { // Use a collection of |gfx::Rect| to make serialization simpler. ShapeRects shape_; - SkBlurImageFilter::TileMode blur_tile_mode_; + SkTileMode blur_tile_mode_; }; } // namespace cc diff --git a/chromium/cc/paint/filter_operations_unittest.cc b/chromium/cc/paint/filter_operations_unittest.cc index b8cc7fa6914..a51ea0f82eb 100644 --- a/chromium/cc/paint/filter_operations_unittest.cc +++ b/chromium/cc/paint/filter_operations_unittest.cc @@ -55,7 +55,7 @@ TEST(FilterOperationsTest, MapRectDropShadowReferenceFilter) { FilterOperation::CreateReferenceFilter(sk_make_sp<DropShadowPaintFilter>( SkIntToScalar(3), SkIntToScalar(8), SkIntToScalar(4), SkIntToScalar(9), SK_ColorBLACK, - SkDropShadowImageFilter::kDrawShadowAndForeground_ShadowMode, + DropShadowPaintFilter::ShadowMode::kDrawShadowAndForeground, nullptr))); EXPECT_EQ(gfx::Rect(-9, -19, 34, 64), ops.MapRect(gfx::Rect(0, 0, 10, 10), SkMatrix::I())); @@ -71,7 +71,7 @@ TEST(FilterOperationsTest, MapRectReverseDropShadowReferenceFilter) { FilterOperation::CreateReferenceFilter(sk_make_sp<DropShadowPaintFilter>( SkIntToScalar(3), SkIntToScalar(8), SkIntToScalar(4), SkIntToScalar(9), SK_ColorBLACK, - SkDropShadowImageFilter::kDrawShadowAndForeground_ShadowMode, + DropShadowPaintFilter::ShadowMode::kDrawShadowAndForeground, nullptr))); EXPECT_EQ(gfx::Rect(-15, -35, 34, 64), ops.MapRectReverse(gfx::Rect(0, 0, 10, 10), SkMatrix::I())); @@ -222,8 +222,7 @@ TEST(FilterOperationsTest, MapRectTypeConversionDoesNotOverflow) { FilterOperation::CreateReferenceFilter(sk_make_sp<XfermodePaintFilter>( SkBlendMode::kSrcOver, sk_make_sp<OffsetPaintFilter>(-big_offset, -big_offset, nullptr), - sk_make_sp<OffsetPaintFilter>(big_offset, big_offset, nullptr), - nullptr))); + sk_make_sp<OffsetPaintFilter>(big_offset, big_offset, nullptr)))); gfx::Rect rect = ops.MapRect(gfx::Rect(-10, -10, 20, 20), SkMatrix::I()); EXPECT_GT(rect.width(), 0); EXPECT_GT(rect.height(), 0); @@ -699,10 +698,10 @@ TEST(FilterOperationsTest, BlendSaturatingBrightnessWithNull) { } TEST(FilterOperationsTest, BlendReferenceFilters) { - sk_sp<PaintFilter> from_filter(sk_make_sp<BlurPaintFilter>( - 1.f, 1.f, BlurPaintFilter::TileMode::kClampToBlack_TileMode, nullptr)); - sk_sp<PaintFilter> to_filter(sk_make_sp<BlurPaintFilter>( - 2.f, 2.f, BlurPaintFilter::TileMode::kClampToBlack_TileMode, nullptr)); + sk_sp<PaintFilter> from_filter( + sk_make_sp<BlurPaintFilter>(1.f, 1.f, SkTileMode::kDecal, nullptr)); + sk_sp<PaintFilter> to_filter( + sk_make_sp<BlurPaintFilter>(2.f, 2.f, SkTileMode::kDecal, nullptr)); FilterOperation from = FilterOperation::CreateReferenceFilter(std::move(from_filter)); FilterOperation to = @@ -722,8 +721,8 @@ TEST(FilterOperationsTest, BlendReferenceFilters) { } TEST(FilterOperationsTest, BlendReferenceWithNull) { - sk_sp<PaintFilter> image_filter(sk_make_sp<BlurPaintFilter>( - 1.f, 1.f, BlurPaintFilter::TileMode::kClampToBlack_TileMode, nullptr)); + sk_sp<PaintFilter> image_filter( + sk_make_sp<BlurPaintFilter>(1.f, 1.f, SkTileMode::kDecal, nullptr)); FilterOperation filter = FilterOperation::CreateReferenceFilter(std::move(image_filter)); FilterOperation null_filter = FilterOperation::CreateReferenceFilter(nullptr); @@ -917,8 +916,8 @@ TEST(FilterOperationsTest, HasFilterOfType) { filters.Append(FilterOperation::CreateGrayscaleFilter(0.5f)); filters.Append(FilterOperation::CreateBlurFilter(20)); - sk_sp<PaintFilter> filter(sk_make_sp<BlurPaintFilter>( - 1.f, 1.f, BlurPaintFilter::TileMode::kClampToBlack_TileMode, nullptr)); + sk_sp<PaintFilter> filter( + sk_make_sp<BlurPaintFilter>(1.f, 1.f, SkTileMode::kDecal, nullptr)); filters.Append(FilterOperation::CreateReferenceFilter(std::move(filter))); EXPECT_TRUE(filters.HasFilterOfType(FilterOperation::GRAYSCALE)); diff --git a/chromium/cc/paint/frame_metadata.h b/chromium/cc/paint/frame_metadata.h index d63bb0d4363..e152074d599 100644 --- a/chromium/cc/paint/frame_metadata.h +++ b/chromium/cc/paint/frame_metadata.h @@ -12,7 +12,7 @@ namespace cc { // TODO(khushalsagar): Find a better name? struct CC_PAINT_EXPORT FrameMetadata { - FrameMetadata() {} + FrameMetadata() = default; FrameMetadata(bool complete, base::TimeDelta duration) : complete(complete), duration(duration) {} diff --git a/chromium/cc/paint/image_provider.h b/chromium/cc/paint/image_provider.h index 973f8f78a9d..ca1f2939c1c 100644 --- a/chromium/cc/paint/image_provider.h +++ b/chromium/cc/paint/image_provider.h @@ -5,6 +5,8 @@ #ifndef CC_PAINT_IMAGE_PROVIDER_H_ #define CC_PAINT_IMAGE_PROVIDER_H_ +#include <vector> + #include "base/callback.h" #include "base/optional.h" #include "cc/paint/decoded_draw_image.h" @@ -12,8 +14,6 @@ #include "cc/paint/paint_export.h" #include "cc/paint/paint_op_buffer.h" -#include <vector> - namespace cc { class PaintImage; @@ -36,7 +36,7 @@ class CC_PAINT_EXPORT ImageProvider { ScopedResult& operator=(const ScopedResult&) = delete; ScopedResult& operator=(ScopedResult&& other); - operator bool() const { return image_ || record_; } + explicit operator bool() const { return image_ || record_; } const DecodedDrawImage& decoded_image() const { return image_; } bool needs_unlock() const { return !destruction_callback_.is_null(); } const PaintRecord* paint_record() { @@ -52,7 +52,7 @@ class CC_PAINT_EXPORT ImageProvider { DestructionCallback destruction_callback_; }; - virtual ~ImageProvider() {} + virtual ~ImageProvider() = default; // Returns either: // 1. The DecodedDrawImage to use for this PaintImage. If no image is diff --git a/chromium/cc/paint/image_transfer_cache_entry.cc b/chromium/cc/paint/image_transfer_cache_entry.cc index 37077c2b1c8..8da6a2cc99d 100644 --- a/chromium/cc/paint/image_transfer_cache_entry.cc +++ b/chromium/cc/paint/image_transfer_cache_entry.cc @@ -17,10 +17,10 @@ #include "third_party/skia/include/core/SkColorSpace.h" #include "third_party/skia/include/core/SkImage.h" #include "third_party/skia/include/core/SkPixmap.h" -#include "third_party/skia/include/core/SkYUVAIndex.h" +#include "third_party/skia/include/core/SkYUVAInfo.h" #include "third_party/skia/include/gpu/GrBackendSurface.h" #include "third_party/skia/include/gpu/GrDirectContext.h" -#include "third_party/skia/include/gpu/GrTypes.h" +#include "third_party/skia/include/gpu/GrYUVABackendTextures.h" namespace cc { namespace { @@ -48,17 +48,18 @@ void ReleaseContext(SkImage::ReleaseContext context) { sk_sp<SkImage> MakeYUVImageFromUploadedPlanes( GrDirectContext* context, const std::vector<sk_sp<SkImage>>& plane_images, - YUVDecodeFormat plane_images_format, + SkYUVAInfo::PlaneConfig plane_config, + SkYUVAInfo::Subsampling subsampling, SkYUVColorSpace yuv_color_space, sk_sp<SkColorSpace> image_color_space) { // 1) Extract the textures. - DCHECK_NE(YUVDecodeFormat::kUnknown, plane_images_format); - DCHECK_EQ(NumberOfPlanesForYUVDecodeFormat(plane_images_format), + DCHECK_NE(SkYUVAInfo::PlaneConfig::kUnknown, plane_config); + DCHECK_NE(SkYUVAInfo::Subsampling::kUnknown, subsampling); + DCHECK_EQ(static_cast<size_t>(SkYUVAInfo::NumPlanes(plane_config)), plane_images.size()); DCHECK_LE(plane_images.size(), - base::checked_cast<size_t>(SkYUVASizeInfo::kMaxCount)); - std::array<GrBackendTexture, SkYUVASizeInfo::kMaxCount> - plane_backend_textures; + base::checked_cast<size_t>(SkYUVAInfo::kMaxPlanes)); + std::array<GrBackendTexture, SkYUVAInfo::kMaxPlanes> plane_backend_textures; for (size_t plane = 0u; plane < plane_images.size(); plane++) { plane_backend_textures[plane] = plane_images[plane]->getBackendTexture( true /* flushPendingGrContextIO */); @@ -69,31 +70,14 @@ sk_sp<SkImage> MakeYUVImageFromUploadedPlanes( } // 2) Create the YUV image. - SkYUVAIndex plane_indices[SkYUVAIndex::kIndexCount]; - if (plane_images_format == YUVDecodeFormat::kYUV3) { - plane_indices[SkYUVAIndex::kY_Index] = {0, SkColorChannel::kR}; - plane_indices[SkYUVAIndex::kU_Index] = {1, SkColorChannel::kR}; - plane_indices[SkYUVAIndex::kV_Index] = {2, SkColorChannel::kR}; - } else if (plane_images_format == YUVDecodeFormat::kYVU3) { - plane_indices[SkYUVAIndex::kY_Index] = {0, SkColorChannel::kR}; - plane_indices[SkYUVAIndex::kU_Index] = {2, SkColorChannel::kR}; - plane_indices[SkYUVAIndex::kV_Index] = {1, SkColorChannel::kR}; - } else if (plane_images_format == YUVDecodeFormat::kYUV2) { - plane_indices[SkYUVAIndex::kY_Index] = {0, SkColorChannel::kR}; - plane_indices[SkYUVAIndex::kU_Index] = {1, SkColorChannel::kR}; - plane_indices[SkYUVAIndex::kV_Index] = {1, SkColorChannel::kG}; - } else { - // TODO(crbug.com/910276): handle and test non-opaque images. - NOTREACHED(); - DLOG(ERROR) << "Unsupported planar format"; - return nullptr; - } - plane_indices[SkYUVAIndex::kA_Index] = {-1, SkColorChannel::kR}; + SkYUVAInfo yuva_info(plane_images[0]->dimensions(), plane_config, subsampling, + yuv_color_space); + GrYUVABackendTextures yuva_backend_textures( + yuva_info, plane_backend_textures.data(), kTopLeft_GrSurfaceOrigin); Context* ctx = new Context{plane_images}; sk_sp<SkImage> image = SkImage::MakeFromYUVATextures( - context, yuv_color_space, plane_backend_textures.data(), plane_indices, - plane_images[0]->dimensions(), kTopLeft_GrSurfaceOrigin, - std::move(image_color_space), ReleaseContext, ctx); + context, yuva_backend_textures, std::move(image_color_space), + ReleaseContext, ctx); if (!image) { DLOG(ERROR) << "Could not create YUV image"; return nullptr; @@ -190,20 +174,28 @@ ClientImageTransferCacheEntry::ClientImageTransferCacheEntry( } ClientImageTransferCacheEntry::ClientImageTransferCacheEntry( - const SkPixmap* y_pixmap, - const SkPixmap* u_pixmap, - const SkPixmap* v_pixmap, + const SkPixmap yuva_pixmaps[], + SkYUVAInfo::PlaneConfig plane_config, + SkYUVAInfo::Subsampling subsampling, const SkColorSpace* decoded_color_space, SkYUVColorSpace yuv_color_space, bool needs_mips) : needs_mips_(needs_mips), - num_planes_(3), + plane_config_(plane_config), id_(GetNextId()), pixmap_(nullptr), target_color_space_(nullptr), - yuv_pixmaps_({y_pixmap, u_pixmap, v_pixmap, nullptr}), decoded_color_space_(decoded_color_space), + subsampling_(subsampling), yuv_color_space_(yuv_color_space) { + yuv_pixmaps_.emplace(std::array<const SkPixmap*, SkYUVAInfo::kMaxPlanes>()); + size_t num_yuva_pixmaps = + static_cast<size_t>(SkYUVAInfo::NumPlanes(plane_config)); + DCHECK_GT(num_yuva_pixmaps, 0U); + DCHECK_LE(num_yuva_pixmaps, yuv_pixmaps_->size()); + for (size_t i = 0; i < num_yuva_pixmaps; ++i) { + yuv_pixmaps_->at(i) = &yuva_pixmaps[i]; + } DCHECK(IsYuv()); size_t decoded_color_space_size = decoded_color_space ? decoded_color_space->writeToMemory(nullptr) : 0u; @@ -215,23 +207,23 @@ ClientImageTransferCacheEntry::ClientImageTransferCacheEntry( // Compute and cache the size of the data. base::CheckedNumeric<uint32_t> safe_size; safe_size += PaintOpWriter::HeaderBytes(); - safe_size += sizeof(uint32_t); // is_yuv - safe_size += sizeof(uint32_t); // num_planes + safe_size += sizeof(uint32_t); // plane_config + safe_size += sizeof(uint32_t); // subsampling safe_size += sizeof(uint32_t); // has mips safe_size += sizeof(uint32_t); // yuv_color_space safe_size += sizeof(uint32_t); // yuv_color_type safe_size += decoded_color_space_size + align; - safe_size += num_planes_ * sizeof(uint64_t); // plane widths - safe_size += num_planes_ * sizeof(uint64_t); // plane heights - safe_size += num_planes_ * sizeof(uint64_t); // plane strides + safe_size += num_yuva_pixmaps * sizeof(uint64_t); // plane widths + safe_size += num_yuva_pixmaps * sizeof(uint64_t); // plane heights + safe_size += num_yuva_pixmaps * sizeof(uint64_t); // plane strides safe_size += - num_planes_ * (sizeof(uint64_t) + align); // pixels size + alignment + num_yuva_pixmaps * (sizeof(uint64_t) + align); // pixels size + alignment // Include 4 bytes of padding before each plane data chunk so we can always // align our data pointer to a 4-byte boundary. - safe_size += 4 * num_planes_; - safe_size += y_pixmap->computeByteSize(); - safe_size += u_pixmap->computeByteSize(); - safe_size += v_pixmap->computeByteSize(); + safe_size += 4 * num_yuva_pixmaps; + for (size_t i = 0; i < num_yuva_pixmaps; ++i) { + safe_size += yuv_pixmaps_->at(i)->computeByteSize(); + } size_ = safe_size.ValueOrDie(); } @@ -250,9 +242,11 @@ uint32_t ClientImageTransferCacheEntry::Id() const { void ClientImageTransferCacheEntry::ValidateYUVDataBeforeSerializing() const { DCHECK(!pixmap_); - DCHECK_LE(yuv_pixmaps_->size(), - static_cast<uint32_t>(SkYUVASizeInfo::kMaxCount)); - for (uint32_t i = 0; i < num_planes_; ++i) { + DCHECK_NE(subsampling_, SkYUVAInfo::Subsampling::kUnknown); + DCHECK_LE(yuv_pixmaps_->size(), static_cast<size_t>(SkYUVAInfo::kMaxPlanes)); + size_t num_planes = static_cast<size_t>(SkYUVAInfo::NumPlanes(plane_config_)); + DCHECK_LE(num_planes, yuv_pixmaps_->size()); + for (size_t i = 0; i < num_planes; ++i) { DCHECK(yuv_pixmaps_->at(i)); const SkPixmap* plane = yuv_pixmaps_->at(i); DCHECK_GT(plane->width(), 0); @@ -266,18 +260,19 @@ bool ClientImageTransferCacheEntry::Serialize(base::span<uint8_t> data) const { // We don't need to populate the SerializeOptions here since the writer is // only used for serializing primitives. PaintOp::SerializeOptions options(nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, false, false, 0, SkMatrix::I()); + nullptr, false, false, 0, SkM44()); PaintOpWriter writer(data.data(), data.size(), options); - writer.Write(static_cast<uint32_t>(IsYuv() ? 1 : 0)); + writer.Write(plane_config_); - if (IsYuv()) { + if (plane_config_ != SkYUVAInfo::PlaneConfig::kUnknown) { ValidateYUVDataBeforeSerializing(); - writer.Write(num_planes_); + writer.Write(subsampling_); + int num_planes = SkYUVAInfo::NumPlanes(plane_config_); writer.Write(static_cast<uint32_t>(needs_mips_ ? 1 : 0)); writer.Write(yuv_color_space_); writer.Write(decoded_color_space_); writer.Write(yuv_pixmaps_->at(0)->colorType()); - for (uint32_t i = 0; i < num_planes_; ++i) { + for (int i = 0; i < num_planes; ++i) { DCHECK(yuv_pixmaps_->at(i)); const SkPixmap* plane = yuv_pixmaps_->at(i); writer.Write(plane->width()); @@ -334,7 +329,8 @@ ServiceImageTransferCacheEntry& ServiceImageTransferCacheEntry::operator=( bool ServiceImageTransferCacheEntry::BuildFromHardwareDecodedImage( GrDirectContext* context, std::vector<sk_sp<SkImage>> plane_images, - YUVDecodeFormat plane_images_format, + SkYUVAInfo::PlaneConfig plane_config, + SkYUVAInfo::Subsampling subsampling, SkYUVColorSpace yuv_color_space, size_t buffer_byte_size, bool needs_mips) { @@ -352,8 +348,7 @@ bool ServiceImageTransferCacheEntry::BuildFromHardwareDecodedImage( DLOG(ERROR) << "Could not generate mipmap chain for plane " << plane; return false; } - plane_sizes_.push_back(GrDirectContext::ComputeImageSize( - plane_images[plane], GrMipMapped::kYes)); + plane_sizes_.push_back(plane_images[plane]->textureSize()); safe_total_size += plane_sizes_.back(); } if (!safe_total_size.AssignIfValid(&size_)) { @@ -362,14 +357,15 @@ bool ServiceImageTransferCacheEntry::BuildFromHardwareDecodedImage( } } plane_images_ = std::move(plane_images); - plane_images_format_ = plane_images_format; + plane_config_ = plane_config; + subsampling_ = subsampling; yuv_color_space_ = yuv_color_space; // 2) Create a SkImage backed by |plane_images|. // TODO(andrescj): support embedded color profiles for hardware decodes and // pass the color space to MakeYUVImageFromUploadedPlanes. - image_ = MakeYUVImageFromUploadedPlanes(context_, plane_images_, - plane_images_format_, yuv_color_space, + image_ = MakeYUVImageFromUploadedPlanes(context_, plane_images_, plane_config, + subsampling, yuv_color_space, SkColorSpace::MakeSRGB()); if (!image_) return false; @@ -395,17 +391,14 @@ bool ServiceImageTransferCacheEntry::Deserialize( PaintOp::DeserializeOptions options(nullptr, nullptr, nullptr, &scratch_buffer, false, nullptr); PaintOpReader reader(data.data(), data.size(), options); - uint32_t image_is_yuv = 0; - reader.Read(&image_is_yuv); - if (!!image_is_yuv) { - uint32_t num_planes = 0; - reader.Read(&num_planes); - // TODO(crbug.com/910276): Allow for four planes if YUVA. - // TODO(crbug.com/986575): consider serializing a YUVDecodeFormat. - if (num_planes != 3u) + plane_config_ = SkYUVAInfo::PlaneConfig::kUnknown; + reader.Read(&plane_config_); + if (plane_config_ != SkYUVAInfo::PlaneConfig::kUnknown) { + SkYUVAInfo::Subsampling subsampling = SkYUVAInfo::Subsampling::kUnknown; + reader.Read(&subsampling); + if (subsampling == SkYUVAInfo::Subsampling::kUnknown) return false; - plane_images_format_ = - num_planes == 3u ? YUVDecodeFormat::kYUV3 : YUVDecodeFormat::kYUVA4; + subsampling_ = subsampling; uint32_t needs_mips; reader.Read(&needs_mips); has_mips_ = needs_mips; @@ -417,10 +410,9 @@ bool ServiceImageTransferCacheEntry::Deserialize( SkColorType yuv_plane_color_type = kUnknown_SkColorType; reader.Read(&yuv_plane_color_type); - // Match GrTexture::onGpuMemorySize so that memory traces agree. - auto gr_mips = has_mips_ ? GrMipMapped::kYes : GrMipMapped::kNo; + int num_planes = SkYUVAInfo::NumPlanes(plane_config_); // Read in each plane and reconstruct pixmaps. - for (uint32_t i = 0; i < num_planes; i++) { + for (int i = 0; i < num_planes; i++) { uint32_t plane_width = 0; reader.Read(&plane_width); uint32_t plane_height = 0; @@ -440,7 +432,7 @@ bool ServiceImageTransferCacheEntry::Deserialize( plane_stride == 0) return false; - size_t plane_bytes; + size_t plane_bytes = 0; reader.ReadSize(&plane_bytes); SkImageInfo plane_pixmap_info = SkImageInfo::Make(plane_width, plane_height, yuv_plane_color_type, @@ -473,10 +465,8 @@ bool ServiceImageTransferCacheEntry::Deserialize( return false; DCHECK(plane->isTextureBacked()); - const size_t plane_size = - GrDirectContext::ComputeImageSize(plane, gr_mips); - size_ += plane_size; - plane_sizes_.push_back(plane_size); + plane_sizes_.push_back(plane->textureSize()); + size_ += plane_sizes_.back(); // |plane_images_| must be set for use in EnsureMips(), memory dumps, and // unit tests. @@ -484,8 +474,8 @@ bool ServiceImageTransferCacheEntry::Deserialize( } DCHECK(yuv_color_space_.has_value()); image_ = MakeYUVImageFromUploadedPlanes( - context_, plane_images_, plane_images_format_, yuv_color_space_.value(), - decoded_color_space); + context_, plane_images_, plane_config_, subsampling_.value(), + yuv_color_space_.value(), decoded_color_space); return !!image_; } @@ -540,11 +530,8 @@ bool ServiceImageTransferCacheEntry::Deserialize( SkPixmap pixmap(image_info, const_cast<const void*>(pixel_data), row_bytes); image_ = MakeSkImage(pixmap, width, height, target_color_space); - if (image_) { - // Match GrTexture::onGpuMemorySize so that memory traces agree. - auto gr_mips = has_mips_ ? GrMipMapped::kYes : GrMipMapped::kNo; - size_ = GrDirectContext::ComputeImageSize(image_, gr_mips); - } + if (image_) + size_ = image_->textureSize(); return !!image_; } @@ -608,8 +595,8 @@ void ServiceImageTransferCacheEntry::EnsureMips() { if (is_yuv()) { DCHECK(image_); DCHECK(yuv_color_space_.has_value()); - DCHECK_NE(YUVDecodeFormat::kUnknown, plane_images_format_); - DCHECK_EQ(NumberOfPlanesForYUVDecodeFormat(plane_images_format_), + DCHECK_NE(SkYUVAInfo::PlaneConfig::kUnknown, plane_config_); + DCHECK_EQ(static_cast<size_t>(SkYUVAInfo::NumPlanes(plane_config_)), plane_images_.size()); // We first do all the work with local variables. Then, if everything @@ -624,11 +611,11 @@ void ServiceImageTransferCacheEntry::EnsureMips() { if (!mipped_plane) return; mipped_planes.push_back(std::move(mipped_plane)); - mipped_plane_sizes.push_back(GrDirectContext::ComputeImageSize( - mipped_planes.back(), GrMipMapped::kYes)); + mipped_plane_sizes.push_back(mipped_planes.back()->textureSize()); } sk_sp<SkImage> mipped_image = MakeYUVImageFromUploadedPlanes( - context_, mipped_planes, plane_images_format_, yuv_color_space_.value(), + context_, mipped_planes, plane_config_, subsampling_.value(), + yuv_color_space_.value(), image_->refColorSpace() /* image_color_space */); if (!mipped_image) return; diff --git a/chromium/cc/paint/image_transfer_cache_entry.h b/chromium/cc/paint/image_transfer_cache_entry.h index ee4ad202d96..4dd8f49d59d 100644 --- a/chromium/cc/paint/image_transfer_cache_entry.h +++ b/chromium/cc/paint/image_transfer_cache_entry.h @@ -16,7 +16,7 @@ #include "cc/paint/transfer_cache_entry.h" #include "third_party/skia/include/core/SkImageInfo.h" #include "third_party/skia/include/core/SkRefCnt.h" -#include "third_party/skia/include/core/SkYUVASizeInfo.h" +#include "third_party/skia/include/core/SkYUVAInfo.h" class GrDirectContext; class SkColorSpace; @@ -49,9 +49,9 @@ class CC_PAINT_EXPORT ClientImageTransferCacheEntry final const SkColorSpace* target_color_space, bool needs_mips); explicit ClientImageTransferCacheEntry( - const SkPixmap* y_pixmap, - const SkPixmap* u_pixmap, - const SkPixmap* v_pixmap, + const SkPixmap yuva_pixmaps[], + SkYUVAInfo::PlaneConfig plane_config, + SkYUVAInfo::Subsampling subsampling, const SkColorSpace* decoded_color_space, SkYUVColorSpace yuv_color_space, bool needs_mips); @@ -68,7 +68,7 @@ class CC_PAINT_EXPORT ClientImageTransferCacheEntry final private: const bool needs_mips_ = false; - const uint32_t num_planes_ = 1; + SkYUVAInfo::PlaneConfig plane_config_ = SkYUVAInfo::PlaneConfig::kUnknown; uint32_t id_; uint32_t size_ = 0; static base::AtomicSequenceNumber s_next_id_; @@ -80,9 +80,10 @@ class CC_PAINT_EXPORT ClientImageTransferCacheEntry final // at raster. // YUVA-only members. - base::Optional<std::array<const SkPixmap*, SkYUVASizeInfo::kMaxCount>> + base::Optional<std::array<const SkPixmap*, SkYUVAInfo::kMaxPlanes>> yuv_pixmaps_; const SkColorSpace* const decoded_color_space_; + SkYUVAInfo::Subsampling subsampling_ = SkYUVAInfo::Subsampling::kUnknown; SkYUVColorSpace yuv_color_space_; // DCHECKs that the appropriate data members are set or not set and have @@ -115,7 +116,8 @@ class CC_PAINT_EXPORT ServiceImageTransferCacheEntry final // Returns true if the entry can be built, false otherwise. bool BuildFromHardwareDecodedImage(GrDirectContext* context, std::vector<sk_sp<SkImage>> plane_images, - YUVDecodeFormat plane_images_format, + SkYUVAInfo::PlaneConfig plane_config, + SkYUVAInfo::Subsampling subsampling, SkYUVColorSpace yuv_color_space, size_t buffer_byte_size, bool needs_mips); @@ -141,7 +143,9 @@ class CC_PAINT_EXPORT ServiceImageTransferCacheEntry final return plane_sizes_; } bool is_yuv() const { return !plane_images_.empty(); } - size_t num_planes() const { return is_yuv() ? plane_images_.size() : 1u; } + size_t num_planes() const { + return is_yuv() ? SkYUVAInfo::NumPlanes(plane_config_) : 1u; + } private: sk_sp<SkImage> MakeSkImage(const SkPixmap& pixmap, @@ -151,9 +155,10 @@ class CC_PAINT_EXPORT ServiceImageTransferCacheEntry final GrDirectContext* context_ = nullptr; std::vector<sk_sp<SkImage>> plane_images_; - YUVDecodeFormat plane_images_format_ = YUVDecodeFormat::kUnknown; + SkYUVAInfo::PlaneConfig plane_config_ = SkYUVAInfo::PlaneConfig::kUnknown; std::vector<size_t> plane_sizes_; sk_sp<SkImage> image_; + base::Optional<SkYUVAInfo::Subsampling> subsampling_; base::Optional<SkYUVColorSpace> yuv_color_space_; bool has_mips_ = false; size_t size_ = 0; diff --git a/chromium/cc/paint/image_transfer_cache_entry_unittest.cc b/chromium/cc/paint/image_transfer_cache_entry_unittest.cc index 1ff291d3696..e57ab845546 100644 --- a/chromium/cc/paint/image_transfer_cache_entry_unittest.cc +++ b/chromium/cc/paint/image_transfer_cache_entry_unittest.cc @@ -21,6 +21,7 @@ #include "third_party/skia/include/core/SkImageInfo.h" #include "third_party/skia/include/core/SkPixmap.h" #include "third_party/skia/include/core/SkRefCnt.h" +#include "third_party/skia/include/core/SkYUVAPixmaps.h" #include "third_party/skia/include/gpu/GrBackendSurface.h" #include "third_party/skia/include/gpu/GrDirectContext.h" #include "third_party/skia/include/gpu/GrTypes.h" @@ -78,7 +79,7 @@ bool CheckImageIsSolidColor(const sk_sp<SkImage>& image, } class ImageTransferCacheEntryTest - : public testing::TestWithParam<YUVDecodeFormat> { + : public testing::TestWithParam<SkYUVAInfo::PlaneConfig> { public: void SetUp() override { // Initialize a GL GrContext for Skia. @@ -109,8 +110,8 @@ class ImageTransferCacheEntryTest std::unique_ptr<bool[]>* release_flags) { std::vector<sk_sp<SkImage>> plane_images; *release_flags = nullptr; - if (GetParam() == YUVDecodeFormat::kYUV3 || - GetParam() == YUVDecodeFormat::kYVU3) { + if (GetParam() == SkYUVAInfo::PlaneConfig::kY_U_V || + GetParam() == SkYUVAInfo::PlaneConfig::kY_V_U) { *release_flags = std::unique_ptr<bool[]>(new bool[3]{false, false, false}); plane_images = { @@ -120,7 +121,7 @@ class ImageTransferCacheEntryTest release_flags->get() + 1), CreateSolidPlane(gr_context(), 32, 32, GL_R8_EXT, SkColors::kWhite, release_flags->get() + 2)}; - } else if (GetParam() == YUVDecodeFormat::kYUV2) { + } else if (GetParam() == SkYUVAInfo::PlaneConfig::kY_UV) { *release_flags = std::unique_ptr<bool[]>(new bool[2]{false, false}); plane_images = { CreateSolidPlane(gr_context(), 64, 64, GL_R8_EXT, SkColors::kWhite, @@ -204,40 +205,30 @@ TEST_P(ImageTransferCacheEntryTest, Deserialize) { // width to test that alignment works correctly. const int image_width = 12; const int image_height = 10; - const size_t y_stride = 16; - const size_t uv_stride = 8; - - const size_t y_bytes = y_stride * image_height; - const size_t uv_bytes = uv_stride * image_height / 2; - const size_t planes_size = y_bytes + 2 * uv_bytes; - std::unique_ptr<char[]> planes_data(new char[planes_size]); - - void* planes[3]; - planes[0] = reinterpret_cast<void*>(planes_data.get()); - planes[1] = reinterpret_cast<char*>(planes[0]) + y_bytes; - planes[2] = reinterpret_cast<char*>(planes[1]) + uv_bytes; - - auto info = SkImageInfo::Make(image_width, image_height, kGray_8_SkColorType, - kUnknown_SkAlphaType); - SkPixmap y_pixmap(info, planes[0], y_stride); - SkPixmap u_pixmap(info.makeWH(image_width / 2, image_height / 2), planes[1], - uv_stride); - SkPixmap v_pixmap(info.makeWH(image_width / 2, image_height / 2), planes[2], - uv_stride); + const size_t yuv_strides[] = {16, 8, 8}; + + SkYUVAInfo yuva_info({image_width, image_height}, + SkYUVAInfo::PlaneConfig::kY_U_V, + SkYUVAInfo::Subsampling::k420, kJpegYUVColorSpace); + SkYUVAPixmapInfo yuva_pixmap_info( + yuva_info, SkYUVAPixmapInfo::DataType::kUnorm8, yuv_strides); + SkYUVAPixmaps yuva_pixmaps = SkYUVAPixmaps::Allocate(yuva_pixmap_info); // rgb (255, 121, 255) -> yuv (255, 255, 255) const SkIRect bottom_color_rect = SkIRect::MakeXYWH(0, image_height / 2, image_width, image_height / 2); - ASSERT_TRUE(y_pixmap.erase(SkColors::kWhite)); - ASSERT_TRUE(u_pixmap.erase(SkColors::kWhite)); - ASSERT_TRUE(v_pixmap.erase(SkColors::kWhite)); + ASSERT_TRUE(yuva_pixmaps.plane(0).erase(SkColors::kWhite)); + ASSERT_TRUE(yuva_pixmaps.plane(1).erase(SkColors::kWhite)); + ASSERT_TRUE(yuva_pixmaps.plane(2).erase(SkColors::kWhite)); + // rgb (178, 0, 225) -> yuv (0, 255, 255) const SkIRect top_color_rect = SkIRect::MakeWH(image_width, image_height / 2); - ASSERT_TRUE(y_pixmap.erase(SkColors::kBlack, &top_color_rect)); + ASSERT_TRUE(yuva_pixmaps.plane(0).erase(SkColors::kBlack, &top_color_rect)); auto client_entry(std::make_unique<ClientImageTransferCacheEntry>( - &y_pixmap, &u_pixmap, &v_pixmap, nullptr, kJpegYUVColorSpace, - true /* needs_mips */)); + yuva_pixmaps.planes().data(), yuva_info.planeConfig(), + yuva_info.subsampling(), nullptr /* decoded color space*/, + yuva_info.yuvColorSpace(), true /* needs_mips */)); uint32_t size = client_entry->SerializedSize(); std::vector<uint8_t> data(size); ASSERT_TRUE(client_entry->Serialize( @@ -263,7 +254,8 @@ TEST_P(ImageTransferCacheEntryTest, HardwareDecodedNoMipsAtCreation) { std::unique_ptr<bool[]> release_flags; std::vector<sk_sp<SkImage>> plane_images = CreateTestYUVImage(&release_flags); const size_t plane_images_size = plane_images.size(); - ASSERT_EQ(NumberOfPlanesForYUVDecodeFormat(GetParam()), plane_images_size); + ASSERT_EQ(static_cast<size_t>(SkYUVAInfo::NumPlanes(GetParam())), + plane_images_size); // Create a service-side image cache entry backed by these planes and do not // request generating mipmap chains. The |buffer_byte_size| is only used for @@ -271,8 +263,8 @@ TEST_P(ImageTransferCacheEntryTest, HardwareDecodedNoMipsAtCreation) { auto entry(std::make_unique<ServiceImageTransferCacheEntry>()); EXPECT_TRUE(entry->BuildFromHardwareDecodedImage( gr_context(), std::move(plane_images), - GetParam() /* plane_images_format */, kJpegYUVColorSpace, - 0u /* buffer_byte_size */, false /* needs_mips */)); + GetParam() /* plane_images_format */, SkYUVAInfo::Subsampling::k420, + kJpegYUVColorSpace, 0u /* buffer_byte_size */, false /* needs_mips */)); // We didn't request generating mipmap chains, so the textures we created // above should stay alive until after the cache entry is deleted. @@ -289,7 +281,8 @@ TEST_P(ImageTransferCacheEntryTest, HardwareDecodedMipsAtCreation) { std::unique_ptr<bool[]> release_flags; std::vector<sk_sp<SkImage>> plane_images = CreateTestYUVImage(&release_flags); const size_t plane_images_size = plane_images.size(); - ASSERT_EQ(NumberOfPlanesForYUVDecodeFormat(GetParam()), plane_images_size); + ASSERT_EQ(static_cast<size_t>(SkYUVAInfo::NumPlanes(GetParam())), + plane_images_size); // Create a service-side image cache entry backed by these planes and request // generating mipmap chains at creation time. The |buffer_byte_size| is only @@ -297,8 +290,8 @@ TEST_P(ImageTransferCacheEntryTest, HardwareDecodedMipsAtCreation) { auto entry(std::make_unique<ServiceImageTransferCacheEntry>()); EXPECT_TRUE(entry->BuildFromHardwareDecodedImage( gr_context(), std::move(plane_images), - GetParam() /* plane_images_format */, kJpegYUVColorSpace, - 0u /* buffer_byte_size */, true /* needs_mips */)); + GetParam() /* plane_images_format */, SkYUVAInfo::Subsampling::k420, + kJpegYUVColorSpace, 0u /* buffer_byte_size */, true /* needs_mips */)); // We requested generating mipmap chains at creation time, so the textures we // created above should be released by now. @@ -320,7 +313,8 @@ TEST_P(ImageTransferCacheEntryTest, HardwareDecodedMipsAfterCreation) { std::unique_ptr<bool[]> release_flags; std::vector<sk_sp<SkImage>> plane_images = CreateTestYUVImage(&release_flags); const size_t plane_images_size = plane_images.size(); - ASSERT_EQ(NumberOfPlanesForYUVDecodeFormat(GetParam()), plane_images_size); + ASSERT_EQ(static_cast<size_t>(SkYUVAInfo::NumPlanes(GetParam())), + plane_images_size); // Create a service-side image cache entry backed by these planes and do not // request generating mipmap chains at creation time. The |buffer_byte_size| @@ -328,8 +322,8 @@ TEST_P(ImageTransferCacheEntryTest, HardwareDecodedMipsAfterCreation) { auto entry(std::make_unique<ServiceImageTransferCacheEntry>()); EXPECT_TRUE(entry->BuildFromHardwareDecodedImage( gr_context(), std::move(plane_images), - GetParam() /* plane_images_format */, kJpegYUVColorSpace, - 0u /* buffer_byte_size */, false /* needs_mips */)); + GetParam() /* plane_images_format */, SkYUVAInfo::Subsampling::k420, + kJpegYUVColorSpace, 0u /* buffer_byte_size */, false /* needs_mips */)); // We didn't request generating mip chains, so the textures we created above // should stay alive for now. @@ -356,14 +350,14 @@ TEST_P(ImageTransferCacheEntryTest, HardwareDecodedMipsAfterCreation) { } std::string TestParamToString( - const testing::TestParamInfo<YUVDecodeFormat>& param_info) { + const testing::TestParamInfo<SkYUVAInfo::PlaneConfig>& param_info) { switch (param_info.param) { - case YUVDecodeFormat::kYUV3: - return "YUV3"; - case YUVDecodeFormat::kYVU3: - return "YVU3"; - case YUVDecodeFormat::kYUV2: - return "YUV2"; + case SkYUVAInfo::PlaneConfig::kY_U_V: + return "Y_U_V"; + case SkYUVAInfo::PlaneConfig::kY_V_U: + return "Y_V_U"; + case SkYUVAInfo::PlaneConfig::kY_UV: + return "Y_UV"; default: NOTREACHED(); return ""; @@ -372,9 +366,9 @@ std::string TestParamToString( INSTANTIATE_TEST_SUITE_P(All, ImageTransferCacheEntryTest, - ::testing::Values(YUVDecodeFormat::kYUV3, - YUVDecodeFormat::kYVU3, - YUVDecodeFormat::kYUV2), + ::testing::Values(SkYUVAInfo::PlaneConfig::kY_U_V, + SkYUVAInfo::PlaneConfig::kY_V_U, + SkYUVAInfo::PlaneConfig::kY_UV), TestParamToString); TEST(ImageTransferCacheEntryTestNoYUV, CPUImageWithMips) { diff --git a/chromium/cc/paint/oop_pixeltest.cc b/chromium/cc/paint/oop_pixeltest.cc index 0f6029834e1..d5eec1c990b 100644 --- a/chromium/cc/paint/oop_pixeltest.cc +++ b/chromium/cc/paint/oop_pixeltest.cc @@ -520,9 +520,9 @@ TEST_P(OopImagePixelTest, DrawImage) { auto display_item_list = base::MakeRefCounted<DisplayItemList>(); display_item_list->StartPaint(); - PaintFlags flags; - flags.setFilterQuality(FilterQuality()); - display_item_list->push<DrawImageOp>(paint_image, 0.f, 0.f, &flags); + SkSamplingOptions sampling(FilterQuality()); + display_item_list->push<DrawImageOp>(paint_image, 0.f, 0.f, sampling, + nullptr); display_item_list->EndPaintOfUnpaired(rect); display_item_list->Finalize(); @@ -559,9 +559,9 @@ TEST_P(OopImagePixelTest, DrawImageScaled) { auto display_item_list = base::MakeRefCounted<DisplayItemList>(); display_item_list->StartPaint(); display_item_list->push<ScaleOp>(0.5f, 0.5f); - PaintFlags flags; - flags.setFilterQuality(FilterQuality()); - display_item_list->push<DrawImageOp>(paint_image, 0.f, 0.f, &flags); + SkSamplingOptions sampling(FilterQuality()); + display_item_list->push<DrawImageOp>(paint_image, 0.f, 0.f, sampling, + nullptr); display_item_list->EndPaintOfUnpaired(rect); display_item_list->Finalize(); @@ -633,9 +633,8 @@ TEST_P(OopImagePixelTest, DrawRecordShaderWithImageScaled) { PaintImage::GetNextId()); auto paint_image = builder.TakePaintImage(); auto paint_record = sk_make_sp<PaintOpBuffer>(); - PaintFlags flags; - flags.setFilterQuality(FilterQuality()); - paint_record->push<DrawImageOp>(paint_image, 0.f, 0.f, &flags); + SkSamplingOptions sampling(FilterQuality()); + paint_record->push<DrawImageOp>(paint_image, 0.f, 0.f, sampling, nullptr); auto paint_record_shader = PaintShader::MakePaintRecord( paint_record, gfx::RectToSkRect(rect), SkTileMode::kRepeat, SkTileMode::kRepeat, nullptr); @@ -683,6 +682,10 @@ TEST_F(OopImagePixelTest, DrawRecordShaderTranslatedTileRect) { sk_sp<PaintShader> paint_record_shader = PaintShader::MakePaintRecord( shader_buffer, tile_rect, SkTileMode::kRepeat, SkTileMode::kRepeat, nullptr, PaintShader::ScalingBehavior::kRasterAtScale); + // Force paint_flags to convert this to kFixedScale, so we can safely compare + // pixels between direct and oop-r modes (since oop will convert to + // kFixedScale no matter what. + paint_record_shader->set_has_animated_images(true); gfx::Size output_size(10, 10); @@ -728,9 +731,9 @@ TEST_P(OopImagePixelTest, DrawImageWithTargetColorSpace) { auto display_item_list = base::MakeRefCounted<DisplayItemList>(); display_item_list->StartPaint(); - PaintFlags flags; - flags.setFilterQuality(FilterQuality()); - display_item_list->push<DrawImageOp>(paint_image, 0.f, 0.f, &flags); + SkSamplingOptions sampling(FilterQuality()); + display_item_list->push<DrawImageOp>(paint_image, 0.f, 0.f, sampling, + nullptr); display_item_list->EndPaintOfUnpaired(rect); display_item_list->Finalize(); @@ -774,9 +777,9 @@ TEST_P(OopImagePixelTest, DrawImageWithSourceColorSpace) { auto display_item_list = base::MakeRefCounted<DisplayItemList>(); display_item_list->StartPaint(); - PaintFlags flags; - flags.setFilterQuality(FilterQuality()); - display_item_list->push<DrawImageOp>(paint_image, 0.f, 0.f, &flags); + SkSamplingOptions sampling(FilterQuality()); + display_item_list->push<DrawImageOp>(paint_image, 0.f, 0.f, sampling, + nullptr); display_item_list->EndPaintOfUnpaired(rect); display_item_list->Finalize(); @@ -819,9 +822,9 @@ TEST_P(OopImagePixelTest, DrawImageWithSourceAndTargetColorSpace) { auto display_item_list = base::MakeRefCounted<DisplayItemList>(); display_item_list->StartPaint(); - PaintFlags flags; - flags.setFilterQuality(FilterQuality()); - display_item_list->push<DrawImageOp>(paint_image, 0.f, 0.f, &flags); + SkSamplingOptions sampling(FilterQuality()); + display_item_list->push<DrawImageOp>(paint_image, 0.f, 0.f, sampling, + nullptr); display_item_list->EndPaintOfUnpaired(rect); display_item_list->Finalize(); @@ -859,10 +862,10 @@ TEST_P(OopImagePixelTest, DrawImageWithSetMatrix) { auto display_item_list = base::MakeRefCounted<DisplayItemList>(); display_item_list->StartPaint(); - PaintFlags flags; - flags.setFilterQuality(FilterQuality()); - display_item_list->push<SetMatrixOp>(SkMatrix::Scale(0.5f, 0.5f)); - display_item_list->push<DrawImageOp>(paint_image, 0.f, 0.f, &flags); + SkSamplingOptions sampling(FilterQuality()); + display_item_list->push<SetMatrixOp>(SkM44::Scale(0.5f, 0.5f)); + display_item_list->push<DrawImageOp>(paint_image, 0.f, 0.f, sampling, + nullptr); display_item_list->EndPaintOfUnpaired(rect); display_item_list->Finalize(); @@ -934,8 +937,7 @@ TEST_F(OopPixelTest, DrawMailboxBackedImage) { auto display_item_list = base::MakeRefCounted<DisplayItemList>(); display_item_list->StartPaint(); - PaintFlags flags; - display_item_list->push<DrawImageOp>(src_paint_image, 0.f, 0.f, &flags); + display_item_list->push<DrawImageOp>(src_paint_image, 0.f, 0.f); display_item_list->EndPaintOfUnpaired(gfx::Rect(options.resource_size)); display_item_list->Finalize(); @@ -1712,7 +1714,12 @@ class OopRecordShaderPixelTest : public OopPixelTest, BuildTextBlob(SkTypeface::MakeDefault(), UseLcdText()), 0u, 0u, flags); auto paint_record_shader = PaintShader::MakePaintRecord( paint_record, SkRect::MakeWH(25, 25), SkTileMode::kRepeat, - SkTileMode::kRepeat, nullptr); + SkTileMode::kRepeat, nullptr, + PaintShader::ScalingBehavior::kRasterAtScale); + // Force paint_flags to convert this to kFixedScale, so we can safely + // compare pixels between direct and oop-r modes (since oop will convert to + // kFixedScale no matter what. + paint_record_shader->set_has_animated_images(true); auto display_item_list = base::MakeRefCounted<DisplayItemList>(); display_item_list->StartPaint(); @@ -1737,7 +1744,7 @@ class OopRecordFilterPixelTest : public OopPixelTest, public ::testing::WithParamInterface<bool> { public: bool UseLcdText() const { return GetParam(); } - void RunTest(const SkMatrix& mat) { + void RunTest(const SkM44& mat) { RasterOptions options; options.resource_size = gfx::Size(100, 100); options.content_size = options.resource_size; @@ -1771,13 +1778,16 @@ class OopRecordFilterPixelTest : public OopPixelTest, }; TEST_P(OopRecordFilterPixelTest, FilterWithTextScaled) { - SkMatrix mat = SkMatrix::Scale(2.f, 2.f); + SkM44 mat = SkM44::Scale(2.f, 2.f); RunTest(mat); } TEST_P(OopRecordFilterPixelTest, FilterWithTextAndComplexCTM) { - SkMatrix mat = SkMatrix::Scale(2.f, 2.f); - mat.preSkew(2.f, 2.f); + SkM44 mat = SkM44::Scale(2.f, 2.f); + SkM44 skew = SkM44(); + skew.setRC(0, 1, 2.f); + skew.setRC(1, 0, 2.f); + mat.preConcat(skew); RunTest(mat); } @@ -1906,12 +1916,13 @@ TEST_F(OopPixelTest, ConvertYUVToRGB) { auto* sii = raster_context_provider_->SharedImageInterface(); gpu::Mailbox dest_mailbox = CreateMailboxSharedImage( ri, sii, options, viz::ResourceFormat::RGBA_8888); - gpu::Mailbox y_mailbox = CreateMailboxSharedImage( - ri, sii, options, viz::ResourceFormat::LUMINANCE_8); - gpu::Mailbox u_mailbox = CreateMailboxSharedImage( - ri, sii, uv_options, viz::ResourceFormat::LUMINANCE_8); - gpu::Mailbox v_mailbox = CreateMailboxSharedImage( - ri, sii, uv_options, viz::ResourceFormat::LUMINANCE_8); + gpu::Mailbox yuv_mailboxes[3]{ + CreateMailboxSharedImage(ri, sii, options, + viz::ResourceFormat::LUMINANCE_8), + CreateMailboxSharedImage(ri, sii, uv_options, + viz::ResourceFormat::LUMINANCE_8), + CreateMailboxSharedImage(ri, sii, uv_options, + viz::ResourceFormat::LUMINANCE_8)}; size_t y_pixels_size = options.resource_size.GetArea(); size_t uv_pixels_size = uv_options.resource_size.GetArea(); @@ -1926,31 +1937,33 @@ TEST_F(OopPixelTest, ConvertYUVToRGB) { // Upload initial yuv image data gpu::gles2::GLES2Interface* gl = gles2_context_provider_->ContextGL(); - UploadPixels(gl, y_mailbox, options.resource_size, GL_LUMINANCE, + UploadPixels(gl, yuv_mailboxes[0], options.resource_size, GL_LUMINANCE, GL_UNSIGNED_BYTE, y_pix.get()); - UploadPixels(gl, u_mailbox, uv_options.resource_size, GL_LUMINANCE, + UploadPixels(gl, yuv_mailboxes[1], uv_options.resource_size, GL_LUMINANCE, GL_UNSIGNED_BYTE, u_pix.get()); - UploadPixels(gl, v_mailbox, uv_options.resource_size, GL_LUMINANCE, + UploadPixels(gl, yuv_mailboxes[2], uv_options.resource_size, GL_LUMINANCE, GL_UNSIGNED_BYTE, v_pix.get()); gl->OrderingBarrierCHROMIUM(); - ri->ConvertYUVMailboxesToRGB(dest_mailbox, kJPEG_SkYUVColorSpace, y_mailbox, - u_mailbox, v_mailbox); + ri->ConvertYUVAMailboxesToRGB(dest_mailbox, kJPEG_SkYUVColorSpace, + SkYUVAInfo::PlaneConfig::kY_U_V, + SkYUVAInfo::Subsampling::k420, yuv_mailboxes); ri->OrderingBarrierCHROMIUM(); SkBitmap actual_bitmap = ReadbackMailbox(gl, dest_mailbox, options); // Create the expected result using SkImage::MakeFromYUVTextures GrBackendTexture backend_textures[3]; - backend_textures[0] = MakeBackendTexture(gl, y_mailbox, options.resource_size, - GL_LUMINANCE8_EXT); + backend_textures[0] = MakeBackendTexture( + gl, yuv_mailboxes[0], options.resource_size, GL_LUMINANCE8_EXT); backend_textures[1] = MakeBackendTexture( - gl, u_mailbox, uv_options.resource_size, GL_LUMINANCE8_EXT); + gl, yuv_mailboxes[1], uv_options.resource_size, GL_LUMINANCE8_EXT); backend_textures[2] = MakeBackendTexture( - gl, v_mailbox, uv_options.resource_size, GL_LUMINANCE8_EXT); + gl, yuv_mailboxes[2], uv_options.resource_size, GL_LUMINANCE8_EXT); SkYUVAInfo yuva_info( {options.resource_size.width(), options.resource_size.height()}, - SkYUVAInfo::PlanarConfig::kY_U_V_420, kJPEG_Full_SkYUVColorSpace); + SkYUVAInfo::PlaneConfig::kY_U_V, SkYUVAInfo::Subsampling::k420, + kJPEG_Full_SkYUVColorSpace); GrYUVABackendTextures yuva_textures(yuva_info, backend_textures, kTopLeft_GrSurfaceOrigin); @@ -1972,9 +1985,9 @@ TEST_F(OopPixelTest, ConvertYUVToRGB) { gpu::SyncToken sync_token; gl->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData()); sii->DestroySharedImage(sync_token, dest_mailbox); - sii->DestroySharedImage(sync_token, y_mailbox); - sii->DestroySharedImage(sync_token, u_mailbox); - sii->DestroySharedImage(sync_token, v_mailbox); + sii->DestroySharedImage(sync_token, yuv_mailboxes[0]); + sii->DestroySharedImage(sync_token, yuv_mailboxes[1]); + sii->DestroySharedImage(sync_token, yuv_mailboxes[2]); } TEST_F(OopPixelTest, ReadbackImagePixels) { @@ -2031,10 +2044,11 @@ TEST_F(OopPixelTest, ConvertNV12ToRGB) { gpu::Mailbox dest_mailbox = CreateMailboxSharedImage( ri, sii, options, viz::ResourceFormat::RGBA_8888); - gpu::Mailbox y_mailbox = CreateMailboxSharedImage( - ri, sii, options, viz::ResourceFormat::LUMINANCE_8); - gpu::Mailbox uv_mailbox = - CreateMailboxSharedImage(ri, sii, uv_options, viz::ResourceFormat::RG_88); + gpu::Mailbox y_uv_mailboxes[2]{ + CreateMailboxSharedImage(ri, sii, options, + viz::ResourceFormat::LUMINANCE_8), + CreateMailboxSharedImage(ri, sii, uv_options, viz::ResourceFormat::RG_88), + }; size_t y_pixels_size = options.resource_size.GetArea(); size_t uv_pixels_size = uv_options.resource_size.GetArea() * 2; @@ -2048,27 +2062,29 @@ TEST_F(OopPixelTest, ConvertNV12ToRGB) { } gpu::gles2::GLES2Interface* gl = gles2_context_provider_->ContextGL(); - UploadPixels(gl, y_mailbox, options.resource_size, GL_LUMINANCE, + UploadPixels(gl, y_uv_mailboxes[0], options.resource_size, GL_LUMINANCE, GL_UNSIGNED_BYTE, y_pix.get()); - UploadPixels(gl, uv_mailbox, uv_options.resource_size, GL_RG, + UploadPixels(gl, y_uv_mailboxes[1], uv_options.resource_size, GL_RG, GL_UNSIGNED_BYTE, uv_pix.get()); gl->OrderingBarrierCHROMIUM(); - ri->ConvertNV12MailboxesToRGB(dest_mailbox, kJPEG_SkYUVColorSpace, y_mailbox, - uv_mailbox); + ri->ConvertYUVAMailboxesToRGB(dest_mailbox, kJPEG_SkYUVColorSpace, + SkYUVAInfo::PlaneConfig::kY_UV, + SkYUVAInfo::Subsampling::k420, y_uv_mailboxes); ri->OrderingBarrierCHROMIUM(); SkBitmap actual_bitmap = ReadbackMailbox(gl, dest_mailbox, options); // Create the expected result using SkImage::MakeFromYUVTextures GrBackendTexture backend_textures[2]; - backend_textures[0] = MakeBackendTexture(gl, y_mailbox, options.resource_size, - GL_LUMINANCE8_EXT); - backend_textures[1] = - MakeBackendTexture(gl, uv_mailbox, uv_options.resource_size, GL_RG8); + backend_textures[0] = MakeBackendTexture( + gl, y_uv_mailboxes[0], options.resource_size, GL_LUMINANCE8_EXT); + backend_textures[1] = MakeBackendTexture(gl, y_uv_mailboxes[1], + uv_options.resource_size, GL_RG8); SkYUVAInfo yuva_info( {options.resource_size.width(), options.resource_size.height()}, - SkYUVAInfo::PlanarConfig::kY_UV_420, kJPEG_Full_SkYUVColorSpace); + SkYUVAInfo::PlaneConfig::kY_UV, SkYUVAInfo::Subsampling::k420, + kJPEG_Full_SkYUVColorSpace); GrYUVABackendTextures yuva_textures(yuva_info, backend_textures, kTopLeft_GrSurfaceOrigin); auto expected_image = SkImage::MakeFromYUVATextures( @@ -2089,8 +2105,8 @@ TEST_F(OopPixelTest, ConvertNV12ToRGB) { gpu::SyncToken sync_token; gl->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData()); sii->DestroySharedImage(sync_token, dest_mailbox); - sii->DestroySharedImage(sync_token, y_mailbox); - sii->DestroySharedImage(sync_token, uv_mailbox); + sii->DestroySharedImage(sync_token, y_uv_mailboxes[0]); + sii->DestroySharedImage(sync_token, y_uv_mailboxes[1]); } #endif // !defined(OS_ANDROID) diff --git a/chromium/cc/paint/paint_canvas.h b/chromium/cc/paint/paint_canvas.h index 2b9c34a2336..e22af8127f6 100644 --- a/chromium/cc/paint/paint_canvas.h +++ b/chromium/cc/paint/paint_canvas.h @@ -76,8 +76,12 @@ class CC_PAINT_EXPORT PaintCanvas { virtual void translate(SkScalar dx, SkScalar dy) = 0; virtual void scale(SkScalar sx, SkScalar sy) = 0; virtual void rotate(SkScalar degrees) = 0; + // TODO(aaronhk): crbug.com/1153330 deprecate these in favor of the SkM44 + // versions. virtual void concat(const SkMatrix& matrix) = 0; virtual void setMatrix(const SkMatrix& matrix) = 0; + virtual void concat(const SkM44& matrix) = 0; + virtual void setMatrix(const SkM44& matrix) = 0; virtual void clipRect(const SkRect& rect, SkClipOp op, @@ -141,16 +145,24 @@ class CC_PAINT_EXPORT PaintCanvas { virtual void drawImage(const PaintImage& image, SkScalar left, SkScalar top, + const SkSamplingOptions&, const PaintFlags* flags) = 0; void drawImage(const PaintImage& image, SkScalar left, SkScalar top) { - drawImage(image, left, top, nullptr); + drawImage(image, left, top, SkSamplingOptions(), nullptr); } virtual void drawImageRect(const PaintImage& image, const SkRect& src, const SkRect& dst, + const SkSamplingOptions&, const PaintFlags* flags, SkCanvas::SrcRectConstraint constraint) = 0; + void drawImageRect(const PaintImage& image, + const SkRect& src, + const SkRect& dst, + SkCanvas::SrcRectConstraint constraint) { + drawImageRect(image, src, dst, SkSamplingOptions(), nullptr, constraint); + } // Draws the frame of the |skottie| animation specified by the normalized time // t [0->first frame..1->last frame] at the destination bounds given by |dst| diff --git a/chromium/cc/paint/paint_filter.cc b/chromium/cc/paint/paint_filter.cc index 5018eb8bf7e..9e32ec4b729 100644 --- a/chromium/cc/paint/paint_filter.cc +++ b/chromium/cc/paint/paint_filter.cc @@ -10,21 +10,8 @@ #include "cc/paint/paint_record.h" #include "third_party/skia/include/core/SkColorFilter.h" #include "third_party/skia/include/core/SkMath.h" -#include "third_party/skia/include/effects/SkAlphaThresholdFilter.h" -#include "third_party/skia/include/effects/SkArithmeticImageFilter.h" -#include "third_party/skia/include/effects/SkColorFilterImageFilter.h" -#include "third_party/skia/include/effects/SkComposeImageFilter.h" -#include "third_party/skia/include/effects/SkImageSource.h" -#include "third_party/skia/include/effects/SkLightingImageFilter.h" -#include "third_party/skia/include/effects/SkMagnifierImageFilter.h" -#include "third_party/skia/include/effects/SkMergeImageFilter.h" -#include "third_party/skia/include/effects/SkMorphologyImageFilter.h" -#include "third_party/skia/include/effects/SkOffsetImageFilter.h" -#include "third_party/skia/include/effects/SkPaintImageFilter.h" +#include "third_party/skia/include/effects/SkImageFilters.h" #include "third_party/skia/include/effects/SkPerlinNoiseShader.h" -#include "third_party/skia/include/effects/SkPictureImageFilter.h" -#include "third_party/skia/include/effects/SkTileImageFilter.h" -#include "third_party/skia/include/effects/SkXfermodeImageFilter.h" namespace cc { namespace { @@ -140,8 +127,7 @@ size_t PaintFilter::BaseSerializedSize() const { total_size += sizeof(uint32_t); if (crop_rect_) { // CropRect. - total_size += sizeof(crop_rect_->flags()); - total_size += sizeof(crop_rect_->rect()); + total_size += sizeof(*crop_rect_); } return total_size; } @@ -159,9 +145,7 @@ bool PaintFilter::operator==(const PaintFilter& other) const { if (!!crop_rect_ != !!other.crop_rect_) return false; if (crop_rect_) { - if (crop_rect_->flags() != other.crop_rect_->flags() || - !PaintOp::AreSkRectsEqual(crop_rect_->rect(), - other.crop_rect_->rect())) { + if (!PaintOp::AreSkRectsEqual(*crop_rect_, *other.crop_rect_)) { return false; } } @@ -248,7 +232,7 @@ ColorFilterPaintFilter::ColorFilterPaintFilter( color_filter_(std::move(color_filter)), input_(std::move(input)) { DCHECK(color_filter_); - cached_sk_filter_ = SkColorFilterImageFilter::Make( + cached_sk_filter_ = SkImageFilters::ColorFilter( color_filter_, GetSkFilter(input_.get()), crop_rect); } @@ -277,7 +261,7 @@ bool ColorFilterPaintFilter::operator==( BlurPaintFilter::BlurPaintFilter(SkScalar sigma_x, SkScalar sigma_y, - TileMode tile_mode, + SkTileMode tile_mode, sk_sp<PaintFilter> input, const CropRect* crop_rect) : PaintFilter(kType, crop_rect, HasDiscardableImages(input)), @@ -285,8 +269,8 @@ BlurPaintFilter::BlurPaintFilter(SkScalar sigma_x, sigma_y_(sigma_y), tile_mode_(tile_mode), input_(std::move(input)) { - cached_sk_filter_ = SkBlurImageFilter::Make( - sigma_x_, sigma_y_, GetSkFilter(input_.get()), crop_rect, tile_mode_); + cached_sk_filter_ = SkImageFilters::Blur( + sigma_x, sigma_y, tile_mode_, GetSkFilter(input_.get()), crop_rect); } BlurPaintFilter::~BlurPaintFilter() = default; @@ -329,9 +313,15 @@ DropShadowPaintFilter::DropShadowPaintFilter(SkScalar dx, color_(color), shadow_mode_(shadow_mode), input_(std::move(input)) { - cached_sk_filter_ = SkDropShadowImageFilter::Make( - dx_, dy_, sigma_x_, sigma_y_, color_, shadow_mode_, - GetSkFilter(input_.get()), crop_rect); + if (shadow_mode == ShadowMode::kDrawShadowOnly) { + cached_sk_filter_ = + SkImageFilters::DropShadowOnly(dx_, dy_, sigma_x_, sigma_y_, color_, + GetSkFilter(input_.get()), crop_rect); + } else { + cached_sk_filter_ = + SkImageFilters::DropShadow(dx_, dy_, sigma_x_, sigma_y_, color_, + GetSkFilter(input_.get()), crop_rect); + } } DropShadowPaintFilter::~DropShadowPaintFilter() = default; @@ -369,7 +359,7 @@ MagnifierPaintFilter::MagnifierPaintFilter(const SkRect& src_rect, src_rect_(src_rect), inset_(inset), input_(std::move(input)) { - cached_sk_filter_ = SkMagnifierImageFilter::Make( + cached_sk_filter_ = SkImageFilters::Magnifier( src_rect_, inset_, GetSkFilter(input_.get()), crop_rect); } @@ -401,8 +391,8 @@ ComposePaintFilter::ComposePaintFilter(sk_sp<PaintFilter> outer, HasDiscardableImages(outer) || HasDiscardableImages(inner)), outer_(std::move(outer)), inner_(std::move(inner)) { - cached_sk_filter_ = SkComposeImageFilter::Make(GetSkFilter(outer_.get()), - GetSkFilter(inner_.get())); + cached_sk_filter_ = SkImageFilters::Compose(GetSkFilter(outer_.get()), + GetSkFilter(inner_.get())); } ComposePaintFilter::~ComposePaintFilter() = default; @@ -435,7 +425,7 @@ AlphaThresholdPaintFilter::AlphaThresholdPaintFilter(const SkRegion& region, inner_min_(inner_min), outer_max_(outer_max), input_(std::move(input)) { - cached_sk_filter_ = SkAlphaThresholdFilter::Make( + cached_sk_filter_ = SkImageFilters::AlphaThreshold( region_, inner_min_, outer_max_, GetSkFilter(input_.get()), crop_rect); } @@ -477,8 +467,8 @@ XfermodePaintFilter::XfermodePaintFilter(SkBlendMode blend_mode, background_(std::move(background)), foreground_(std::move(foreground)) { cached_sk_filter_ = - SkXfermodeImageFilter::Make(blend_mode_, GetSkFilter(background_.get()), - GetSkFilter(foreground_.get()), crop_rect); + SkImageFilters::Blend(blend_mode_, GetSkFilter(background_.get()), + GetSkFilter(foreground_.get()), crop_rect); } XfermodePaintFilter::~XfermodePaintFilter() = default; @@ -523,7 +513,7 @@ ArithmeticPaintFilter::ArithmeticPaintFilter(float k1, enforce_pm_color_(enforce_pm_color), background_(std::move(background)), foreground_(std::move(foreground)) { - cached_sk_filter_ = SkArithmeticImageFilter::Make( + cached_sk_filter_ = SkImageFilters::Arithmetic( k1_, k2_, k3_, k4_, enforce_pm_color_, GetSkFilter(background_.get()), GetSkFilter(foreground_.get()), crop_rect); } @@ -564,7 +554,7 @@ MatrixConvolutionPaintFilter::MatrixConvolutionPaintFilter( SkScalar gain, SkScalar bias, const SkIPoint& kernel_offset, - TileMode tile_mode, + SkTileMode tile_mode, bool convolve_alpha, sk_sp<PaintFilter> input, const CropRect* crop_rect) @@ -582,7 +572,7 @@ MatrixConvolutionPaintFilter::MatrixConvolutionPaintFilter( for (size_t i = 0; i < len; ++i) kernel_->push_back(kernel[i]); - cached_sk_filter_ = SkMatrixConvolutionImageFilter::Make( + cached_sk_filter_ = SkImageFilters::MatrixConvolution( kernel_size_, kernel, gain_, bias_, kernel_offset_, tile_mode_, convolve_alpha_, GetSkFilter(input_.get()), crop_rect); } @@ -619,8 +609,8 @@ bool MatrixConvolutionPaintFilter::operator==( } DisplacementMapEffectPaintFilter::DisplacementMapEffectPaintFilter( - ChannelSelectorType channel_x, - ChannelSelectorType channel_y, + SkColorChannel channel_x, + SkColorChannel channel_y, SkScalar scale, sk_sp<PaintFilter> displacement, sk_sp<PaintFilter> color, @@ -634,7 +624,7 @@ DisplacementMapEffectPaintFilter::DisplacementMapEffectPaintFilter( scale_(scale), displacement_(std::move(displacement)), color_(std::move(color)) { - cached_sk_filter_ = SkDisplacementMapEffect::Make( + cached_sk_filter_ = SkImageFilters::DisplacementMap( channel_x_, channel_y_, scale_, GetSkFilter(displacement_.get()), GetSkFilter(color_.get()), crop_rect); } @@ -643,8 +633,8 @@ DisplacementMapEffectPaintFilter::~DisplacementMapEffectPaintFilter() = default; size_t DisplacementMapEffectPaintFilter::SerializedSize() const { base::CheckedNumeric<size_t> total_size = BaseSerializedSize() + - sizeof(uint32_t) + - sizeof(uint32_t) + sizeof(scale_); + sizeof(channel_x_) + + sizeof(channel_y_) + sizeof(scale_); total_size += GetFilterSize(displacement_.get()); total_size += GetFilterSize(color_.get()); return total_size.ValueOrDefault(0u); @@ -674,8 +664,10 @@ ImagePaintFilter::ImagePaintFilter(PaintImage image, src_rect_(src_rect), dst_rect_(dst_rect), filter_quality_(filter_quality) { - cached_sk_filter_ = SkImageSource::Make(image_.GetSkImage(), src_rect_, - dst_rect_, filter_quality_); + SkSamplingOptions sampling(filter_quality, + SkSamplingOptions::kMedium_asMipmapLinear); + cached_sk_filter_ = SkImageFilters::Image(image_.GetSkImage(), src_rect_, + dst_rect_, sampling); } ImagePaintFilter::~ImagePaintFilter() = default; @@ -726,7 +718,7 @@ RecordPaintFilter::RecordPaintFilter(sk_sp<PaintRecord> record, : PaintFilter(kType, nullptr, record->HasDiscardableImages()), record_(std::move(record)), record_bounds_(record_bounds) { - cached_sk_filter_ = SkPictureImageFilter::Make( + cached_sk_filter_ = SkImageFilters::Picture( ToSkPicture(record_, record_bounds_, image_provider)); } @@ -770,7 +762,7 @@ MergePaintFilter::MergePaintFilter(const sk_sp<PaintFilter>* const filters, sk_filters.push_back(GetSkFilter(inputs_->back().get())); } - cached_sk_filter_ = SkMergeImageFilter::Make( + cached_sk_filter_ = SkImageFilters::Merge( static_cast<sk_sp<SkImageFilter>*>(sk_filters.data()), count, crop_rect); } @@ -813,11 +805,11 @@ MorphologyPaintFilter::MorphologyPaintFilter(MorphType morph_type, input_(std::move(input)) { switch (morph_type_) { case MorphType::kDilate: - cached_sk_filter_ = SkDilateImageFilter::Make( + cached_sk_filter_ = SkImageFilters::Dilate( radius_x_, radius_y_, GetSkFilter(input_.get()), crop_rect); break; case MorphType::kErode: - cached_sk_filter_ = SkErodeImageFilter::Make( + cached_sk_filter_ = SkImageFilters::Erode( radius_x_, radius_y_, GetSkFilter(input_.get()), crop_rect); break; } @@ -856,7 +848,7 @@ OffsetPaintFilter::OffsetPaintFilter(SkScalar dx, dy_(dy), input_(std::move(input)) { cached_sk_filter_ = - SkOffsetImageFilter::Make(dx_, dy_, GetSkFilter(input_.get()), crop_rect); + SkImageFilters::Offset(dx_, dy_, GetSkFilter(input_.get()), crop_rect); } OffsetPaintFilter::~OffsetPaintFilter() = default; @@ -888,7 +880,7 @@ TilePaintFilter::TilePaintFilter(const SkRect& src, dst_(dst), input_(std::move(input)) { cached_sk_filter_ = - SkTileImageFilter::Make(src_, dst_, GetSkFilter(input_.get())); + SkImageFilters::Tile(src_, dst_, GetSkFilter(input_.get())); } TilePaintFilter::~TilePaintFilter() = default; @@ -940,9 +932,7 @@ TurbulencePaintFilter::TurbulencePaintFilter(TurbulenceType turbulence_type, break; } - SkPaint paint; - paint.setShader(std::move(shader)); - cached_sk_filter_ = SkPaintImageFilter::Make(paint, crop_rect); + cached_sk_filter_ = SkImageFilters::Shader(std::move(shader), crop_rect); } TurbulencePaintFilter::~TurbulencePaintFilter() = default; @@ -984,9 +974,37 @@ PaintFlagsPaintFilter::PaintFlagsPaintFilter(PaintFlags flags, if (image_provider) { raster_flags_.emplace(&flags_, image_provider, SkMatrix::I(), 0, 255u); } - cached_sk_filter_ = SkPaintImageFilter::Make( - raster_flags_ ? raster_flags_->flags()->ToSkPaint() : flags_.ToSkPaint(), - crop_rect); + + const SkPaint& paint = + raster_flags_ ? raster_flags_->flags()->ToSkPaint() : flags_.ToSkPaint(); + // The paint flags should only be a color, shader, and filter quality, + // just DCHECK to make sure caller expectations are not valid. + DCHECK(paint.getBlendMode() == SkBlendMode::kSrcOver); + DCHECK(paint.getStyle() == SkPaint::kFill_Style); + DCHECK(!paint.getPathEffect()); + DCHECK(!paint.getMaskFilter()); + DCHECK(!paint.getImageFilter()); + DCHECK(!paint.getColorFilter()); + + sk_sp<SkShader> shader = paint.refShader(); + if (shader) { + // Combine paint's alpha if the color isn't opaque (the constant RGB is + // overridden by the shader's per-pixel color). + if (paint.getAlpha() < 255) { + // The blend effectively produces (shader * paint alpha). + shader = SkShaders::Blend(SkBlendMode::kDstIn, std::move(shader), + SkShaders::Color(paint.getColor())); + } + } else { + shader = SkShaders::Color(paint.getColor()); + } + + // TODO(michaelludwig): Remove SkFilterQuality arg once all image shaders are + // created with explicit filter settings + using Dither = SkImageFilters::Dither; + cached_sk_filter_ = SkImageFilters::Shader( + std::move(shader), paint.isDither() ? Dither::kYes : Dither::kNo, + paint.getFilterQuality(), crop_rect); } PaintFlagsPaintFilter::~PaintFlagsPaintFilter() = default; @@ -1015,7 +1033,7 @@ MatrixPaintFilter::MatrixPaintFilter(const SkMatrix& matrix, matrix_(matrix), filter_quality_(filter_quality), input_(std::move(input)) { - cached_sk_filter_ = SkImageFilter::MakeMatrixFilter( + cached_sk_filter_ = SkImageFilters::MatrixTransform( matrix_, filter_quality_, GetSkFilter(input_.get())); } @@ -1059,12 +1077,12 @@ LightingDistantPaintFilter::LightingDistantPaintFilter( input_(std::move(input)) { switch (lighting_type_) { case LightingType::kDiffuse: - cached_sk_filter_ = SkLightingImageFilter::MakeDistantLitDiffuse( + cached_sk_filter_ = SkImageFilters::DistantLitDiffuse( direction_, light_color_, surface_scale_, kconstant_, GetSkFilter(input_.get()), crop_rect); break; case LightingType::kSpecular: - cached_sk_filter_ = SkLightingImageFilter::MakeDistantLitSpecular( + cached_sk_filter_ = SkImageFilters::DistantLitSpecular( direction_, light_color_, surface_scale_, kconstant_, shininess_, GetSkFilter(input_.get()), crop_rect); break; @@ -1118,12 +1136,12 @@ LightingPointPaintFilter::LightingPointPaintFilter(LightingType lighting_type, input_(std::move(input)) { switch (lighting_type_) { case LightingType::kDiffuse: - cached_sk_filter_ = SkLightingImageFilter::MakePointLitDiffuse( + cached_sk_filter_ = SkImageFilters::PointLitDiffuse( location_, light_color_, surface_scale_, kconstant_, GetSkFilter(input_.get()), crop_rect); break; case LightingType::kSpecular: - cached_sk_filter_ = SkLightingImageFilter::MakePointLitSpecular( + cached_sk_filter_ = SkImageFilters::PointLitSpecular( location_, light_color_, surface_scale_, kconstant_, shininess_, GetSkFilter(input_.get()), crop_rect); break; @@ -1183,12 +1201,12 @@ LightingSpotPaintFilter::LightingSpotPaintFilter(LightingType lighting_type, input_(std::move(input)) { switch (lighting_type_) { case LightingType::kDiffuse: - cached_sk_filter_ = SkLightingImageFilter::MakeSpotLitDiffuse( + cached_sk_filter_ = SkImageFilters::SpotLitDiffuse( location_, target_, specular_exponent_, cutoff_angle_, light_color_, surface_scale_, kconstant_, GetSkFilter(input_.get()), crop_rect); break; case LightingType::kSpecular: - cached_sk_filter_ = SkLightingImageFilter::MakeSpotLitSpecular( + cached_sk_filter_ = SkImageFilters::SpotLitSpecular( location_, target_, specular_exponent_, cutoff_angle_, light_color_, surface_scale_, kconstant_, shininess_, GetSkFilter(input_.get()), crop_rect); diff --git a/chromium/cc/paint/paint_filter.h b/chromium/cc/paint/paint_filter.h index 556ab9a91e8..e4e5adcdbfb 100644 --- a/chromium/cc/paint/paint_filter.h +++ b/chromium/cc/paint/paint_filter.h @@ -17,10 +17,6 @@ #include "third_party/skia/include/core/SkImageFilter.h" #include "third_party/skia/include/core/SkPoint3.h" #include "third_party/skia/include/core/SkRegion.h" -#include "third_party/skia/include/effects/SkBlurImageFilter.h" -#include "third_party/skia/include/effects/SkDisplacementMapEffect.h" -#include "third_party/skia/include/effects/SkDropShadowImageFilter.h" -#include "third_party/skia/include/effects/SkMatrixConvolutionImageFilter.h" namespace viz { class GLRenderer; @@ -33,7 +29,7 @@ class ImageProvider; class CC_PAINT_EXPORT PaintFilter : public SkRefCnt { public: - enum class Type : uint32_t { + enum class Type { // For serialization purposes, we reserve one enum to indicate that there // was no PaintFilter, ie the filter is "null". kNullFilter, @@ -60,17 +56,17 @@ class CC_PAINT_EXPORT PaintFilter : public SkRefCnt { kLightingPoint, kLightingSpot, // Update the following if kLightingSpot is not the max anymore. - kMaxFilterType = kLightingSpot + kMaxValue = kLightingSpot }; - enum class LightingType : uint32_t { + enum class LightingType { kDiffuse, kSpecular, // Update the following if kSpecular is not the max anymore. - kMaxLightingType = kSpecular + kMaxValue = kSpecular }; using MapDirection = SkImageFilter::MapDirection; - using CropRect = SkImageFilter::CropRect; + using CropRect = SkRect; PaintFilter(const PaintFilter&) = delete; ~PaintFilter() override; @@ -186,11 +182,10 @@ class CC_PAINT_EXPORT ColorFilterPaintFilter final : public PaintFilter { class CC_PAINT_EXPORT BlurPaintFilter final : public PaintFilter { public: - using TileMode = SkBlurImageFilter::TileMode; static constexpr Type kType = Type::kBlur; BlurPaintFilter(SkScalar sigma_x, SkScalar sigma_y, - TileMode tile_mode, + SkTileMode tile_mode, sk_sp<PaintFilter> input, const CropRect* crop_rect = nullptr); ~BlurPaintFilter() override; @@ -199,7 +194,7 @@ class CC_PAINT_EXPORT BlurPaintFilter final : public PaintFilter { SkScalar sigma_x() const { return sigma_x_; } SkScalar sigma_y() const { return sigma_y_; } - TileMode tile_mode() const { return tile_mode_; } + SkTileMode tile_mode() const { return tile_mode_; } size_t SerializedSize() const override; bool operator==(const BlurPaintFilter& other) const; @@ -211,13 +206,17 @@ class CC_PAINT_EXPORT BlurPaintFilter final : public PaintFilter { private: SkScalar sigma_x_; SkScalar sigma_y_; - TileMode tile_mode_; + SkTileMode tile_mode_; sk_sp<PaintFilter> input_; }; class CC_PAINT_EXPORT DropShadowPaintFilter final : public PaintFilter { public: - using ShadowMode = SkDropShadowImageFilter::ShadowMode; + enum class ShadowMode { + kDrawShadowAndForeground, + kDrawShadowOnly, + kMaxValue = kDrawShadowOnly + }; static constexpr Type kType = Type::kDropShadow; DropShadowPaintFilter(SkScalar dx, SkScalar dy, @@ -396,14 +395,13 @@ class CC_PAINT_EXPORT ArithmeticPaintFilter final : public PaintFilter { class CC_PAINT_EXPORT MatrixConvolutionPaintFilter final : public PaintFilter { public: - using TileMode = SkMatrixConvolutionImageFilter::TileMode; static constexpr Type kType = Type::kMatrixConvolution; MatrixConvolutionPaintFilter(const SkISize& kernel_size, const SkScalar* kernel, SkScalar gain, SkScalar bias, const SkIPoint& kernel_offset, - TileMode tile_mode, + SkTileMode tile_mode, bool convolve_alpha, sk_sp<PaintFilter> input, const CropRect* crop_rect = nullptr); @@ -414,7 +412,7 @@ class CC_PAINT_EXPORT MatrixConvolutionPaintFilter final : public PaintFilter { SkScalar gain() const { return gain_; } SkScalar bias() const { return bias_; } SkIPoint kernel_offset() const { return kernel_offset_; } - TileMode tile_mode() const { return tile_mode_; } + SkTileMode tile_mode() const { return tile_mode_; } bool convolve_alpha() const { return convolve_alpha_; } const sk_sp<PaintFilter>& input() const { return input_; } @@ -431,7 +429,7 @@ class CC_PAINT_EXPORT MatrixConvolutionPaintFilter final : public PaintFilter { SkScalar gain_; SkScalar bias_; SkIPoint kernel_offset_; - TileMode tile_mode_; + SkTileMode tile_mode_; bool convolve_alpha_; sk_sp<PaintFilter> input_; }; @@ -439,18 +437,17 @@ class CC_PAINT_EXPORT MatrixConvolutionPaintFilter final : public PaintFilter { class CC_PAINT_EXPORT DisplacementMapEffectPaintFilter final : public PaintFilter { public: - using ChannelSelectorType = SkDisplacementMapEffect::ChannelSelectorType; static constexpr Type kType = Type::kDisplacementMapEffect; - DisplacementMapEffectPaintFilter(ChannelSelectorType channel_x, - ChannelSelectorType channel_y, + DisplacementMapEffectPaintFilter(SkColorChannel channel_x, + SkColorChannel channel_y, SkScalar scale, sk_sp<PaintFilter> displacement, sk_sp<PaintFilter> color, const CropRect* crop_rect = nullptr); ~DisplacementMapEffectPaintFilter() override; - ChannelSelectorType channel_x() const { return channel_x_; } - ChannelSelectorType channel_y() const { return channel_y_; } + SkColorChannel channel_x() const { return channel_x_; } + SkColorChannel channel_y() const { return channel_y_; } SkScalar scale() const { return scale_; } const sk_sp<PaintFilter>& displacement() const { return displacement_; } const sk_sp<PaintFilter>& color() const { return color_; } @@ -463,8 +460,8 @@ class CC_PAINT_EXPORT DisplacementMapEffectPaintFilter final ImageProvider* image_provider) const override; private: - ChannelSelectorType channel_x_; - ChannelSelectorType channel_y_; + SkColorChannel channel_x_; + SkColorChannel channel_y_; SkScalar scale_; sk_sp<PaintFilter> displacement_; sk_sp<PaintFilter> color_; @@ -554,7 +551,7 @@ class CC_PAINT_EXPORT MergePaintFilter final : public PaintFilter { class CC_PAINT_EXPORT MorphologyPaintFilter final : public PaintFilter { public: - enum class MorphType : uint32_t { kDilate, kErode, kMaxMorphType = kErode }; + enum class MorphType { kDilate, kErode, kMaxValue = kErode }; static constexpr Type kType = Type::kMorphology; MorphologyPaintFilter(MorphType morph_type, float radius_x, @@ -636,10 +633,10 @@ class CC_PAINT_EXPORT TilePaintFilter final : public PaintFilter { class CC_PAINT_EXPORT TurbulencePaintFilter final : public PaintFilter { public: static constexpr Type kType = Type::kTurbulence; - enum class TurbulenceType : uint32_t { + enum class TurbulenceType { kTurbulence, kFractalNoise, - kMaxTurbulenceType = kFractalNoise + kMaxValue = kFractalNoise }; TurbulencePaintFilter(TurbulenceType turbulence_type, SkScalar base_frequency_x, diff --git a/chromium/cc/paint/paint_filter_unittest.cc b/chromium/cc/paint/paint_filter_unittest.cc index 9e562561b0d..c3461250003 100644 --- a/chromium/cc/paint/paint_filter_unittest.cc +++ b/chromium/cc/paint/paint_filter_unittest.cc @@ -23,7 +23,7 @@ class MockImageProvider : public ImageProvider { return ScopedResult( DecodedDrawImage(CreateBitmapImage(gfx::Size(10, 10)).GetSwSkImage(), nullptr, SkSize::MakeEmpty(), SkSize::Make(1.0f, 1.0f), - draw_image.filter_quality())); + draw_image.filter_quality(), true)); } int image_count_ = 0; }; @@ -40,11 +40,11 @@ sk_sp<PaintFilter> CreateTestFilter(PaintFilter::Type filter_type, image, SkRect::MakeWH(100.f, 100.f), SkRect::MakeWH(100.f, 100.f), kNone_SkFilterQuality); auto record = sk_make_sp<PaintOpBuffer>(); - record->push<DrawImageOp>(image, 0.f, 0.f, nullptr); + record->push<DrawImageOp>(image, 0.f, 0.f); auto record_filter = sk_make_sp<RecordPaintFilter>(record, SkRect::MakeWH(100.f, 100.f)); - SkImageFilter::CropRect crop_rect(SkRect::MakeWH(100.f, 100.f)); + PaintFilter::CropRect crop_rect(SkRect::MakeWH(100.f, 100.f)); switch (filter_type) { case PaintFilter::Type::kNullFilter: @@ -54,13 +54,12 @@ sk_sp<PaintFilter> CreateTestFilter(PaintFilter::Type filter_type, return sk_make_sp<ColorFilterPaintFilter>(SkLumaColorFilter::Make(), image_filter, &crop_rect); case PaintFilter::Type::kBlur: - return sk_make_sp<BlurPaintFilter>(0.1f, 0.2f, - SkBlurImageFilter::kClamp_TileMode, + return sk_make_sp<BlurPaintFilter>(0.1f, 0.2f, SkTileMode::kClamp, record_filter, &crop_rect); case PaintFilter::Type::kDropShadow: return sk_make_sp<DropShadowPaintFilter>( 0.1, 0.2f, 0.3f, 0.4f, SK_ColorWHITE, - SkDropShadowImageFilter::kDrawShadowOnly_ShadowMode, image_filter, + DropShadowPaintFilter::ShadowMode::kDrawShadowOnly, image_filter, &crop_rect); case PaintFilter::Type::kMagnifier: return sk_make_sp<MagnifierPaintFilter>(SkRect::MakeWH(100.f, 100.f), @@ -82,14 +81,12 @@ sk_sp<PaintFilter> CreateTestFilter(PaintFilter::Type filter_type, SkScalar scalars[9] = {1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f, 9.f}; return sk_make_sp<MatrixConvolutionPaintFilter>( SkISize::Make(3, 3), scalars, 0.1f, 0.2f, SkIPoint::Make(2, 2), - SkMatrixConvolutionImageFilter::TileMode::kRepeat_TileMode, false, - image_filter, &crop_rect); + SkTileMode::kRepeat, false, image_filter, &crop_rect); } case PaintFilter::Type::kDisplacementMapEffect: return sk_make_sp<DisplacementMapEffectPaintFilter>( - SkDisplacementMapEffect::ChannelSelectorType::kR_ChannelSelectorType, - SkDisplacementMapEffect::ChannelSelectorType::kR_ChannelSelectorType, - 0.1f, image_filter, record_filter, &crop_rect); + SkColorChannel::kR, SkColorChannel::kR, 0.1f, image_filter, + record_filter, &crop_rect); case PaintFilter::Type::kImage: return image_filter; case PaintFilter::Type::kPaintRecord: @@ -153,7 +150,7 @@ INSTANTIATE_TEST_SUITE_P( P, PaintFilterTest, ::testing::Range(static_cast<uint8_t>(PaintFilter::Type::kColorFilter), - static_cast<uint8_t>(PaintFilter::Type::kMaxFilterType))); + static_cast<uint8_t>(PaintFilter::Type::kMaxValue))); TEST_P(PaintFilterTest, HasDiscardableImagesYes) { // TurbulencePaintFilter can not embed images. diff --git a/chromium/cc/paint/paint_image.cc b/chromium/cc/paint/paint_image.cc index 70853e09ca3..f57728dd4ef 100644 --- a/chromium/cc/paint/paint_image.cc +++ b/chromium/cc/paint/paint_image.cc @@ -298,11 +298,6 @@ void PaintImage::FlushPendingSkiaOps() { texture_backing_->FlushPendingSkiaOps(); } -bool PaintImage::HasExclusiveTextureAccess() const { - DCHECK(IsTextureBacked()); - return texture_backing_->unique(); -} - int PaintImage::width() const { return paint_worklet_input_ ? static_cast<int>(paint_worklet_input_->GetSize().width()) diff --git a/chromium/cc/paint/paint_image.h b/chromium/cc/paint/paint_image.h index 237ab4d8576..ab1a18c7f2d 100644 --- a/chromium/cc/paint/paint_image.h +++ b/chromium/cc/paint/paint_image.h @@ -21,6 +21,10 @@ #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/size.h" +namespace blink { +class VideoFrame; +} + namespace cc { class PaintImageGenerator; @@ -235,11 +239,11 @@ class CC_PAINT_EXPORT PaintImage { int src_x, int src_y) const; - SkImageInfo GetSkImageInfo() const; + // Returned mailbox must not outlive this PaintImage. + gpu::Mailbox GetMailbox() const; Id stable_id() const { return id_; } - const sk_sp<SkImage>& GetSkImage() const; - gpu::Mailbox GetMailbox() const; + SkImageInfo GetSkImageInfo() const; AnimationType animation_type() const { return animation_type_; } CompletionState completion_state() const { return completion_state_; } bool is_multipart() const { return is_multipart_; } @@ -262,7 +266,6 @@ class CC_PAINT_EXPORT PaintImage { // Skia internally buffers commands and flushes them as necessary but there // are some cases where we need to force a flush. void FlushPendingSkiaOps(); - bool HasExclusiveTextureAccess() const; int width() const; int height() const; SkColorSpace* color_space() const { @@ -324,6 +327,12 @@ class CC_PAINT_EXPORT PaintImage { friend class DrawImageRectOp; friend class DrawImageOp; + // TODO(crbug.com/1031051): Remove these once GetSkImage() + // is fully removed. + friend class ImagePaintFilter; + friend class PaintShader; + friend class blink::VideoFrame; + bool DecodeFromGenerator(void* memory, SkImageInfo* info, sk_sp<SkColorSpace> color_space, @@ -339,6 +348,10 @@ class CC_PAINT_EXPORT PaintImage { // Only supported in non-OOPR contexts by friend callers. sk_sp<SkImage> GetAcceleratedSkImage() const; + // GetSkImage() is being deprecated, see crbug.com/1031051. + // Prefer using GetSwSkImage() or GetSkImageInfo(). + const sk_sp<SkImage>& GetSkImage() const; + sk_sp<SkImage> sk_image_; sk_sp<PaintRecord> paint_record_; gfx::Rect paint_record_rect_; diff --git a/chromium/cc/paint/paint_image_builder.cc b/chromium/cc/paint/paint_image_builder.cc index d8b3199f935..80aa99af7b1 100644 --- a/chromium/cc/paint/paint_image_builder.cc +++ b/chromium/cc/paint/paint_image_builder.cc @@ -33,6 +33,7 @@ PaintImageBuilder::PaintImageBuilder(PaintImage image, bool clear_contents) paint_image_.paint_record_rect_ = gfx::Rect(); paint_image_.paint_image_generator_ = nullptr; paint_image_.cached_sk_image_ = nullptr; + paint_image_.texture_backing_ = nullptr; } } PaintImageBuilder::PaintImageBuilder(PaintImageBuilder&& other) = default; diff --git a/chromium/cc/paint/paint_image_unittest.cc b/chromium/cc/paint/paint_image_unittest.cc index 6de36fe986b..d328030961b 100644 --- a/chromium/cc/paint/paint_image_unittest.cc +++ b/chromium/cc/paint/paint_image_unittest.cc @@ -74,7 +74,8 @@ TEST(PaintImageTest, GetSkImageForFrameNotGeneratorBacked) { TEST(PaintImageTest, DecodeToYuv420NoAlpha) { const SkISize full_size = SkISize::Make(10, 10); - SkYUVAInfo yuva_info(full_size, SkYUVAInfo::PlanarConfig::kY_U_V_420, + SkYUVAInfo yuva_info(full_size, SkYUVAInfo::PlaneConfig::kY_U_V, + SkYUVAInfo::Subsampling::k420, kJPEG_Full_SkYUVColorSpace); SkYUVAPixmapInfo yuva_pixmap_info(yuva_info, SkYUVAPixmapInfo::DataType::kUnorm8, diff --git a/chromium/cc/paint/paint_op_buffer.cc b/chromium/cc/paint/paint_op_buffer.cc index 1fb6e17f432..c9f9ac8bb80 100644 --- a/chromium/cc/paint/paint_op_buffer.cc +++ b/chromium/cc/paint/paint_op_buffer.cc @@ -27,15 +27,28 @@ namespace cc { namespace { +// In a future CL, convert DrawImage to explicitly take sampling instead of +// quality +SkFilterQuality sampling_to_quality(const SkSamplingOptions& sampling) { + if (sampling.useCubic) { + return kHigh_SkFilterQuality; + } + if (sampling.mipmap != SkMipmapMode::kNone) { + return kMedium_SkFilterQuality; + } + return sampling.filter == SkFilterMode::kLinear ? kLow_SkFilterQuality + : kNone_SkFilterQuality; +} + DrawImage CreateDrawImage(const PaintImage& image, const PaintFlags* flags, + const SkSamplingOptions& sampling, const SkMatrix& matrix) { if (!image) return DrawImage(); return DrawImage(image, flags->useDarkModeForImage(), SkIRect::MakeWH(image.width(), image.height()), - flags ? flags->getFilterQuality() : kLow_SkFilterQuality, - matrix); + sampling_to_quality(sampling), matrix); } bool IsScaleAdjustmentIdentity(const SkSize& scale_adjustment) { @@ -312,10 +325,10 @@ std::ostream& operator<<(std::ostream& os, PaintOpType type) { } PlaybackParams::PlaybackParams(ImageProvider* image_provider) - : PlaybackParams(image_provider, SkMatrix::I()) {} + : PlaybackParams(image_provider, SkM44()) {} PlaybackParams::PlaybackParams(ImageProvider* image_provider, - const SkMatrix& original_ctm, + const SkM44& original_ctm, CustomDataRasterCallback custom_callback, DidDrawOpCallback did_draw_op_callback) : image_provider(image_provider), @@ -339,7 +352,7 @@ PaintOp::SerializeOptions::SerializeOptions( bool can_use_lcd_text, bool context_supports_distance_field_text, int max_texture_size, - const SkMatrix& original_ctm) + const SkM44& original_ctm) : image_provider(image_provider), transfer_cache(transfer_cache), paint_cache(paint_cache), @@ -422,9 +435,9 @@ size_t ClipRRectOp::Serialize(const PaintOp* base_op, } size_t ConcatOp::Serialize(const PaintOp* base_op, - void* memory, - size_t size, - const SerializeOptions& options) { + void* memory, + size_t size, + const SerializeOptions& options) { auto* op = static_cast<const ConcatOp*>(base_op); PaintOpWriter helper(memory, size, options); helper.Write(op->matrix); @@ -479,7 +492,7 @@ size_t DrawImageOp::Serialize(const PaintOp* base_op, helper.Write(*serialized_flags); SkSize scale_adjustment = SkSize::Make(1.f, 1.f); - helper.Write(CreateDrawImage(op->image, serialized_flags, + helper.Write(CreateDrawImage(op->image, serialized_flags, op->sampling, options.canvas->getTotalMatrix()), &scale_adjustment); helper.AlignMemory(alignof(SkScalar)); @@ -488,6 +501,7 @@ size_t DrawImageOp::Serialize(const PaintOp* base_op, helper.Write(op->left); helper.Write(op->top); + helper.Write(op->sampling); return helper.size(); } @@ -503,20 +517,21 @@ size_t DrawImageRectOp::Serialize(const PaintOp* base_op, helper.Write(*serialized_flags); // This adjustment mirrors DiscardableImageMap::GatherDiscardableImage logic. - SkMatrix matrix = options.canvas->getTotalMatrix(); - matrix.postConcat( - SkMatrix::MakeRectToRect(op->src, op->dst, SkMatrix::kFill_ScaleToFit)); + SkMatrix matrix = + SkMatrix::RectToRect(op->src, op->dst) * options.canvas->getTotalMatrix(); // Note that we don't request subsets here since the GpuImageCache has no // optimizations for using subsets. SkSize scale_adjustment = SkSize::Make(1.f, 1.f); - helper.Write(CreateDrawImage(op->image, serialized_flags, matrix), - &scale_adjustment); + helper.Write( + CreateDrawImage(op->image, serialized_flags, op->sampling, matrix), + &scale_adjustment); helper.AlignMemory(alignof(SkScalar)); helper.Write(scale_adjustment.width()); helper.Write(scale_adjustment.height()); helper.Write(op->src); helper.Write(op->dst); + helper.Write(op->sampling); helper.Write(op->constraint); return helper.size(); } @@ -578,6 +593,7 @@ size_t DrawPathOp::Serialize(const PaintOp* base_op, serialized_flags = &op->flags; helper.Write(*serialized_flags); helper.Write(op->path); + helper.Write(op->sk_path_fill_type); return helper.size(); } @@ -726,19 +742,12 @@ size_t ScaleOp::Serialize(const PaintOp* base_op, } size_t SetMatrixOp::Serialize(const PaintOp* base_op, - void* memory, - size_t size, - const SerializeOptions& options) { + void* memory, + size_t size, + const SerializeOptions& options) { auto* op = static_cast<const SetMatrixOp*>(base_op); PaintOpWriter helper(memory, size, options); - - if (options.original_ctm.isIdentity()) { - helper.Write(op->matrix); - } else { - SkMatrix transformed = op->matrix; - transformed.postConcat(options.original_ctm); - helper.Write(transformed); - } + helper.Write(options.original_ctm * op->matrix); return helper.size(); } @@ -869,7 +878,6 @@ PaintOp* ConcatOp::Deserialize(const volatile void* input, } UpdateTypeAndSkip(op); - PaintOpReader::FixupMatrixPostSerialization(&op->matrix); return op; } @@ -950,6 +958,7 @@ PaintOp* DrawImageOp::Deserialize(const volatile void* input, helper.Read(&op->left); helper.Read(&op->top); + helper.Read(&op->sampling); if (!helper.valid() || !op->IsValid()) { op->~DrawImageOp(); return nullptr; @@ -976,6 +985,7 @@ PaintOp* DrawImageRectOp::Deserialize(const volatile void* input, helper.Read(&op->src); helper.Read(&op->dst); + helper.Read(&op->sampling); helper.Read(&op->constraint); if (!helper.valid() || !op->IsValid()) { op->~DrawImageRectOp(); @@ -1057,6 +1067,8 @@ PaintOp* DrawPathOp::Deserialize(const volatile void* input, PaintOpReader helper(input, input_size, options); helper.Read(&op->flags); helper.Read(&op->path); + helper.Read(&op->sk_path_fill_type); + op->path.setFillType(static_cast<SkPathFillType>(op->sk_path_fill_type)); if (!helper.valid() || !op->IsValid()) { op->~DrawPathOp(); return nullptr; @@ -1314,7 +1326,6 @@ PaintOp* SetMatrixOp::Deserialize(const volatile void* input, } UpdateTypeAndSkip(op); - PaintOpReader::FixupMatrixPostSerialization(&op->matrix); return op; } @@ -1394,8 +1405,8 @@ void ClipRRectOp::Raster(const ClipRRectOp* op, } void ConcatOp::Raster(const ConcatOp* op, - SkCanvas* canvas, - const PlaybackParams& params) { + SkCanvas* canvas, + const PlaybackParams& params) { canvas->concat(op->matrix); } @@ -1435,18 +1446,22 @@ void DrawImageOp::RasterWithFlags(const DrawImageOp* op, canvas->scale(1.f / op->scale_adjustment.width(), 1.f / op->scale_adjustment.height()); } - auto sk_image = op->image.IsTextureBacked() - ? op->image.GetAcceleratedSkImage() - : op->image.GetSwSkImage(); - canvas->drawImage(sk_image.get(), op->left, op->top, &paint); + sk_sp<SkImage> sk_image; + if (op->image.IsTextureBacked()) { + sk_image = op->image.GetAcceleratedSkImage(); + DCHECK(sk_image || !canvas->recordingContext()); + } + if (!sk_image) + sk_image = op->image.GetSwSkImage(); + + canvas->drawImage(sk_image.get(), op->left, op->top, op->sampling, &paint); return; } // Dark mode is applied only for OOP raster during serialization. DrawImage draw_image( op->image, false, SkIRect::MakeWH(op->image.width(), op->image.height()), - flags ? flags->getFilterQuality() : kNone_SkFilterQuality, - canvas->getTotalMatrix()); + sampling_to_quality(op->sampling), canvas->getTotalMatrix()); auto scoped_result = params.image_provider->GetRasterContent(draw_image); if (!scoped_result) return; @@ -1466,8 +1481,11 @@ void DrawImageOp::RasterWithFlags(const DrawImageOp* op, canvas->scale(1.f / scale_adjustment.width(), 1.f / scale_adjustment.height()); } - paint.setFilterQuality(decoded_image.filter_quality()); - canvas->drawImage(decoded_image.image().get(), op->left, op->top, &paint); + canvas->drawImage( + decoded_image.image().get(), op->left, op->top, + SkSamplingOptions(decoded_image.filter_quality(), + SkSamplingOptions::kMedium_asMipmapLinear), + &paint); } void DrawImageRectOp::RasterWithFlags(const DrawImageRectOp* op, @@ -1482,13 +1500,6 @@ void DrawImageRectOp::RasterWithFlags(const DrawImageRectOp* op, // we should draw nothing. if (!params.image_provider) return; - // TODO(crbug.com/1157152): We shouldn't need this check, QuickRejectDraw - // should have done the job. - const SkRect& clip_rect = SkRect::Make(canvas->getDeviceClipBounds()); - const SkMatrix& ctm = canvas->getTotalMatrix(); - gfx::Rect local_op_rect = PaintOp::ComputePaintRect(op, clip_rect, ctm); - if (local_op_rect.IsEmpty()) - return; ImageProvider::ScopedResult result = params.image_provider->GetRasterContent(DrawImage(op->image)); @@ -1499,8 +1510,7 @@ void DrawImageRectOp::RasterWithFlags(const DrawImageRectOp* op, DCHECK(IsScaleAdjustmentIdentity(op->scale_adjustment)); SkAutoCanvasRestore save_restore(canvas, true); - canvas->concat( - SkMatrix::MakeRectToRect(op->src, op->dst, SkMatrix::kFill_ScaleToFit)); + canvas->concat(SkMatrix::RectToRect(op->src, op->dst)); canvas->clipRect(op->src); canvas->saveLayer(&op->src, &paint); // Compositor thread animations can cause PaintWorklet jobs to be dispatched @@ -1516,26 +1526,28 @@ void DrawImageRectOp::RasterWithFlags(const DrawImageRectOp* op, if (!params.image_provider) { SkRect adjusted_src = AdjustSrcRectForScale(op->src, op->scale_adjustment); flags->DrawToSk(canvas, [op, adjusted_src](SkCanvas* c, const SkPaint& p) { - auto sk_image = op->image.IsTextureBacked() - ? op->image.GetAcceleratedSkImage() - : op->image.GetSwSkImage(); - c->drawImageRect(sk_image.get(), adjusted_src, op->dst, &p, + sk_sp<SkImage> sk_image; + if (op->image.IsTextureBacked()) { + sk_image = op->image.GetAcceleratedSkImage(); + DCHECK(sk_image || !c->recordingContext()); + } + if (!sk_image) + sk_image = op->image.GetSwSkImage(); + c->drawImageRect(sk_image.get(), adjusted_src, op->dst, op->sampling, &p, op->constraint); }); return; } - SkMatrix matrix; - matrix.setRectToRect(op->src, op->dst, SkMatrix::kFill_ScaleToFit); - matrix.postConcat(canvas->getTotalMatrix()); + SkMatrix matrix = + canvas->getTotalMatrix() * SkMatrix::RectToRect(op->src, op->dst); SkIRect int_src_rect; op->src.roundOut(&int_src_rect); // Dark mode is applied only for OOP raster during serialization. - DrawImage draw_image( - op->image, false, int_src_rect, - flags ? flags->getFilterQuality() : kNone_SkFilterQuality, matrix); + DrawImage draw_image(op->image, false, int_src_rect, + sampling_to_quality(op->sampling), matrix); auto scoped_result = params.image_provider->GetRasterContent(draw_image); if (!scoped_result) return; @@ -1553,10 +1565,11 @@ void DrawImageRectOp::RasterWithFlags(const DrawImageRectOp* op, adjusted_src = AdjustSrcRectForScale(adjusted_src, scale_adjustment); flags->DrawToSk(canvas, [op, &decoded_image, adjusted_src](SkCanvas* c, const SkPaint& p) { - SkPaint paint_with_filter_quality(p); - paint_with_filter_quality.setFilterQuality(decoded_image.filter_quality()); - c->drawImageRect(decoded_image.image().get(), adjusted_src, op->dst, - &paint_with_filter_quality, op->constraint); + c->drawImageRect( + decoded_image.image().get(), adjusted_src, op->dst, + SkSamplingOptions(decoded_image.filter_quality(), + SkSamplingOptions::kMedium_asMipmapLinear), + &p, op->constraint); }); } @@ -1698,9 +1711,9 @@ void ScaleOp::Raster(const ScaleOp* op, } void SetMatrixOp::Raster(const SetMatrixOp* op, - SkCanvas* canvas, - const PlaybackParams& params) { - canvas->setMatrix(SkMatrix::Concat(params.original_ctm, op->matrix)); + SkCanvas* canvas, + const PlaybackParams& params) { + canvas->setMatrix(params.original_ctm * op->matrix); } void SetNodeIdOp::Raster(const SetNodeIdOp* op, @@ -1780,6 +1793,18 @@ bool PaintOp::AreSkMatricesEqual(const SkMatrix& left, const SkMatrix& right) { } // static +bool PaintOp::AreSkM44sEqual(const SkM44& left, const SkM44& right) { + for (int r = 0; r < 4; ++r) { + for (int c = 0; c < 4; ++c) { + if (!AreEqualEvenIfNaN(left.rc(r, c), right.rc(r, c))) + return false; + } + } + + return true; +} + +// static bool PaintOp::AreSkFlattenablesEqual(SkFlattenable* left, SkFlattenable* right) { if (!right || !left) @@ -1863,7 +1888,7 @@ bool ConcatOp::AreEqual(const PaintOp* base_left, const PaintOp* base_right) { auto* right = static_cast<const ConcatOp*>(base_right); DCHECK(left->IsValid()); DCHECK(right->IsValid()); - return AreSkMatricesEqual(left->matrix, right->matrix); + return AreSkM44sEqual(left->matrix, right->matrix); } bool CustomDataOp::AreEqual(const PaintOp* base_left, @@ -2131,7 +2156,7 @@ bool SetMatrixOp::AreEqual(const PaintOp* base_left, auto* right = static_cast<const SetMatrixOp*>(base_right); DCHECK(left->IsValid()); DCHECK(right->IsValid()); - if (!AreSkMatricesEqual(left->matrix, right->matrix)) + if (!AreSkM44sEqual(left->matrix, right->matrix)) return false; return true; } @@ -2370,6 +2395,15 @@ bool PaintOp::QuickRejectDraw(const PaintOp* op, const SkCanvas* canvas) { SkPaint paint = static_cast<const PaintOpWithFlags*>(op)->flags.ToSkPaint(); if (!paint.canComputeFastBounds()) return false; + // canvas->quickReject tried to be very fast, and sometimes give a false + // but conservative result. That's why we need the additional check for + // |local_op_rect| because it could quickReject could return false even if + // |local_op_rect| is empty. + const SkRect& clip_rect = SkRect::Make(canvas->getDeviceClipBounds()); + const SkMatrix& ctm = canvas->getTotalMatrix(); + gfx::Rect local_op_rect = PaintOp::ComputePaintRect(op, clip_rect, ctm); + if (local_op_rect.IsEmpty()) + return true; paint.computeFastBounds(rect, &rect); } @@ -2486,14 +2520,23 @@ AnnotateOp::~AnnotateOp() = default; DrawImageOp::DrawImageOp() : PaintOpWithFlags(kType) {} +DrawImageOp::DrawImageOp(const PaintImage& image, SkScalar left, SkScalar top) + : PaintOpWithFlags(kType, PaintFlags()), + image(image), + left(left), + top(top), + sampling(SkSamplingOptions()) {} + DrawImageOp::DrawImageOp(const PaintImage& image, SkScalar left, SkScalar top, + const SkSamplingOptions& sampling, const PaintFlags* flags) : PaintOpWithFlags(kType, flags ? *flags : PaintFlags()), image(image), left(left), - top(top) {} + top(top), + sampling(sampling) {} bool DrawImageOp::HasDiscardableImages() const { return image && !image.IsTextureBacked(); @@ -2506,12 +2549,25 @@ DrawImageRectOp::DrawImageRectOp() : PaintOpWithFlags(kType) {} DrawImageRectOp::DrawImageRectOp(const PaintImage& image, const SkRect& src, const SkRect& dst, + SkCanvas::SrcRectConstraint constraint) + : PaintOpWithFlags(kType, PaintFlags()), + image(image), + src(src), + dst(dst), + sampling(SkSamplingOptions()), + constraint(constraint) {} + +DrawImageRectOp::DrawImageRectOp(const PaintImage& image, + const SkRect& src, + const SkRect& dst, + const SkSamplingOptions& sampling, const PaintFlags* flags, SkCanvas::SrcRectConstraint constraint) : PaintOpWithFlags(kType, flags ? *flags : PaintFlags()), image(image), src(src), dst(dst), + sampling(sampling), constraint(constraint) {} bool DrawImageRectOp::HasDiscardableImages() const { @@ -2785,7 +2841,7 @@ void PaintOpBuffer::Playback(SkCanvas* canvas, // translate(x, y), then draw a paint record with a SetMatrix(identity), // the translation should be preserved instead of clobbering the top level // transform. This could probably be done more efficiently. - PlaybackParams new_params(params.image_provider, canvas->getTotalMatrix(), + PlaybackParams new_params(params.image_provider, canvas->getLocalToDevice(), params.custom_callback, params.did_draw_op_callback); new_params.save_layer_alpha_should_preserve_lcd_text = diff --git a/chromium/cc/paint/paint_op_buffer.h b/chromium/cc/paint/paint_op_buffer.h index 57b1d8e4440..ee0c79b8477 100644 --- a/chromium/cc/paint/paint_op_buffer.h +++ b/chromium/cc/paint/paint_op_buffer.h @@ -8,8 +8,11 @@ #include <stdint.h> #include <limits> +#include <memory> #include <string> #include <type_traits> +#include <utility> +#include <vector> #include "base/callback.h" #include "base/check_op.h" @@ -44,14 +47,6 @@ class ClientPaintCache; class ImageProvider; class ServicePaintCache; -class CC_PAINT_EXPORT ThreadsafeMatrix : public SkMatrix { - public: - explicit ThreadsafeMatrix(const SkMatrix& matrix) : SkMatrix(matrix) { - (void)getType(); - } - ThreadsafeMatrix() { (void)getType(); } -}; - class CC_PAINT_EXPORT ThreadsafePath : public SkPath { public: explicit ThreadsafePath(const SkPath& path) : SkPath(path) { @@ -121,7 +116,7 @@ struct CC_PAINT_EXPORT PlaybackParams { explicit PlaybackParams(ImageProvider* image_provider); PlaybackParams( ImageProvider* image_provider, - const SkMatrix& original_ctm, + const SkM44& original_ctm, CustomDataRasterCallback custom_callback = CustomDataRasterCallback(), DidDrawOpCallback did_draw_op_callback = DidDrawOpCallback()); ~PlaybackParams(); @@ -130,7 +125,7 @@ struct CC_PAINT_EXPORT PlaybackParams { PlaybackParams& operator=(const PlaybackParams& other); ImageProvider* image_provider; - SkMatrix original_ctm; + SkM44 original_ctm; CustomDataRasterCallback custom_callback; DidDrawOpCallback did_draw_op_callback; base::Optional<bool> save_layer_alpha_should_preserve_lcd_text; @@ -166,7 +161,7 @@ class CC_PAINT_EXPORT PaintOp { bool can_use_lcd_text, bool context_supports_distance_field_text, int max_texture_size, - const SkMatrix& original_ctm); + const SkM44& original_ctm); SerializeOptions(const SerializeOptions&); SerializeOptions& operator=(const SerializeOptions&); ~SerializeOptions(); @@ -181,7 +176,7 @@ class CC_PAINT_EXPORT PaintOp { bool can_use_lcd_text = false; bool context_supports_distance_field_text = true; int max_texture_size = 0; - SkMatrix original_ctm = SkMatrix::I(); + SkM44 original_ctm = SkM44(); // Identity // Optional. // The flags to use when serializing this op. This can be used to override @@ -332,6 +327,7 @@ class CC_PAINT_EXPORT PaintOp { static bool AreSkRectsEqual(const SkRect& left, const SkRect& right); static bool AreSkRRectsEqual(const SkRRect& left, const SkRRect& right); static bool AreSkMatricesEqual(const SkMatrix& left, const SkMatrix& right); + static bool AreSkM44sEqual(const SkM44& left, const SkM44& right); static bool AreSkFlattenablesEqual(SkFlattenable* left, SkFlattenable* right); static constexpr bool kIsDrawOp = false; @@ -455,7 +451,7 @@ class CC_PAINT_EXPORT ClipRRectOp final : public PaintOp { class CC_PAINT_EXPORT ConcatOp final : public PaintOp { public: static constexpr PaintOpType kType = PaintOpType::Concat; - explicit ConcatOp(const SkMatrix& matrix) : PaintOp(kType), matrix(matrix) {} + explicit ConcatOp(const SkM44& matrix) : PaintOp(kType), matrix(matrix) {} static void Raster(const ConcatOp* op, SkCanvas* canvas, const PlaybackParams& params); @@ -463,7 +459,7 @@ class CC_PAINT_EXPORT ConcatOp final : public PaintOp { static bool AreEqual(const PaintOp* left, const PaintOp* right); HAS_SERIALIZATION_FUNCTIONS(); - ThreadsafeMatrix matrix; + SkM44 matrix; private: ConcatOp() : PaintOp(kType) {} @@ -536,9 +532,11 @@ class CC_PAINT_EXPORT DrawImageOp final : public PaintOpWithFlags { public: static constexpr PaintOpType kType = PaintOpType::DrawImage; static constexpr bool kIsDrawOp = true; + DrawImageOp(const PaintImage& image, SkScalar left, SkScalar top); DrawImageOp(const PaintImage& image, SkScalar left, SkScalar top, + const SkSamplingOptions&, const PaintFlags* flags); ~DrawImageOp(); static void RasterWithFlags(const DrawImageOp* op, @@ -557,6 +555,7 @@ class CC_PAINT_EXPORT DrawImageOp final : public PaintOpWithFlags { PaintImage image; SkScalar left; SkScalar top; + SkSamplingOptions sampling; private: DrawImageOp(); @@ -573,6 +572,11 @@ class CC_PAINT_EXPORT DrawImageRectOp final : public PaintOpWithFlags { DrawImageRectOp(const PaintImage& image, const SkRect& src, const SkRect& dst, + SkCanvas::SrcRectConstraint constraint); + DrawImageRectOp(const PaintImage& image, + const SkRect& src, + const SkRect& dst, + const SkSamplingOptions&, const PaintFlags* flags, SkCanvas::SrcRectConstraint constraint); ~DrawImageRectOp(); @@ -592,6 +596,7 @@ class CC_PAINT_EXPORT DrawImageRectOp final : public PaintOpWithFlags { PaintImage image; SkRect src; SkRect dst; + SkSamplingOptions sampling; SkCanvas::SrcRectConstraint constraint; private: @@ -680,7 +685,9 @@ class CC_PAINT_EXPORT DrawPathOp final : public PaintOpWithFlags { static constexpr PaintOpType kType = PaintOpType::DrawPath; static constexpr bool kIsDrawOp = true; DrawPathOp(const SkPath& path, const PaintFlags& flags) - : PaintOpWithFlags(kType, flags), path(path) {} + : PaintOpWithFlags(kType, flags), + path(path), + sk_path_fill_type(static_cast<uint8_t>(path.getFillType())) {} static void RasterWithFlags(const DrawPathOp* op, const PaintFlags* flags, SkCanvas* canvas, @@ -692,6 +699,12 @@ class CC_PAINT_EXPORT DrawPathOp final : public PaintOpWithFlags { ThreadsafePath path; + // Changing the fill type on an SkPath does not change the + // generation id. This can lead to caching issues so we explicitly + // serialize/deserialize this value and set it on the SkPath before handing it + // to Skia. + uint8_t sk_path_fill_type; + private: DrawPathOp() : PaintOpWithFlags(kType) {} }; @@ -935,8 +948,7 @@ class CC_PAINT_EXPORT ScaleOp final : public PaintOp { class CC_PAINT_EXPORT SetMatrixOp final : public PaintOp { public: static constexpr PaintOpType kType = PaintOpType::SetMatrix; - explicit SetMatrixOp(const SkMatrix& matrix) - : PaintOp(kType), matrix(matrix) {} + explicit SetMatrixOp(const SkM44& matrix) : PaintOp(kType), matrix(matrix) {} // This is the only op that needs the original ctm of the SkCanvas // used for raster (since SetMatrix is relative to the recording origin and // shouldn't clobber the SkCanvas raster origin). @@ -950,7 +962,7 @@ class CC_PAINT_EXPORT SetMatrixOp final : public PaintOp { static bool AreEqual(const PaintOp* left, const PaintOp* right); HAS_SERIALIZATION_FUNCTIONS(); - ThreadsafeMatrix matrix; + SkM44 matrix; private: SetMatrixOp() : PaintOp(kType) {} @@ -1290,7 +1302,7 @@ class CC_PAINT_EXPORT PaintOpBuffer : public SkRefCnt { base::Optional<Iterator> iter_; }; - class PlaybackFoldingIterator { + class CC_PAINT_EXPORT PlaybackFoldingIterator { public: PlaybackFoldingIterator(const PaintOpBuffer* buffer, const std::vector<size_t>* offsets); diff --git a/chromium/cc/paint/paint_op_buffer_fuzzer.cc b/chromium/cc/paint/paint_op_buffer_fuzzer.cc index 562f62f093d..152b78b6511 100644 --- a/chromium/cc/paint/paint_op_buffer_fuzzer.cc +++ b/chromium/cc/paint/paint_op_buffer_fuzzer.cc @@ -79,7 +79,7 @@ void Raster(scoped_refptr<viz::TestContextProvider> context_provider, context_provider->GrContext(), SkBudgeted::kYes, image_info); SkCanvas* canvas = surface->getCanvas(); - cc::PlaybackParams params(nullptr, canvas->getTotalMatrix()); + cc::PlaybackParams params(nullptr, canvas->getLocalToDevice()); cc::TransferCacheTestHelper transfer_cache_helper; std::vector<uint8_t> scratch_buffer; cc::PaintOp::DeserializeOptions deserialize_options( diff --git a/chromium/cc/paint/paint_op_buffer_serializer.cc b/chromium/cc/paint/paint_op_buffer_serializer.cc index 709b4321626..824df365dfb 100644 --- a/chromium/cc/paint/paint_op_buffer_serializer.cc +++ b/chromium/cc/paint/paint_op_buffer_serializer.cc @@ -35,7 +35,7 @@ class ScopedFlagsOverride { PlaybackParams MakeParams(const SkCanvas* canvas) { // We don't use an ImageProvider here since the ops are played onto a no-draw // canvas for state tracking and don't need decoded images. - return PlaybackParams(nullptr, canvas->getTotalMatrix()); + return PlaybackParams(nullptr, canvas->getLocalToDevice()); } // Use half of the max int as the extent for the SkNoDrawCanvas. The correct @@ -259,8 +259,7 @@ void PaintOpBufferSerializer::SerializeBuffer( Save(options, params); // The following ops are copying the canvas's ops from // DrawImageRectOp::RasterWithFlags. - SkMatrix trans = SkMatrix::MakeRectToRect(draw_op->src, draw_op->dst, - SkMatrix::kFill_ScaleToFit); + SkM44 trans = SkM44(SkMatrix::RectToRect(draw_op->src, draw_op->dst)); ConcatOp concat_op(trans); bool success = SerializeOp(&concat_op, options, params); if (!success) @@ -380,7 +379,7 @@ PaintOp::SerializeOptions PaintOpBufferSerializer::MakeSerializeOptions() { image_provider_, transfer_cache_, paint_cache_, text_blob_canvas_.get(), strike_server_, color_space_, can_use_lcd_text_, context_supports_distance_field_text_, max_texture_size_, - text_blob_canvas_->getTotalMatrix()); + text_blob_canvas_->getLocalToDevice()); } SimpleBufferSerializer::SimpleBufferSerializer( diff --git a/chromium/cc/paint/paint_op_buffer_unittest.cc b/chromium/cc/paint/paint_op_buffer_unittest.cc index 19233b89d23..39288b97b54 100644 --- a/chromium/cc/paint/paint_op_buffer_unittest.cc +++ b/chromium/cc/paint/paint_op_buffer_unittest.cc @@ -5,10 +5,6 @@ #include "cc/paint/paint_op_buffer.h" #include <algorithm> -#include <memory> -#include <utility> -#include <vector> - #include "base/bind.h" #include "base/memory/scoped_refptr.h" #include "base/stl_util.h" @@ -37,7 +33,6 @@ #include "third_party/skia/include/effects/SkColorMatrixFilter.h" #include "third_party/skia/include/effects/SkDashPathEffect.h" #include "third_party/skia/include/effects/SkLayerDrawLooper.h" -#include "third_party/skia/include/effects/SkOffsetImageFilter.h" #include "third_party/skia/src/core/SkRemoteGlyphCache.h" using testing::_; @@ -488,7 +483,7 @@ TEST(PaintOpBufferTest, DiscardableImagesTracking_NoImageOp) { TEST(PaintOpBufferTest, DiscardableImagesTracking_DrawImage) { PaintOpBuffer buffer; PaintImage image = CreateDiscardablePaintImage(gfx::Size(100, 100)); - buffer.push<DrawImageOp>(image, SkIntToScalar(0), SkIntToScalar(0), nullptr); + buffer.push<DrawImageOp>(image, SkIntToScalar(0), SkIntToScalar(0)); EXPECT_TRUE(buffer.HasDiscardableImages()); } @@ -497,7 +492,7 @@ TEST(PaintOpBufferTest, DiscardableImagesTracking_PaintWorkletImage) { base::MakeRefCounted<TestPaintWorkletInput>(gfx::SizeF(32.0f, 32.0f)); PaintOpBuffer buffer; PaintImage image = CreatePaintWorkletPaintImage(input); - buffer.push<DrawImageOp>(image, SkIntToScalar(0), SkIntToScalar(0), nullptr); + buffer.push<DrawImageOp>(image, SkIntToScalar(0), SkIntToScalar(0)); EXPECT_TRUE(buffer.HasDiscardableImages()); } @@ -508,7 +503,7 @@ TEST(PaintOpBufferTest, DiscardableImagesTracking_PaintWorkletImageRect) { PaintImage image = CreatePaintWorkletPaintImage(input); SkRect src = SkRect::MakeEmpty(); SkRect dst = SkRect::MakeEmpty(); - buffer.push<DrawImageRectOp>(image, src, dst, nullptr, + buffer.push<DrawImageRectOp>(image, src, dst, SkCanvas::kStrict_SrcRectConstraint); EXPECT_TRUE(buffer.HasDiscardableImages()); } @@ -517,7 +512,7 @@ TEST(PaintOpBufferTest, DiscardableImagesTracking_DrawImageRect) { PaintOpBuffer buffer; PaintImage image = CreateDiscardablePaintImage(gfx::Size(100, 100)); buffer.push<DrawImageRectOp>(image, SkRect::MakeWH(100, 100), - SkRect::MakeWH(100, 100), nullptr, + SkRect::MakeWH(100, 100), SkCanvas::kFast_SrcRectConstraint); EXPECT_TRUE(buffer.HasDiscardableImages()); } @@ -665,7 +660,7 @@ TEST(PaintOpBufferTest, NonAAPaint) { PaintFlags non_aa_flags; non_aa_flags.setAntiAlias(true); buffer->push<DrawImageOp>(image, SkIntToScalar(0), SkIntToScalar(0), - &non_aa_flags); + SkSamplingOptions(), &non_aa_flags); EXPECT_FALSE(buffer->HasNonAAPaint()); } @@ -727,6 +722,31 @@ class PaintOpBufferOffsetsTest : public ::testing::Test { PaintOpBuffer buffer_; }; +TEST_F(PaintOpBufferOffsetsTest, EmptyClipRectShouldRejectAnOp) { + SkCanvas device(0, 0); + SkCanvas* canvas = &device; + canvas->translate(-254, 0); + SkIRect bounds = canvas->getDeviceClipBounds(); + EXPECT_TRUE(bounds.isEmpty()); + SkMatrix ctm = canvas->getTotalMatrix(); + EXPECT_EQ(ctm[2], -254); + + scoped_refptr<TestPaintWorkletInput> input = + base::MakeRefCounted<TestPaintWorkletInput>(gfx::SizeF(32.0f, 32.0f)); + PaintImage image = CreatePaintWorkletPaintImage(input); + SkRect src = SkRect::MakeLTRB(0, 0, 100, 100); + SkRect dst = SkRect::MakeLTRB(168, -23, 268, 77); + push_op<DrawImageRectOp>(image, src, dst, + SkCanvas::kStrict_SrcRectConstraint); + std::vector<size_t> offsets = Select({0}); + for (PaintOpBuffer::PlaybackFoldingIterator iter(&buffer_, &offsets); iter; + ++iter) { + const PaintOp* op = *iter; + EXPECT_EQ(op->GetType(), PaintOpType::DrawImageRect); + EXPECT_TRUE(PaintOp::QuickRejectDraw(op, canvas)); + } +} + TEST_F(PaintOpBufferOffsetsTest, ContiguousIndices) { testing::StrictMock<MockCanvas> canvas; @@ -1085,22 +1105,12 @@ std::vector<SkIRect> test_irects = { std::vector<uint32_t> test_ids = {0, 1, 56, 0xFFFFFFFF, 0xFFFFFFFE, 0x10001}; -std::vector<SkMatrix> test_matrices = { - SkMatrix::I(), - SkMatrix::Scale(3.91f, 4.31f), - SkMatrix::Translate(-5.2f, 8.7f), - [] { - SkMatrix matrix; - SkScalar buffer[] = {0, 0, 0, 0, 0, 0, 0, 0, 0}; - matrix.set9(buffer); - return matrix; - }(), - [] { - SkMatrix matrix; - SkScalar buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; - matrix.set9(buffer); - return matrix; - }(), +std::vector<SkM44> test_matrices = { + SkM44(), + SkM44::Scale(3.91f, 4.31f, 1.0f), + SkM44::Translate(-5.2f, 8.7f, 0.0f), + SkM44(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), + SkM44(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16), }; std::vector<SkPath> test_paths = { @@ -1427,8 +1437,8 @@ void PushClipRRectOps(PaintOpBuffer* buffer) { } void PushConcatOps(PaintOpBuffer* buffer) { - for (size_t i = 0; i < test_matrices.size(); ++i) - buffer->push<ConcatOp>(test_matrices[i]); + for (auto& test_matrix : test_matrices) + buffer->push<ConcatOp>(test_matrix); ValidateOps<ConcatOp>(buffer); } @@ -1458,14 +1468,16 @@ void PushDrawImageOps(PaintOpBuffer* buffer) { size_t len = std::min({test_images.size(), test_flags.size(), test_floats.size() - 1}); for (size_t i = 0; i < len; ++i) { - buffer->push<DrawImageOp>(test_images[i], test_floats[i], - test_floats[i + 1], &test_flags[i]); + buffer->push<DrawImageOp>( + test_images[i], test_floats[i], test_floats[i + 1], + SkSamplingOptions(test_flags[i].getFilterQuality(), + SkSamplingOptions::kMedium_asMipmapLinear), + &test_flags[i]); } // Test optional flags // TODO(enne): maybe all these optional ops should not be optional. - buffer->push<DrawImageOp>(test_images[0], test_floats[0], test_floats[1], - nullptr); + buffer->push<DrawImageOp>(test_images[0], test_floats[0], test_floats[1]); ValidateOps<DrawImageOp>(buffer); } @@ -1476,14 +1488,16 @@ void PushDrawImageRectOps(PaintOpBuffer* buffer) { SkCanvas::SrcRectConstraint constraint = i % 2 ? SkCanvas::kStrict_SrcRectConstraint : SkCanvas::kFast_SrcRectConstraint; - buffer->push<DrawImageRectOp>(test_images[i], test_rects[i], - test_rects[i + 1], &test_flags[i], - constraint); + buffer->push<DrawImageRectOp>( + test_images[i], test_rects[i], test_rects[i + 1], + SkSamplingOptions(test_flags[i].getFilterQuality(), + SkSamplingOptions::kMedium_asMipmapLinear), + &test_flags[i], constraint); } // Test optional flags. buffer->push<DrawImageRectOp>(test_images[0], test_rects[0], test_rects[1], - nullptr, SkCanvas::kStrict_SrcRectConstraint); + SkCanvas::kStrict_SrcRectConstraint); ValidateOps<DrawImageRectOp>(buffer); } @@ -1658,8 +1672,8 @@ void PushScaleOps(PaintOpBuffer* buffer) { } void PushSetMatrixOps(PaintOpBuffer* buffer) { - for (size_t i = 0; i < test_matrices.size(); ++i) - buffer->push<SetMatrixOp>(test_matrices[i]); + for (auto& test_matrix : test_matrices) + buffer->push<SetMatrixOp>(test_matrix); ValidateOps<SetMatrixOp>(buffer); } @@ -1987,7 +2001,7 @@ TEST_P(PaintOpSerializationTest, UsesOverridenFlags) { base::AlignedAlloc(deserialized_size, PaintOpBuffer::PaintOpAlign))); for (const auto* op : PaintOpBuffer::Iterator(&buffer_)) { options_provider.mutable_serialize_options().flags_to_serialize = - &static_cast<const PaintOpWithFlags*>(op)->flags; + &(static_cast<const PaintOpWithFlags*>(op))->flags; size_t bytes_written = op->Serialize(output_.get(), output_size_, options_provider.serialize_options()); @@ -2283,7 +2297,7 @@ TEST(PaintOpBufferTest, ClipsImagesDuringSerialization) { buffer.push<DrawImageOp>( CreateDiscardablePaintImage(test_case.image_rect.size()), static_cast<SkScalar>(test_case.image_rect.x()), - static_cast<SkScalar>(test_case.image_rect.y()), nullptr); + static_cast<SkScalar>(test_case.image_rect.y())); std::unique_ptr<char, base::AlignedFreeDeleter> memory( static_cast<char*>(base::AlignedAlloc(PaintOpBuffer::kInitialBufferSize, @@ -2478,7 +2492,10 @@ TEST(PaintOpBufferTest, ValidateSkClip) { buffer.push<ClipRectOp>(test_rects[0], bad_clip, true); buffer.push<ClipRRectOp>(test_rrects[0], bad_clip, false); - SkClipOp bad_clip_max = static_cast<SkClipOp>(~static_cast<uint32_t>(0)); + // SkClipOp is serialized to uint8_t (see WriteEnum). Values outside uint8_t + // would crash the serialization, so this is the max value that passes checked + // cast but will still fail validation during deserialization. + SkClipOp bad_clip_max = static_cast<SkClipOp>(static_cast<uint8_t>(~0)); buffer.push<ClipRectOp>(test_rects[1], bad_clip_max, false); TestOptionsProvider options_provider; @@ -2540,15 +2557,15 @@ TEST(PaintOpBufferTest, ValidateSkBlendMode) { SkBlendMode::kSaturation, SkBlendMode::kColor, SkBlendMode::kLuminosity, - static_cast<SkBlendMode>(static_cast<uint32_t>(SkBlendMode::kLastMode) + + static_cast<SkBlendMode>(static_cast<uint8_t>(SkBlendMode::kLastMode) + 1), - static_cast<SkBlendMode>(static_cast<uint32_t>(~0)), + static_cast<SkBlendMode>(static_cast<uint8_t>(~0)), }; SkBlendMode bad_modes_for_flags[] = { - static_cast<SkBlendMode>(static_cast<uint32_t>(SkBlendMode::kLastMode) + + static_cast<SkBlendMode>(static_cast<uint8_t>(SkBlendMode::kLastMode) + 1), - static_cast<SkBlendMode>(static_cast<uint32_t>(~0)), + static_cast<SkBlendMode>(static_cast<uint8_t>(~0)), }; for (size_t i = 0; i < base::size(bad_modes_for_draw_color); ++i) { @@ -2606,9 +2623,9 @@ TEST(PaintOpBufferTest, ValidateRects) { SkData::MakeWithCString("test1")); buffer.push<ClipRectOp>(bad_rect, SkClipOp::kDifference, true); - buffer.push<DrawImageRectOp>(test_images[0], bad_rect, test_rects[1], nullptr, + buffer.push<DrawImageRectOp>(test_images[0], bad_rect, test_rects[1], SkCanvas::kStrict_SrcRectConstraint); - buffer.push<DrawImageRectOp>(test_images[0], test_rects[0], bad_rect, nullptr, + buffer.push<DrawImageRectOp>(test_images[0], test_rects[0], bad_rect, SkCanvas::kStrict_SrcRectConstraint); buffer.push<DrawOvalOp>(bad_rect, test_flags[0]); buffer.push<DrawRectOp>(bad_rect, test_flags[0]); @@ -2810,7 +2827,7 @@ class MockImageProvider : public ImageProvider { sk_sp<SkImage> image = SkImage::MakeFromBitmap(bitmap); size_t i = index_++; return ScopedResult(DecodedDrawImage(image, nullptr, src_rect_offset_[i], - scale_[i], quality_[i])); + scale_[i], quality_[i], true)); } void SetRecord(sk_sp<PaintRecord> record) { record_ = std::move(record); } @@ -2834,9 +2851,8 @@ TEST(PaintOpBufferTest, SkipsOpsOutsideClip) { buffer.push<ClipRectOp>(SkRect::MakeXYWH(0, 0, 100, 100), SkClipOp::kIntersect, false); - PaintFlags flags; PaintImage paint_image = CreateDiscardablePaintImage(gfx::Size(10, 10)); - buffer.push<DrawImageOp>(paint_image, 105.0f, 105.0f, &flags); + buffer.push<DrawImageOp>(paint_image, 105.0f, 105.0f); PaintFlags image_flags; image_flags.setShader(PaintShader::MakeImage(paint_image, SkTileMode::kRepeat, SkTileMode::kRepeat, nullptr)); @@ -2859,9 +2875,8 @@ TEST(PaintOpBufferTest, SkipsOpsWithFailedDecodes) { MockImageProvider image_provider(true); PaintOpBuffer buffer; - PaintFlags flags; PaintImage paint_image = CreateDiscardablePaintImage(gfx::Size(10, 10)); - buffer.push<DrawImageOp>(paint_image, 105.0f, 105.0f, &flags); + buffer.push<DrawImageOp>(paint_image, 105.0f, 105.0f); PaintFlags image_flags; image_flags.setShader(PaintShader::MakeImage(paint_image, SkTileMode::kRepeat, SkTileMode::kRepeat, nullptr)); @@ -2879,10 +2894,10 @@ MATCHER(NonLazyImage, "") { } MATCHER_P2(MatchesRect, rect, scale, "") { - EXPECT_EQ(arg->x(), rect.x() * scale.width()); - EXPECT_EQ(arg->y(), rect.y() * scale.height()); - EXPECT_EQ(arg->width(), rect.width() * scale.width()); - EXPECT_EQ(arg->height(), rect.height() * scale.height()); + EXPECT_EQ(arg.x(), rect.x() * scale.width()); + EXPECT_EQ(arg.y(), rect.y() * scale.height()); + EXPECT_EQ(arg.width(), rect.width() * scale.width()); + EXPECT_EQ(arg.height(), rect.height() * scale.height()); return true; } @@ -2928,7 +2943,7 @@ TEST(PaintOpBufferTest, RasterPaintWorkletImageRectBasicCase) { PaintImage image = CreatePaintWorkletPaintImage(input); SkRect src = SkRect::MakeXYWH(0, 0, 100, 100); SkRect dst = SkRect::MakeXYWH(0, 0, 100, 100); - blink_buffer.push<DrawImageRectOp>(image, src, dst, nullptr, + blink_buffer.push<DrawImageRectOp>(image, src, dst, SkCanvas::kStrict_SrcRectConstraint); testing::StrictMock<MockCanvas> canvas; @@ -2953,10 +2968,10 @@ TEST(PaintOpBufferTest, RasterPaintWorkletImageRectTranslated) { PaintFlags noop_flags; SkRect savelayer_rect = SkRect::MakeXYWH(0, 0, 10, 10); paint_worklet_buffer->push<SaveLayerOp>(&savelayer_rect, &noop_flags); - PaintFlags draw_flags; - draw_flags.setFilterQuality(kLow_SkFilterQuality); PaintImage paint_image = CreateDiscardablePaintImage(gfx::Size(10, 10)); - paint_worklet_buffer->push<DrawImageOp>(paint_image, 0.0f, 0.0f, &draw_flags); + paint_worklet_buffer->push<DrawImageOp>( + paint_image, 0.0f, 0.0f, SkSamplingOptions(SkFilterMode::kLinear), + nullptr); std::vector<SkSize> src_rect_offset = {SkSize::MakeEmpty()}; std::vector<SkSize> scale_adjustment = {SkSize::Make(0.2f, 0.2f)}; @@ -2970,21 +2985,22 @@ TEST(PaintOpBufferTest, RasterPaintWorkletImageRectTranslated) { PaintImage image = CreatePaintWorkletPaintImage(input); SkRect src = SkRect::MakeXYWH(0, 0, 100, 100); SkRect dst = SkRect::MakeXYWH(5, 7, 100, 100); - blink_buffer.push<DrawImageRectOp>(image, src, dst, nullptr, + blink_buffer.push<DrawImageRectOp>(image, src, dst, SkCanvas::kStrict_SrcRectConstraint); testing::StrictMock<MockCanvas> canvas; testing::Sequence s; + SkSamplingOptions sampling({1.0f / 3, 1.0f / 3}); + EXPECT_CALL(canvas, willSave()).InSequence(s); EXPECT_CALL(canvas, OnSaveLayer()).InSequence(s); EXPECT_CALL(canvas, OnSaveLayer()).InSequence(s); - EXPECT_CALL(canvas, didConcat(SkMatrix::Translate(5.0f, 7.0f))); + EXPECT_CALL(canvas, didConcat44(SkM44::Translate(5.0f, 7.0f))); EXPECT_CALL(canvas, willSave()).InSequence(s); EXPECT_CALL(canvas, didScale(1.0f / scale_adjustment[0].width(), 1.0f / scale_adjustment[0].height())); - EXPECT_CALL(canvas, onDrawImage(NonLazyImage(), 0.0f, 0.0f, - MatchesQuality(quality[0]))); + EXPECT_CALL(canvas, onDrawImage2(NonLazyImage(), 0.0f, 0.0f, sampling, _)); EXPECT_CALL(canvas, willRestore()).InSequence(s); EXPECT_CALL(canvas, willRestore()).InSequence(s); EXPECT_CALL(canvas, willRestore()).InSequence(s); @@ -2998,10 +3014,10 @@ TEST(PaintOpBufferTest, RasterPaintWorkletImageRectScaled) { PaintFlags noop_flags; SkRect savelayer_rect = SkRect::MakeXYWH(0, 0, 10, 10); paint_worklet_buffer->push<SaveLayerOp>(&savelayer_rect, &noop_flags); - PaintFlags draw_flags; - draw_flags.setFilterQuality(kLow_SkFilterQuality); PaintImage paint_image = CreateDiscardablePaintImage(gfx::Size(10, 10)); - paint_worklet_buffer->push<DrawImageOp>(paint_image, 0.0f, 0.0f, &draw_flags); + paint_worklet_buffer->push<DrawImageOp>( + paint_image, 0.0f, 0.0f, SkSamplingOptions(SkFilterMode::kLinear), + nullptr); std::vector<SkSize> src_rect_offset = {SkSize::MakeEmpty()}; std::vector<SkSize> scale_adjustment = {SkSize::Make(0.2f, 0.2f)}; @@ -3015,21 +3031,22 @@ TEST(PaintOpBufferTest, RasterPaintWorkletImageRectScaled) { PaintImage image = CreatePaintWorkletPaintImage(input); SkRect src = SkRect::MakeXYWH(0, 0, 100, 100); SkRect dst = SkRect::MakeXYWH(0, 0, 200, 150); - blink_buffer.push<DrawImageRectOp>(image, src, dst, nullptr, + blink_buffer.push<DrawImageRectOp>(image, src, dst, SkCanvas::kStrict_SrcRectConstraint); testing::StrictMock<MockCanvas> canvas; testing::Sequence s; + SkSamplingOptions sampling({1.0f / 3, 1.0f / 3}); + EXPECT_CALL(canvas, willSave()).InSequence(s); EXPECT_CALL(canvas, OnSaveLayer()).InSequence(s); EXPECT_CALL(canvas, OnSaveLayer()).InSequence(s); - EXPECT_CALL(canvas, didConcat(SkMatrix::Scale(2.f, 1.5f))); + EXPECT_CALL(canvas, didConcat44(SkM44::Scale(2.f, 1.5f))); EXPECT_CALL(canvas, willSave()).InSequence(s); EXPECT_CALL(canvas, didScale(1.0f / scale_adjustment[0].width(), 1.0f / scale_adjustment[0].height())); - EXPECT_CALL(canvas, onDrawImage(NonLazyImage(), 0.0f, 0.0f, - MatchesQuality(quality[0]))); + EXPECT_CALL(canvas, onDrawImage2(NonLazyImage(), 0.0f, 0.0f, sampling, _)); EXPECT_CALL(canvas, willRestore()).InSequence(s); EXPECT_CALL(canvas, willRestore()).InSequence(s); EXPECT_CALL(canvas, willRestore()).InSequence(s); @@ -3043,13 +3060,13 @@ TEST(PaintOpBufferTest, RasterPaintWorkletImageRectClipped) { PaintFlags noop_flags; SkRect savelayer_rect = SkRect::MakeXYWH(0, 0, 60, 60); paint_worklet_buffer->push<SaveLayerOp>(&savelayer_rect, &noop_flags); - PaintFlags draw_flags; - draw_flags.setFilterQuality(kLow_SkFilterQuality); + SkSamplingOptions linear(SkFilterMode::kLinear); PaintImage paint_image = CreateDiscardablePaintImage(gfx::Size(10, 10)); // One rect inside the src-rect, one outside. - paint_worklet_buffer->push<DrawImageOp>(paint_image, 0.0f, 0.0f, &draw_flags); - paint_worklet_buffer->push<DrawImageOp>(paint_image, 50.0f, 50.0f, - &draw_flags); + paint_worklet_buffer->push<DrawImageOp>(paint_image, 0.0f, 0.0f, linear, + nullptr); + paint_worklet_buffer->push<DrawImageOp>(paint_image, 50.0f, 50.0f, linear, + nullptr); std::vector<SkSize> src_rect_offset = {SkSize::MakeEmpty()}; std::vector<SkSize> scale_adjustment = {SkSize::Make(0.2f, 0.2f)}; @@ -3063,20 +3080,21 @@ TEST(PaintOpBufferTest, RasterPaintWorkletImageRectClipped) { PaintImage image = CreatePaintWorkletPaintImage(input); SkRect src = SkRect::MakeXYWH(0, 0, 20, 20); SkRect dst = SkRect::MakeXYWH(0, 0, 20, 20); - blink_buffer.push<DrawImageRectOp>(image, src, dst, nullptr, + blink_buffer.push<DrawImageRectOp>(image, src, dst, SkCanvas::kStrict_SrcRectConstraint); testing::StrictMock<MockCanvas> canvas; testing::Sequence s; + SkSamplingOptions sampling({1.0f / 3, 1.0f / 3}); + EXPECT_CALL(canvas, willSave()).InSequence(s); EXPECT_CALL(canvas, OnSaveLayer()).InSequence(s); EXPECT_CALL(canvas, OnSaveLayer()).InSequence(s); EXPECT_CALL(canvas, willSave()).InSequence(s); EXPECT_CALL(canvas, didScale(1.0f / scale_adjustment[0].width(), 1.0f / scale_adjustment[0].height())); - EXPECT_CALL(canvas, onDrawImage(NonLazyImage(), 0.0f, 0.0f, - MatchesQuality(quality[0]))); + EXPECT_CALL(canvas, onDrawImage2(NonLazyImage(), 0.0f, 0.0f, sampling, _)); EXPECT_CALL(canvas, willRestore()).InSequence(s); EXPECT_CALL(canvas, willRestore()).InSequence(s); EXPECT_CALL(canvas, willRestore()).InSequence(s); @@ -3098,12 +3116,12 @@ TEST(PaintOpBufferTest, ReplacesImagesFromProvider) { PaintOpBuffer buffer; SkRect rect = SkRect::MakeWH(10, 10); - PaintFlags flags; - flags.setFilterQuality(kLow_SkFilterQuality); + SkSamplingOptions sampling(SkFilterMode::kLinear); PaintImage paint_image = CreateDiscardablePaintImage(gfx::Size(10, 10)); - buffer.push<DrawImageOp>(paint_image, 0.0f, 0.0f, &flags); - buffer.push<DrawImageRectOp>(paint_image, rect, rect, &flags, + buffer.push<DrawImageOp>(paint_image, 0.0f, 0.0f, sampling, nullptr); + buffer.push<DrawImageRectOp>(paint_image, rect, rect, sampling, nullptr, SkCanvas::kFast_SrcRectConstraint); + PaintFlags flags; flags.setShader(PaintShader::MakeImage(paint_image, SkTileMode::kRepeat, SkTileMode::kRepeat, nullptr)); buffer.push<DrawOvalOp>(SkRect::MakeWH(10, 10), flags); @@ -3111,22 +3129,24 @@ TEST(PaintOpBufferTest, ReplacesImagesFromProvider) { testing::StrictMock<MockCanvas> canvas; testing::Sequence s; + SkSamplingOptions sampling0({1.0f / 3, 1.0f / 3}); + SkSamplingOptions sampling1(SkFilterMode::kLinear, SkMipmapMode::kLinear); + // Save/scale/image/restore from DrawImageop. EXPECT_CALL(canvas, willSave()).InSequence(s); EXPECT_CALL(canvas, didScale(1.0f / scale_adjustment[0].width(), 1.0f / scale_adjustment[0].height())); - EXPECT_CALL(canvas, onDrawImage(NonLazyImage(), 0.0f, 0.0f, - MatchesQuality(quality[0]))); + EXPECT_CALL(canvas, onDrawImage2(NonLazyImage(), 0.0f, 0.0f, sampling0, _)); EXPECT_CALL(canvas, willRestore()).InSequence(s); // DrawImageRectop. SkRect src_rect = rect.makeOffset(src_rect_offset[1].width(), src_rect_offset[1].height()); EXPECT_CALL(canvas, - onDrawImageRect( - NonLazyImage(), MatchesRect(src_rect, scale_adjustment[1]), - SkRect::MakeWH(10, 10), MatchesQuality(quality[1]), - SkCanvas::kFast_SrcRectConstraint)); + onDrawImageRect2(NonLazyImage(), + MatchesRect(src_rect, scale_adjustment[1]), + SkRect::MakeWH(10, 10), sampling1, _, + SkCanvas::kFast_SrcRectConstraint)); // DrawOvalop. EXPECT_CALL(canvas, onDrawOval(SkRect::MakeWH(10, 10), @@ -3146,14 +3166,14 @@ TEST(PaintOpBufferTest, DrawImageRectOpWithLooperNoImageProvider) { PaintFlags paint_flags; paint_flags.setLooper(sk_draw_looper_builder.detach()); buffer.push<DrawImageRectOp>(image, SkRect::MakeWH(100, 100), - SkRect::MakeWH(100, 100), &paint_flags, - SkCanvas::kFast_SrcRectConstraint); + SkRect::MakeWH(100, 100), SkSamplingOptions(), + &paint_flags, SkCanvas::kFast_SrcRectConstraint); testing::StrictMock<MockCanvas> canvas; EXPECT_CALL(canvas, willSave); EXPECT_CALL(canvas, didTranslate); EXPECT_CALL(canvas, willRestore); - EXPECT_CALL(canvas, onDrawImageRect).Times(2); + EXPECT_CALL(canvas, onDrawImageRect2).Times(2); buffer.Playback(&canvas, PlaybackParams(nullptr)); } @@ -3169,14 +3189,14 @@ TEST(PaintOpBufferTest, DrawImageRectOpWithLooperWithImageProvider) { PaintFlags paint_flags; paint_flags.setLooper(sk_draw_looper_builder.detach()); buffer.push<DrawImageRectOp>(image, SkRect::MakeWH(100, 100), - SkRect::MakeWH(100, 100), &paint_flags, - SkCanvas::kFast_SrcRectConstraint); + SkRect::MakeWH(100, 100), SkSamplingOptions(), + &paint_flags, SkCanvas::kFast_SrcRectConstraint); testing::StrictMock<MockCanvas> canvas; EXPECT_CALL(canvas, willSave); EXPECT_CALL(canvas, didTranslate); EXPECT_CALL(canvas, willRestore); - EXPECT_CALL(canvas, onDrawImageRect).Times(2); + EXPECT_CALL(canvas, onDrawImageRect2).Times(2); std::vector<SkSize> src_rect_offset = {SkSize::MakeEmpty()}; std::vector<SkSize> scale_adjustment = {SkSize::Make(1.0f, 1.0f)}; @@ -3191,11 +3211,11 @@ TEST(PaintOpBufferTest, ReplacesImagesFromProviderOOP) { SkRect rect = SkRect::MakeWH(10, 10); PaintFlags flags; - flags.setFilterQuality(kLow_SkFilterQuality); + SkSamplingOptions sampling(SkFilterMode::kLinear); PaintImage paint_image = CreateDiscardablePaintImage(gfx::Size(10, 10)); buffer.push<ScaleOp>(expected_scale.width(), expected_scale.height()); - buffer.push<DrawImageOp>(paint_image, 0.0f, 0.0f, &flags); - buffer.push<DrawImageRectOp>(paint_image, rect, rect, &flags, + buffer.push<DrawImageOp>(paint_image, 0.0f, 0.0f, sampling, nullptr); + buffer.push<DrawImageRectOp>(paint_image, rect, rect, sampling, nullptr, SkCanvas::kFast_SrcRectConstraint); flags.setShader(PaintShader::MakeImage(paint_image, SkTileMode::kRepeat, SkTileMode::kRepeat, nullptr)); @@ -3231,14 +3251,14 @@ TEST(PaintOpBufferTest, ReplacesImagesFromProviderOOP) { EXPECT_CALL(canvas, willSave()).InSequence(s); EXPECT_CALL(canvas, didScale(1.0f / expected_scale.width(), 1.0f / expected_scale.height())); - EXPECT_CALL(canvas, onDrawImage(NonLazyImage(), 0.0f, 0.0f, _)); + EXPECT_CALL(canvas, onDrawImage2(NonLazyImage(), 0.0f, 0.0f, _, _)); EXPECT_CALL(canvas, willRestore()).InSequence(s); op->Raster(&canvas, params); } else if (op->GetType() == PaintOpType::DrawImageRect) { - EXPECT_CALL(canvas, onDrawImageRect(NonLazyImage(), - MatchesRect(rect, expected_scale), - SkRect::MakeWH(10, 10), _, - SkCanvas::kFast_SrcRectConstraint)); + EXPECT_CALL(canvas, onDrawImageRect2(NonLazyImage(), + MatchesRect(rect, expected_scale), + SkRect::MakeWH(10, 10), _, _, + SkCanvas::kFast_SrcRectConstraint)); op->Raster(&canvas, params); } else if (op->GetType() == PaintOpType::DrawOval) { EXPECT_CALL(canvas, onDrawOval(SkRect::MakeWH(10, 10), @@ -3259,19 +3279,18 @@ TEST_P(PaintFilterSerializationTest, Basic) { std::vector<sk_sp<PaintFilter>> filters = { sk_sp<PaintFilter>{new ColorFilterPaintFilter( SkColorFilters::LinearToSRGBGamma(), nullptr)}, - sk_sp<PaintFilter>{new BlurPaintFilter( - 0.5f, 0.3f, SkBlurImageFilter::kRepeat_TileMode, nullptr)}, + sk_sp<PaintFilter>{ + new BlurPaintFilter(0.5f, 0.3f, SkTileMode::kRepeat, nullptr)}, sk_sp<PaintFilter>{new DropShadowPaintFilter( 5.f, 10.f, 0.1f, 0.3f, SK_ColorBLUE, - SkDropShadowImageFilter::kDrawShadowOnly_ShadowMode, nullptr)}, + DropShadowPaintFilter::ShadowMode::kDrawShadowOnly, nullptr)}, sk_sp<PaintFilter>{new MagnifierPaintFilter(SkRect::MakeXYWH(5, 6, 7, 8), 10.5f, nullptr)}, sk_sp<PaintFilter>{new AlphaThresholdPaintFilter( SkRegion(SkIRect::MakeXYWH(0, 0, 100, 200)), 10.f, 20.f, nullptr)}, sk_sp<PaintFilter>{new MatrixConvolutionPaintFilter( SkISize::Make(3, 3), scalars, 30.f, 123.f, SkIPoint::Make(0, 0), - SkMatrixConvolutionImageFilter::kClampToBlack_TileMode, true, - nullptr)}, + SkTileMode::kDecal, true, nullptr)}, sk_sp<PaintFilter>{new MorphologyPaintFilter( MorphologyPaintFilter::MorphType::kErode, 15.5f, 30.2f, nullptr)}, sk_sp<PaintFilter>{new OffsetPaintFilter(-1.f, -2.f, nullptr)}, @@ -3300,12 +3319,10 @@ TEST_P(PaintFilterSerializationTest, Basic) { filters.emplace_back(new ComposePaintFilter(filters[0], filters[1])); filters.emplace_back( new XfermodePaintFilter(SkBlendMode::kDst, filters[2], filters[3])); - filters.emplace_back(new ArithmeticPaintFilter( - 1.1f, 2.2f, 3.3f, 4.4f, false, filters[4], filters[5], nullptr)); + filters.emplace_back(new ArithmeticPaintFilter(1.1f, 2.2f, 3.3f, 4.4f, false, + filters[4], filters[5])); filters.emplace_back(new DisplacementMapEffectPaintFilter( - SkDisplacementMapEffect::kR_ChannelSelectorType, - SkDisplacementMapEffect::kG_ChannelSelectorType, 10, filters[6], - filters[7])); + SkColorChannel::kR, SkColorChannel::kG, 10, filters[6], filters[7])); filters.emplace_back(new MergePaintFilter(filters.data(), filters.size())); filters.emplace_back(new RecordPaintFilter( sk_sp<PaintRecord>{new PaintRecord}, SkRect::MakeXYWH(10, 15, 20, 25))); @@ -3521,7 +3538,7 @@ TEST(PaintOpBufferTest, CustomData) { buffer.push<CustomDataOp>(9999u); testing::StrictMock<MockCanvas> canvas; EXPECT_CALL(canvas, onCustomCallback(&canvas, 9999)).Times(1); - buffer.Playback(&canvas, PlaybackParams(nullptr, SkMatrix::I(), + buffer.Playback(&canvas, PlaybackParams(nullptr, SkM44(), base::BindRepeating( &MockCanvas::onCustomCallback, base::Unretained(&canvas)))); @@ -3561,8 +3578,7 @@ TEST(PaintOpBufferTest, DrawImageRectSerializeScaledImages) { SkRect src = SkRect::MakeXYWH(3, 4, 20, 6); SkRect dst = SkRect::MakeXYWH(20, 38, 5, 30); buffer->push<DrawImageRectOp>(CreateDiscardablePaintImage(gfx::Size(32, 16)), - src, dst, nullptr, - SkCanvas::kStrict_SrcRectConstraint); + src, dst, SkCanvas::kStrict_SrcRectConstraint); std::unique_ptr<char, base::AlignedFreeDeleter> memory( static_cast<char*>(base::AlignedAlloc(PaintOpBuffer::kInitialBufferSize, @@ -3587,7 +3603,7 @@ TEST(PaintOpBufferTest, DrawImageRectSerializeScaledImages) { TEST(PaintOpBufferTest, RecordShadersSerializeScaledImages) { auto record_buffer = sk_make_sp<PaintOpBuffer>(); record_buffer->push<DrawImageOp>( - CreateDiscardablePaintImage(gfx::Size(10, 10)), 0.f, 0.f, nullptr); + CreateDiscardablePaintImage(gfx::Size(10, 10)), 0.f, 0.f); auto shader = PaintShader::MakePaintRecord( record_buffer, SkRect::MakeWH(10.f, 10.f), SkTileMode::kRepeat, @@ -3622,7 +3638,7 @@ TEST(PaintOpBufferTest, RecordShadersSerializeScaledImages) { TEST(PaintOpBufferTest, RecordShadersCached) { auto record_buffer = sk_make_sp<PaintOpBuffer>(); record_buffer->push<DrawImageOp>( - CreateDiscardablePaintImage(gfx::Size(10, 10)), 0.f, 0.f, nullptr); + CreateDiscardablePaintImage(gfx::Size(10, 10)), 0.f, 0.f); auto shader = PaintShader::MakePaintRecord( record_buffer, SkRect::MakeWH(10.f, 10.f), SkTileMode::kRepeat, SkTileMode::kRepeat, nullptr); @@ -3740,7 +3756,7 @@ TEST(PaintOpBufferTest, RecordShadersCachedSize) { auto record_buffer = sk_make_sp<PaintOpBuffer>(); size_t estimated_image_size = 30 * 30 * 4; auto image = CreateBitmapImage(gfx::Size(30, 30)); - record_buffer->push<DrawImageOp>(image, 0.f, 0.f, nullptr); + record_buffer->push<DrawImageOp>(image, 0.f, 0.f); auto shader = PaintShader::MakePaintRecord( record_buffer, SkRect::MakeWH(10.f, 10.f), SkTileMode::kRepeat, SkTileMode::kRepeat, nullptr); @@ -3804,7 +3820,7 @@ TEST(PaintOpBufferTest, TotalOpCount) { TEST(PaintOpBufferTest, NullImages) { PaintOpBuffer buffer; - buffer.push<DrawImageOp>(PaintImage(), 0.f, 0.f, nullptr); + buffer.push<DrawImageOp>(PaintImage(), 0.f, 0.f); std::unique_ptr<char, base::AlignedFreeDeleter> memory( static_cast<char*>(base::AlignedAlloc(PaintOpBuffer::kInitialBufferSize, @@ -3935,4 +3951,22 @@ TEST(PaintOpBufferTest, NeedsAdditionalInvalidationForLCDText) { } } +// A regression test for crbug.com/1195276. Ensure that PlaybackParams works +// with SetMatrix operations. +TEST(PaintOpBufferTest, SetMatrixOpWithNonIdentityPlaybackParams) { + for (const auto& original_ctm : test_matrices) { + for (const auto& matrix : test_matrices) { + SkCanvas device(0, 0); + SkCanvas* canvas = &device; + + PlaybackParams params(nullptr, original_ctm); + SetMatrixOp op(matrix); + SetMatrixOp::Raster(&op, canvas, params); + + EXPECT_TRUE(AnnotateOp::AreSkM44sEqual(canvas->getLocalToDevice(), + SkM44(original_ctm, matrix))); + } + } +} + } // namespace cc diff --git a/chromium/cc/paint/paint_op_helper_unittest.cc b/chromium/cc/paint/paint_op_helper_unittest.cc index 3d5db9a8710..3710ef041fe 100644 --- a/chromium/cc/paint/paint_op_helper_unittest.cc +++ b/chromium/cc/paint/paint_op_helper_unittest.cc @@ -43,11 +43,12 @@ TEST(PaintOpHelper, ClipRRectToString) { } TEST(PaintOpHelper, ConcatToString) { - ConcatOp op(SkMatrix::MakeAll(1, 2, 3, 4, 5, 6, 7, 8, 9)); + ConcatOp op(SkM44(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)); std::string str = PaintOpHelper::ToString(&op); EXPECT_EQ(str, - "ConcatOp(matrix=[ 1.0000 2.0000 3.0000][ 4.0000 5.0000 " - "6.0000][ 7.0000 8.0000 9.0000])"); + "ConcatOp(matrix=[ 1.0000 2.0000 3.0000 4.0000][ 5.0000 " + "6.0000 7.0000 8.0000][ 9.0000 10.0000 11.0000 12.0000][ " + "13.0000 14.0000 15.0000 16.0000]])"); } TEST(PaintOpHelper, DrawColorToString) { @@ -76,7 +77,7 @@ TEST(PaintOpHelper, DrawDRRectToString) { } TEST(PaintOpHelper, DrawImageToString) { - DrawImageOp op(PaintImage(), 10.5f, 20.3f, nullptr); + DrawImageOp op(PaintImage(), 10.5f, 20.3f); std::string str = PaintOpHelper::ToString(&op); EXPECT_EQ( str, @@ -92,7 +93,7 @@ TEST(PaintOpHelper, DrawImageToString) { TEST(PaintOpHelper, DrawImageRectToString) { DrawImageRectOp op(PaintImage(), SkRect::MakeXYWH(1, 2, 3, 4), - SkRect::MakeXYWH(5, 6, 7, 8), nullptr, + SkRect::MakeXYWH(5, 6, 7, 8), SkCanvas::kStrict_SrcRectConstraint); std::string str = PaintOpHelper::ToString(&op); EXPECT_EQ( @@ -278,11 +279,12 @@ TEST(PaintOpHelper, ScaleToString) { } TEST(PaintOpHelper, SetMatrixToString) { - SetMatrixOp op(SkMatrix::MakeAll(-1, 2, -3, 4, -5, 6, -7, 8, -9)); + SetMatrixOp op(SkM44(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)); std::string str = PaintOpHelper::ToString(&op); EXPECT_EQ(str, - "SetMatrixOp(matrix=[ -1.0000 2.0000 -3.0000][ 4.0000 -5.0000 " - " 6.0000][ -7.0000 8.0000 -9.0000])"); + "SetMatrixOp(matrix=[ 1.0000 2.0000 3.0000 4.0000][ 5.0000 " + " 6.0000 7.0000 8.0000][ 9.0000 10.0000 11.0000 12.0000][ " + "13.0000 14.0000 15.0000 16.0000]])"); } TEST(PaintOpHelper, TranslateToString) { diff --git a/chromium/cc/paint/paint_op_perftest.cc b/chromium/cc/paint/paint_op_perftest.cc index 4dd80d1c7ac..8ef166e44f1 100644 --- a/chromium/cc/paint/paint_op_perftest.cc +++ b/chromium/cc/paint/paint_op_perftest.cc @@ -15,7 +15,6 @@ #include "third_party/skia/include/effects/SkColorMatrixFilter.h" #include "third_party/skia/include/effects/SkDashPathEffect.h" #include "third_party/skia/include/effects/SkLayerDrawLooper.h" -#include "third_party/skia/include/effects/SkOffsetImageFilter.h" namespace cc { namespace { @@ -112,7 +111,7 @@ class PaintOpPerfTest : public testing::Test { TEST_F(PaintOpPerfTest, SimpleOps) { PaintOpBuffer buffer; for (size_t i = 0; i < 100; ++i) - buffer.push<ConcatOp>(SkMatrix::I()); + buffer.push<ConcatOp>(SkM44()); RunTest("simple", buffer); } diff --git a/chromium/cc/paint/paint_op_reader.cc b/chromium/cc/paint/paint_op_reader.cc index 59c594dc250..eb795ba528e 100644 --- a/chromium/cc/paint/paint_op_reader.cc +++ b/chromium/cc/paint/paint_op_reader.cc @@ -41,18 +41,6 @@ bool IsValidPaintShaderType(PaintShader::Type type) { static_cast<uint8_t>(PaintShader::Type::kShaderCount); } -// SkTileMode has no defined backing type, so read/write int32_t's. -// If read_mode is a valid tile mode, this returns true and updates mode to the -// equivalent enum value. Otherwise false is returned and mode is not modified. -bool ValidateAndGetSkShaderTileMode(int32_t read_mode, SkTileMode* mode) { - if (read_mode < 0 || read_mode >= kSkTileModeCount) { - return false; - } - - *mode = static_cast<SkTileMode>(read_mode); - return true; -} - bool IsValidPaintShaderScalingBehavior(PaintShader::ScalingBehavior behavior) { return behavior == PaintShader::ScalingBehavior::kRasterAtScale || behavior == PaintShader::ScalingBehavior::kFixedScale; @@ -266,11 +254,7 @@ void PaintOpReader::Read(PaintFlags* flags) { Read(&flags->width_); Read(&flags->miter_limit_); - ReadSimple(&flags->blend_mode_); - if (flags->blend_mode_ > static_cast<uint32_t>(SkBlendMode::kLastMode)) { - SetInvalid(); - return; - } + Read(&flags->blend_mode_); ReadSimple(&flags->bitfields_uint_); @@ -523,16 +507,8 @@ void PaintOpReader::Read(sk_sp<PaintShader>* shader) { ReadSimple(&ref.flags_); ReadSimple(&ref.end_radius_); ReadSimple(&ref.start_radius_); - - // See ValidateAndGetSkShaderTileMode - int32_t tx = 0; - int32_t ty = 0; - Read(&tx); - Read(&ty); - if (!ValidateAndGetSkShaderTileMode(tx, &ref.tx_) || - !ValidateAndGetSkShaderTileMode(ty, &ref.ty_)) { - SetInvalid(); - } + Read(&ref.tx_); + Read(&ref.ty_); ReadSimple(&ref.fallback_color_); ReadSimple(&ref.scaling_behavior_); if (!IsValidPaintShaderScalingBehavior(ref.scaling_behavior_)) @@ -646,16 +622,25 @@ void PaintOpReader::Read(SkMatrix* matrix) { FixupMatrixPostSerialization(matrix); } -void PaintOpReader::Read(SkColorType* color_type) { - uint32_t raw_color_type = kUnknown_SkColorType; - ReadSimple(&raw_color_type); +void PaintOpReader::Read(SkM44* matrix) { + ReadSimple(matrix); +} - if (raw_color_type > kLastEnum_SkColorType) { - SetInvalid(); - return; +void PaintOpReader::Read(SkSamplingOptions* sampling) { + bool useCubic; + Read(&useCubic); + if (useCubic) { + SkCubicResampler cubic; + Read(&cubic.B); + Read(&cubic.C); + *sampling = SkSamplingOptions(cubic); + } else { + SkFilterMode filter; + SkMipmapMode mipmap; + Read(&filter); + Read(&mipmap); + *sampling = SkSamplingOptions(filter, mipmap); } - - *color_type = static_cast<SkColorType>(raw_color_type); } void PaintOpReader::Read(SkYUVColorSpace* yuv_color_space) { @@ -670,6 +655,33 @@ void PaintOpReader::Read(SkYUVColorSpace* yuv_color_space) { *yuv_color_space = static_cast<SkYUVColorSpace>(raw_yuv_color_space); } +void PaintOpReader::Read(SkYUVAInfo::PlaneConfig* plane_config) { + uint32_t raw_plane_config = + static_cast<uint32_t>(SkYUVAInfo::PlaneConfig::kUnknown); + ReadSimple(&raw_plane_config); + + if (raw_plane_config > + static_cast<uint32_t>(SkYUVAInfo::PlaneConfig::kLast)) { + SetInvalid(); + return; + } + + *plane_config = static_cast<SkYUVAInfo::PlaneConfig>(raw_plane_config); +} + +void PaintOpReader::Read(SkYUVAInfo::Subsampling* subsampling) { + uint32_t raw_subsampling = + static_cast<uint32_t>(SkYUVAInfo::Subsampling::kUnknown); + ReadSimple(&raw_subsampling); + + if (raw_subsampling > static_cast<uint32_t>(SkYUVAInfo::Subsampling::kLast)) { + SetInvalid(); + return; + } + + *subsampling = static_cast<SkYUVAInfo::Subsampling>(raw_subsampling); +} + void PaintOpReader::Read(gpu::Mailbox* mailbox) { ReadData(sizeof(gpu::Mailbox::Name), (*mailbox).name); } @@ -742,14 +754,11 @@ const volatile void* PaintOpReader::ExtractReadableMemory(size_t bytes) { } void PaintOpReader::Read(sk_sp<PaintFilter>* filter) { - uint32_t type_int = 0; - ReadSimple(&type_int); - if (type_int > static_cast<uint32_t>(PaintFilter::Type::kMaxFilterType)) - SetInvalid(); + PaintFilter::Type type; + ReadEnum(&type); if (!valid_) return; - auto type = static_cast<PaintFilter::Type>(type_int); if (type == PaintFilter::Type::kNullFilter) { *filter = nullptr; return; @@ -759,11 +768,9 @@ void PaintOpReader::Read(sk_sp<PaintFilter>* filter) { base::Optional<PaintFilter::CropRect> crop_rect; ReadSimple(&has_crop_rect); if (has_crop_rect) { - uint32_t flags = 0; SkRect rect = SkRect::MakeEmpty(); - ReadSimple(&flags); ReadSimple(&rect); - crop_rect.emplace(rect, flags); + crop_rect.emplace(rect); } AlignMemory(4); @@ -862,12 +869,12 @@ void PaintOpReader::ReadBlurPaintFilter( const base::Optional<PaintFilter::CropRect>& crop_rect) { SkScalar sigma_x = 0.f; SkScalar sigma_y = 0.f; - BlurPaintFilter::TileMode tile_mode = SkBlurImageFilter::kClamp_TileMode; + SkTileMode tile_mode; sk_sp<PaintFilter> input; Read(&sigma_x); Read(&sigma_y); - ReadSimple(&tile_mode); + Read(&tile_mode); Read(&input); if (!valid_) return; @@ -884,8 +891,7 @@ void PaintOpReader::ReadDropShadowPaintFilter( SkScalar sigma_x = 0.f; SkScalar sigma_y = 0.f; SkColor color = SK_ColorBLACK; - DropShadowPaintFilter::ShadowMode shadow_mode = - SkDropShadowImageFilter::kDrawShadowAndForeground_ShadowMode; + DropShadowPaintFilter::ShadowMode shadow_mode; sk_sp<PaintFilter> input; Read(&dx); @@ -893,11 +899,9 @@ void PaintOpReader::ReadDropShadowPaintFilter( Read(&sigma_x); Read(&sigma_y); Read(&color); - ReadSimple(&shadow_mode); + ReadEnum(&shadow_mode); Read(&input); - if (shadow_mode > SkDropShadowImageFilter::kLast_ShadowMode) - SetInvalid(); if (!valid_) return; filter->reset(new DropShadowPaintFilter(dx, dy, sigma_x, sigma_y, color, @@ -956,19 +960,15 @@ void PaintOpReader::ReadAlphaThresholdPaintFilter( void PaintOpReader::ReadXfermodePaintFilter( sk_sp<PaintFilter>* filter, const base::Optional<PaintFilter::CropRect>& crop_rect) { - uint32_t blend_mode_int = 0; + SkBlendMode blend_mode; sk_sp<PaintFilter> background; sk_sp<PaintFilter> foreground; - Read(&blend_mode_int); + Read(&blend_mode); Read(&background); Read(&foreground); - SkBlendMode blend_mode = SkBlendMode::kClear; - if (blend_mode_int > static_cast<uint32_t>(SkBlendMode::kLastMode)) - SetInvalid(); if (!valid_) return; - blend_mode = static_cast<SkBlendMode>(blend_mode_int); filter->reset(new XfermodePaintFilter(blend_mode, std::move(background), std::move(foreground), @@ -1006,7 +1006,7 @@ void PaintOpReader::ReadMatrixConvolutionPaintFilter( SkScalar gain = 0.f; SkScalar bias = 0.f; SkIPoint kernel_offset = SkIPoint::Make(0, 0); - uint32_t tile_mode_int = 0; + SkTileMode tile_mode; bool convolve_alpha = false; sk_sp<PaintFilter> input; @@ -1025,15 +1025,11 @@ void PaintOpReader::ReadMatrixConvolutionPaintFilter( Read(&gain); Read(&bias); ReadSimple(&kernel_offset); - Read(&tile_mode_int); + Read(&tile_mode); Read(&convolve_alpha); Read(&input); - if (tile_mode_int > SkMatrixConvolutionImageFilter::kMax_TileMode) - SetInvalid(); if (!valid_) return; - MatrixConvolutionPaintFilter::TileMode tile_mode = - static_cast<MatrixConvolutionPaintFilter::TileMode>(tile_mode_int); filter->reset(new MatrixConvolutionPaintFilter( kernel_size, kernel.data(), gain, bias, kernel_offset, tile_mode, convolve_alpha, std::move(input), base::OptionalOrNullptr(crop_rect))); @@ -1042,33 +1038,20 @@ void PaintOpReader::ReadMatrixConvolutionPaintFilter( void PaintOpReader::ReadDisplacementMapEffectPaintFilter( sk_sp<PaintFilter>* filter, const base::Optional<PaintFilter::CropRect>& crop_rect) { - // Unknown, R, G, B, A: max type is 4. - static const int kMaxChannelSelectorType = 4; - - uint32_t channel_x_int = 0; - uint32_t channel_y_int = 0; + SkColorChannel channel_x; + SkColorChannel channel_y; SkScalar scale = 0.f; sk_sp<PaintFilter> displacement; sk_sp<PaintFilter> color; - Read(&channel_x_int); - Read(&channel_y_int); + ReadEnum<SkColorChannel, SkColorChannel::kA>(&channel_x); + ReadEnum<SkColorChannel, SkColorChannel::kA>(&channel_y); Read(&scale); Read(&displacement); Read(&color); - if (channel_x_int > kMaxChannelSelectorType || - channel_y_int > kMaxChannelSelectorType) { - SetInvalid(); - } if (!valid_) return; - DisplacementMapEffectPaintFilter::ChannelSelectorType channel_x = - static_cast<DisplacementMapEffectPaintFilter::ChannelSelectorType>( - channel_x_int); - DisplacementMapEffectPaintFilter::ChannelSelectorType channel_y = - static_cast<DisplacementMapEffectPaintFilter::ChannelSelectorType>( - channel_y_int); filter->reset(new DisplacementMapEffectPaintFilter( channel_x, channel_y, scale, std::move(displacement), std::move(color), base::OptionalOrNullptr(crop_rect))); @@ -1136,22 +1119,16 @@ void PaintOpReader::ReadMergePaintFilter( void PaintOpReader::ReadMorphologyPaintFilter( sk_sp<PaintFilter>* filter, const base::Optional<PaintFilter::CropRect>& crop_rect) { - uint32_t morph_type_int = 0; + MorphologyPaintFilter::MorphType morph_type; float radius_x = 0; float radius_y = 0; sk_sp<PaintFilter> input; - Read(&morph_type_int); + ReadEnum(&morph_type); Read(&radius_x); Read(&radius_y); Read(&input); - if (morph_type_int > - static_cast<uint32_t>(MorphologyPaintFilter::MorphType::kMaxMorphType)) { - SetInvalid(); - } if (!valid_) return; - MorphologyPaintFilter::MorphType morph_type = - static_cast<MorphologyPaintFilter::MorphType>(morph_type_int); filter->reset(new MorphologyPaintFilter(morph_type, radius_x, radius_y, std::move(input), base::OptionalOrNullptr(crop_rect))); @@ -1191,28 +1168,21 @@ void PaintOpReader::ReadTilePaintFilter( void PaintOpReader::ReadTurbulencePaintFilter( sk_sp<PaintFilter>* filter, const base::Optional<PaintFilter::CropRect>& crop_rect) { - uint32_t turbulence_type_int = 0; + TurbulencePaintFilter::TurbulenceType turbulence_type; SkScalar base_frequency_x = 0.f; SkScalar base_frequency_y = 0.f; int num_octaves = 0; SkScalar seed = 0.f; SkISize tile_size = SkISize::MakeEmpty(); - Read(&turbulence_type_int); + ReadEnum(&turbulence_type); Read(&base_frequency_x); Read(&base_frequency_y); Read(&num_octaves); Read(&seed); ReadSimple(&tile_size); - if (turbulence_type_int > - static_cast<uint32_t>( - TurbulencePaintFilter::TurbulenceType::kMaxTurbulenceType)) { - SetInvalid(); - } if (!valid_) return; - TurbulencePaintFilter::TurbulenceType turbulence_type = - static_cast<TurbulencePaintFilter::TurbulenceType>(turbulence_type_int); filter->reset(new TurbulencePaintFilter( turbulence_type, base_frequency_x, base_frequency_y, num_octaves, seed, &tile_size, base::OptionalOrNullptr(crop_rect))); @@ -1221,7 +1191,6 @@ void PaintOpReader::ReadTurbulencePaintFilter( void PaintOpReader::ReadPaintFlagsPaintFilter( sk_sp<PaintFilter>* filter, const base::Optional<PaintFilter::CropRect>& crop_rect) { - AlignMemory(4); PaintFlags flags; Read(&flags); if (!valid_) @@ -1238,10 +1207,8 @@ void PaintOpReader::ReadMatrixPaintFilter( sk_sp<PaintFilter> input; Read(&matrix); - ReadSimple(&filter_quality); + Read(&filter_quality); Read(&input); - if (filter_quality > kLast_SkFilterQuality) - SetInvalid(); if (!valid_) return; filter->reset( @@ -1251,7 +1218,7 @@ void PaintOpReader::ReadMatrixPaintFilter( void PaintOpReader::ReadLightingDistantPaintFilter( sk_sp<PaintFilter>* filter, const base::Optional<PaintFilter::CropRect>& crop_rect) { - uint32_t lighting_type_int = 0; + PaintFilter::LightingType lighting_type; SkPoint3 direction = SkPoint3::Make(0.f, 0.f, 0.f); SkColor light_color = SK_ColorBLACK; SkScalar surface_scale = 0.f; @@ -1259,21 +1226,15 @@ void PaintOpReader::ReadLightingDistantPaintFilter( SkScalar shininess = 0.f; sk_sp<PaintFilter> input; - Read(&lighting_type_int); + ReadEnum(&lighting_type); ReadSimple(&direction); Read(&light_color); Read(&surface_scale); Read(&kconstant); Read(&shininess); Read(&input); - if (lighting_type_int > - static_cast<uint32_t>(PaintFilter::LightingType::kMaxLightingType)) { - SetInvalid(); - } if (!valid_) return; - PaintFilter::LightingType lighting_type = - static_cast<PaintFilter::LightingType>(lighting_type_int); filter->reset(new LightingDistantPaintFilter( lighting_type, direction, light_color, surface_scale, kconstant, shininess, std::move(input), base::OptionalOrNullptr(crop_rect))); @@ -1282,7 +1243,7 @@ void PaintOpReader::ReadLightingDistantPaintFilter( void PaintOpReader::ReadLightingPointPaintFilter( sk_sp<PaintFilter>* filter, const base::Optional<PaintFilter::CropRect>& crop_rect) { - uint32_t lighting_type_int = 0; + PaintFilter::LightingType lighting_type; SkPoint3 location = SkPoint3::Make(0.f, 0.f, 0.f); SkColor light_color = SK_ColorBLACK; SkScalar surface_scale = 0.f; @@ -1290,21 +1251,15 @@ void PaintOpReader::ReadLightingPointPaintFilter( SkScalar shininess = 0.f; sk_sp<PaintFilter> input; - Read(&lighting_type_int); + ReadEnum(&lighting_type); ReadSimple(&location); Read(&light_color); Read(&surface_scale); Read(&kconstant); Read(&shininess); Read(&input); - if (lighting_type_int > - static_cast<uint32_t>(PaintFilter::LightingType::kMaxLightingType)) { - SetInvalid(); - } if (!valid_) return; - PaintFilter::LightingType lighting_type = - static_cast<PaintFilter::LightingType>(lighting_type_int); filter->reset(new LightingPointPaintFilter( lighting_type, location, light_color, surface_scale, kconstant, shininess, std::move(input), base::OptionalOrNullptr(crop_rect))); @@ -1313,7 +1268,7 @@ void PaintOpReader::ReadLightingPointPaintFilter( void PaintOpReader::ReadLightingSpotPaintFilter( sk_sp<PaintFilter>* filter, const base::Optional<PaintFilter::CropRect>& crop_rect) { - uint32_t lighting_type_int = 0; + PaintFilter::LightingType lighting_type; SkPoint3 location = SkPoint3::Make(0.f, 0.f, 0.f); SkPoint3 target = SkPoint3::Make(0.f, 0.f, 0.f); SkScalar specular_exponent = 0.f; @@ -1324,7 +1279,7 @@ void PaintOpReader::ReadLightingSpotPaintFilter( SkScalar shininess = 0.f; sk_sp<PaintFilter> input; - Read(&lighting_type_int); + ReadEnum(&lighting_type); ReadSimple(&location); ReadSimple(&target); Read(&specular_exponent); @@ -1335,14 +1290,8 @@ void PaintOpReader::ReadLightingSpotPaintFilter( Read(&shininess); Read(&input); - if (lighting_type_int > - static_cast<uint32_t>(PaintFilter::LightingType::kMaxLightingType)) { - SetInvalid(); - } if (!valid_) return; - PaintFilter::LightingType lighting_type = - static_cast<PaintFilter::LightingType>(lighting_type_int); filter->reset(new LightingSpotPaintFilter( lighting_type, location, target, specular_exponent, cutoff_angle, light_color, surface_scale, kconstant, shininess, std::move(input), diff --git a/chromium/cc/paint/paint_op_reader.h b/chromium/cc/paint/paint_op_reader.h index c9ab0842ccc..fc8cda79bf0 100644 --- a/chromium/cc/paint/paint_op_reader.h +++ b/chromium/cc/paint/paint_op_reader.h @@ -71,49 +71,47 @@ class CC_PAINT_EXPORT PaintOpReader { void Read(sk_sp<PaintFilter>* filter); void Read(sk_sp<PaintShader>* shader); void Read(SkMatrix* matrix); - void Read(SkColorType* color_type); + void Read(SkM44* matrix); void Read(SkImageInfo* info); + void Read(SkSamplingOptions* sampling); void Read(sk_sp<SkColorSpace>* color_space); void Read(SkYUVColorSpace* yuv_color_space); + void Read(SkYUVAInfo::PlaneConfig* plane_config); + void Read(SkYUVAInfo::Subsampling* subsampling); void Read(gpu::Mailbox* mailbox); #if !defined(OS_ANDROID) void Read(scoped_refptr<SkottieWrapper>* skottie); #endif - void Read(SkClipOp* op) { - uint8_t value = 0u; - Read(&value); - *op = static_cast<SkClipOp>(value); - } + void Read(SkClipOp* op) { ReadEnum<SkClipOp, SkClipOp::kMax_EnumValue>(op); } void Read(PaintCanvas::AnnotationType* type) { - uint8_t value = 0u; - Read(&value); - *type = static_cast<PaintCanvas::AnnotationType>(value); + ReadEnum<PaintCanvas::AnnotationType, + PaintCanvas::AnnotationType::LINK_TO_DESTINATION>(type); } void Read(SkCanvas::SrcRectConstraint* constraint) { - uint8_t value = 0u; - Read(&value); - *constraint = static_cast<SkCanvas::SrcRectConstraint>(value); + ReadEnum<SkCanvas::SrcRectConstraint, SkCanvas::kFast_SrcRectConstraint>( + constraint); + } + void Read(SkColorType* color_type) { + ReadEnum<SkColorType, kLastEnum_SkColorType>(color_type); } void Read(SkFilterQuality* quality) { - uint8_t value = 0u; - Read(&value); - if (value > static_cast<uint8_t>(kLast_SkFilterQuality)) { - SetInvalid(); - return; - } - *quality = static_cast<SkFilterQuality>(value); + ReadEnum<SkFilterQuality, kLast_SkFilterQuality>(quality); } void Read(SkBlendMode* blend_mode) { - uint8_t value = 0u; - Read(&value); - if (value > static_cast<uint8_t>(SkBlendMode::kLastMode)) { - SetInvalid(); - return; - } - *blend_mode = static_cast<SkBlendMode>(value); + ReadEnum<SkBlendMode, SkBlendMode::kLastMode>(blend_mode); + } + void Read(SkTileMode* tile_mode) { + ReadEnum<SkTileMode, SkTileMode::kLastTileMode>(tile_mode); } + void Read(SkFilterMode* filter_mode) { + ReadEnum<SkFilterMode, SkFilterMode::kLast>(filter_mode); + } + void Read(SkMipmapMode* mipmap_mode) { + ReadEnum<SkMipmapMode, SkMipmapMode::kLast>(mipmap_mode); + } + void Read(bool* data) { uint8_t value = 0u; Read(&value); @@ -135,6 +133,19 @@ class CC_PAINT_EXPORT PaintOpReader { template <typename T> void ReadFlattenable(sk_sp<T>* val); + template <typename Enum, Enum kMaxValue = Enum::kMaxValue> + void ReadEnum(Enum* enum_value) { + static_assert(static_cast<unsigned>(kMaxValue) <= 255, + "Max value must fit in uint8_t"); + uint8_t value = 0u; + Read(&value); + if (value > static_cast<uint8_t>(kMaxValue)) { + SetInvalid(); + return; + } + *enum_value = static_cast<Enum>(value); + } + void SetInvalid(bool skip_crash_dump = false); // The main entry point is Read(sk_sp<PaintFilter>* filter) which calls one of diff --git a/chromium/cc/paint/paint_op_writer.cc b/chromium/cc/paint/paint_op_writer.cc index e6b9d93b630..32aae4f2ce9 100644 --- a/chromium/cc/paint/paint_op_writer.cc +++ b/chromium/cc/paint/paint_op_writer.cc @@ -210,7 +210,7 @@ void PaintOpWriter::Write(const PaintFlags& flags) { WriteSimple(flags.color_); Write(flags.width_); Write(flags.miter_limit_); - WriteSimple(flags.blend_mode_); + Write(flags.blend_mode_); WriteSimple(flags.bitfields_uint_); WriteFlattenable(flags.path_effect_.get()); @@ -352,6 +352,17 @@ void PaintOpWriter::Write(const sk_sp<SkData>& data) { } } +void PaintOpWriter::Write(const SkSamplingOptions& sampling) { + Write(sampling.useCubic); + if (sampling.useCubic) { + Write(sampling.cubic.B); + Write(sampling.cubic.C); + } else { + Write(sampling.filter); + Write(sampling.mipmap); + } +} + void PaintOpWriter::Write(const SkColorSpace* color_space) { if (!color_space) { WriteSize(static_cast<size_t>(0)); @@ -449,6 +460,10 @@ void PaintOpWriter::Write(SkMatrix matrix) { WriteSimple(matrix); } +void PaintOpWriter::Write(const SkM44& matrix) { + WriteSimple(matrix); +} + void PaintOpWriter::Write(const PaintShader* shader, SkFilterQuality quality) { sk_sp<PaintShader> transformed_shader; uint32_t paint_image_transfer_cache_id = kInvalidImageTransferCacheEntryId; @@ -476,10 +491,8 @@ void PaintOpWriter::Write(const PaintShader* shader, SkFilterQuality quality) { WriteSimple(shader->flags_); WriteSimple(shader->end_radius_); WriteSimple(shader->start_radius_); - // SkTileMode does not have an explicitly defined backing type, so - // write a consistently sized value. - Write(static_cast<int32_t>(shader->tx_)); - Write(static_cast<int32_t>(shader->ty_)); + Write(shader->tx_); + Write(shader->ty_); WriteSimple(shader->fallback_color_); WriteSimple(shader->scaling_behavior_); if (shader->local_matrix_) { @@ -534,14 +547,18 @@ void PaintOpWriter::Write(const PaintShader* shader, SkFilterQuality quality) { // using other fields. } -void PaintOpWriter::Write(SkColorType color_type) { - WriteSimple(static_cast<uint32_t>(color_type)); -} - void PaintOpWriter::Write(SkYUVColorSpace yuv_color_space) { WriteSimple(static_cast<uint32_t>(yuv_color_space)); } +void PaintOpWriter::Write(SkYUVAInfo::PlaneConfig plane_config) { + WriteSimple(static_cast<uint32_t>(plane_config)); +} + +void PaintOpWriter::Write(SkYUVAInfo::Subsampling subsampling) { + WriteSimple(static_cast<uint32_t>(subsampling)); +} + void PaintOpWriter::WriteData(size_t bytes, const void* input) { EnsureBytes(bytes); if (!valid_) @@ -575,15 +592,14 @@ void PaintOpWriter::AlignMemory(size_t alignment) { void PaintOpWriter::Write(const PaintFilter* filter) { if (!filter) { - WriteSimple(static_cast<uint32_t>(PaintFilter::Type::kNullFilter)); + WriteEnum(PaintFilter::Type::kNullFilter); return; } - WriteSimple(static_cast<uint32_t>(filter->type())); + WriteEnum(filter->type()); auto* crop_rect = filter->crop_rect(); WriteSimple(static_cast<uint32_t>(!!crop_rect)); if (crop_rect) { - WriteSimple(crop_rect->flags()); - WriteSimple(crop_rect->rect()); + WriteSimple(*crop_rect); } if (!valid_) @@ -671,7 +687,7 @@ void PaintOpWriter::Write(const ColorFilterPaintFilter& filter) { void PaintOpWriter::Write(const BlurPaintFilter& filter) { WriteSimple(filter.sigma_x()); WriteSimple(filter.sigma_y()); - WriteSimple(filter.tile_mode()); + Write(filter.tile_mode()); Write(filter.input().get()); } @@ -681,7 +697,7 @@ void PaintOpWriter::Write(const DropShadowPaintFilter& filter) { WriteSimple(filter.sigma_x()); WriteSimple(filter.sigma_y()); WriteSimple(filter.color()); - WriteSimple(filter.shadow_mode()); + WriteEnum(filter.shadow_mode()); Write(filter.input().get()); } @@ -704,7 +720,7 @@ void PaintOpWriter::Write(const AlphaThresholdPaintFilter& filter) { } void PaintOpWriter::Write(const XfermodePaintFilter& filter) { - WriteSimple(static_cast<uint32_t>(filter.blend_mode())); + Write(filter.blend_mode()); Write(filter.background().get()); Write(filter.foreground().get()); } @@ -728,14 +744,14 @@ void PaintOpWriter::Write(const MatrixConvolutionPaintFilter& filter) { WriteSimple(filter.gain()); WriteSimple(filter.bias()); WriteSimple(filter.kernel_offset()); - WriteSimple(static_cast<uint32_t>(filter.tile_mode())); + Write(filter.tile_mode()); WriteSimple(filter.convolve_alpha()); Write(filter.input().get()); } void PaintOpWriter::Write(const DisplacementMapEffectPaintFilter& filter) { - WriteSimple(static_cast<uint32_t>(filter.channel_x())); - WriteSimple(static_cast<uint32_t>(filter.channel_y())); + WriteEnum(filter.channel_x()); + WriteEnum(filter.channel_y()); WriteSimple(filter.scale()); Write(filter.displacement().get()); Write(filter.color().get()); @@ -783,7 +799,7 @@ void PaintOpWriter::Write(const MergePaintFilter& filter) { } void PaintOpWriter::Write(const MorphologyPaintFilter& filter) { - WriteSimple(filter.morph_type()); + WriteEnum(filter.morph_type()); WriteSimple(filter.radius_x()); WriteSimple(filter.radius_y()); Write(filter.input().get()); @@ -802,7 +818,7 @@ void PaintOpWriter::Write(const TilePaintFilter& filter) { } void PaintOpWriter::Write(const TurbulencePaintFilter& filter) { - WriteSimple(filter.turbulence_type()); + WriteEnum(filter.turbulence_type()); WriteSimple(filter.base_frequency_x()); WriteSimple(filter.base_frequency_y()); WriteSimple(filter.num_octaves()); @@ -816,12 +832,12 @@ void PaintOpWriter::Write(const PaintFlagsPaintFilter& filter) { void PaintOpWriter::Write(const MatrixPaintFilter& filter) { Write(filter.matrix()); - WriteSimple(filter.filter_quality()); + Write(filter.filter_quality()); Write(filter.input().get()); } void PaintOpWriter::Write(const LightingDistantPaintFilter& filter) { - WriteSimple(filter.lighting_type()); + WriteEnum(filter.lighting_type()); WriteSimple(filter.direction()); WriteSimple(filter.light_color()); WriteSimple(filter.surface_scale()); @@ -831,7 +847,7 @@ void PaintOpWriter::Write(const LightingDistantPaintFilter& filter) { } void PaintOpWriter::Write(const LightingPointPaintFilter& filter) { - WriteSimple(filter.lighting_type()); + WriteEnum(filter.lighting_type()); WriteSimple(filter.location()); WriteSimple(filter.light_color()); WriteSimple(filter.surface_scale()); @@ -841,7 +857,7 @@ void PaintOpWriter::Write(const LightingPointPaintFilter& filter) { } void PaintOpWriter::Write(const LightingSpotPaintFilter& filter) { - WriteSimple(filter.lighting_type()); + WriteEnum(filter.lighting_type()); WriteSimple(filter.location()); WriteSimple(filter.target()); WriteSimple(filter.specular_exponent()); diff --git a/chromium/cc/paint/paint_op_writer.h b/chromium/cc/paint/paint_op_writer.h index d00eeea1d36..682ba1c8dff 100644 --- a/chromium/cc/paint/paint_op_writer.h +++ b/chromium/cc/paint/paint_op_writer.h @@ -13,6 +13,7 @@ #include "cc/paint/paint_filter.h" #include "cc/paint/paint_op_buffer_serializer.h" #include "third_party/skia/include/core/SkImageInfo.h" +#include "third_party/skia/include/core/SkYUVAInfo.h" struct SkRect; struct SkIRect; @@ -52,6 +53,7 @@ class CC_PAINT_EXPORT PaintOpWriter { void Write(SkScalar data); void Write(SkMatrix data); + void Write(const SkM44& data); void Write(uint8_t data); void Write(uint32_t data); void Write(uint64_t data); @@ -64,26 +66,25 @@ class CC_PAINT_EXPORT PaintOpWriter { void Write(const PaintFlags& flags); void Write(const sk_sp<SkData>& data); void Write(const SkColorSpace* data); + void Write(const SkSamplingOptions&); void Write(const PaintShader* shader, SkFilterQuality quality); void Write(const PaintFilter* filter); void Write(const sk_sp<SkTextBlob>& blob); - void Write(SkColorType color_type); void Write(SkYUVColorSpace yuv_color_space); + void Write(SkYUVAInfo::PlaneConfig plane_config); + void Write(SkYUVAInfo::Subsampling subsampling); void Write(const gpu::Mailbox& mailbox); - void Write(SkClipOp op) { Write(static_cast<uint8_t>(op)); } - void Write(PaintCanvas::AnnotationType type) { - Write(static_cast<uint8_t>(type)); - } - void Write(SkCanvas::SrcRectConstraint constraint) { - Write(static_cast<uint8_t>(constraint)); - } - void Write(SkFilterQuality filter_quality) { - Write(static_cast<uint8_t>(filter_quality)); - } - void Write(SkBlendMode blend_mode) { - Write(static_cast<uint8_t>(blend_mode)); - } + void Write(SkClipOp op) { WriteEnum(op); } + void Write(PaintCanvas::AnnotationType type) { WriteEnum(type); } + void Write(SkCanvas::SrcRectConstraint constraint) { WriteEnum(constraint); } + void Write(SkColorType color_type) { WriteEnum(color_type); } + void Write(SkFilterQuality filter_quality) { WriteEnum(filter_quality); } + void Write(SkBlendMode blend_mode) { WriteEnum(blend_mode); } + void Write(SkTileMode tile_mode) { WriteEnum(tile_mode); } + void Write(SkFilterMode filter_mode) { WriteEnum(filter_mode); } + void Write(SkMipmapMode mipmap_mode) { WriteEnum(mipmap_mode); } + void Write(bool data) { Write(static_cast<uint8_t>(data)); } // Aligns the memory to the given alignment. @@ -123,6 +124,11 @@ class CC_PAINT_EXPORT PaintOpWriter { void WriteFlattenable(const SkFlattenable* val); + template <typename Enum> + void WriteEnum(Enum value) { + Write(base::checked_cast<uint8_t>(value)); + } + // The main entry point is Write(const PaintFilter* filter) which casts the // filter and calls one of the following functions. void Write(const ColorFilterPaintFilter& filter); diff --git a/chromium/cc/paint/paint_shader_unittest.cc b/chromium/cc/paint/paint_shader_unittest.cc index e7e2223639d..4a33b87ffc2 100644 --- a/chromium/cc/paint/paint_shader_unittest.cc +++ b/chromium/cc/paint/paint_shader_unittest.cc @@ -48,7 +48,7 @@ class MockImageProvider : public ImageProvider { sk_sp<SkImage> image = SkImage::MakeFromBitmap(bitmap); return ScopedResult(DecodedDrawImage(image, nullptr, SkSize::MakeEmpty(), SkSize::Make(1.0f, 1.0f), - draw_image.filter_quality())); + draw_image.filter_quality(), true)); } const DrawImage& draw_image() const { return draw_image_; } @@ -84,7 +84,7 @@ TEST(PaintShaderTest, DecodePaintRecord) { .set_paint_image_generator(generator) .TakePaintImage(); - record->push<DrawImageOp>(paint_image, 0.f, 0.f, nullptr); + record->push<DrawImageOp>(paint_image, 0.f, 0.f); SkMatrix local_matrix = SkMatrix::Scale(0.5f, 0.5f); auto record_shader = PaintShader::MakePaintRecord( record, SkRect::MakeWH(100, 100), SkTileMode::kClamp, SkTileMode::kClamp, diff --git a/chromium/cc/paint/paint_worklet_input.cc b/chromium/cc/paint/paint_worklet_input.cc index 2e156bf1cb3..d0e05799183 100644 --- a/chromium/cc/paint/paint_worklet_input.cc +++ b/chromium/cc/paint/paint_worklet_input.cc @@ -4,10 +4,52 @@ #include "cc/paint/paint_worklet_input.h" -#include <utility> - namespace cc { +PaintWorkletInput::PropertyKey::PropertyKey( + const std::string& custom_property_name, + ElementId element_id) + : custom_property_name(custom_property_name), element_id(element_id) {} + +PaintWorkletInput::PropertyKey::PropertyKey( + NativePropertyType native_property_type, + ElementId element_id) + : native_property_type(native_property_type), element_id(element_id) {} + +PaintWorkletInput::PropertyKey::PropertyKey(const PropertyKey& other) = default; + +PaintWorkletInput::PropertyKey::~PropertyKey() = default; + +bool PaintWorkletInput::PropertyKey::operator==( + const PropertyKey& other) const { + return custom_property_name == other.custom_property_name && + native_property_type == other.native_property_type && + element_id == other.element_id; +} + +bool PaintWorkletInput::PropertyKey::operator!=( + const PropertyKey& other) const { + return !(*this == other); +} + +bool PaintWorkletInput::PropertyKey::operator<(const PropertyKey& other) const { + if (custom_property_name.has_value() && + !other.custom_property_name.has_value()) + return true; + if (!custom_property_name.has_value() && + other.custom_property_name.has_value()) + return false; + if (custom_property_name.has_value() && + other.custom_property_name.has_value()) { + if (custom_property_name.value() == other.custom_property_name.value()) + return element_id < other.element_id; + return custom_property_name.value() < other.custom_property_name.value(); + } + if (native_property_type.value() == other.native_property_type.value()) + return element_id < other.element_id; + return native_property_type.value() < other.native_property_type.value(); +} + PaintWorkletInput::PropertyValue::PropertyValue() = default; PaintWorkletInput::PropertyValue::PropertyValue(float value) diff --git a/chromium/cc/paint/paint_worklet_input.h b/chromium/cc/paint/paint_worklet_input.h index 0f8eaec7602..18dbc8b7116 100644 --- a/chromium/cc/paint/paint_worklet_input.h +++ b/chromium/cc/paint/paint_worklet_input.h @@ -5,6 +5,10 @@ #ifndef CC_PAINT_PAINT_WORKLET_INPUT_H_ #define CC_PAINT_PAINT_WORKLET_INPUT_H_ +#include <string> +#include <utility> +#include <vector> + #include "base/containers/flat_map.h" #include "base/memory/ref_counted.h" #include "base/optional.h" @@ -23,15 +27,38 @@ using PaintRecord = PaintOpBuffer; class CC_PAINT_EXPORT PaintWorkletInput : public base::RefCountedThreadSafe<PaintWorkletInput> { public: + enum class NativePropertyType { + kBackgroundColor, + kInvalid, + }; // Uniquely identifies a property from the animation system, so that a // PaintWorkletInput can specify the properties it depends on to be painted // (and for which it must be repainted if their values change). // - // PropertyKey is designed to support both native and custom properties. The - // same ElementId will be produced for all custom properties for a given - // element. As such we require the custom property name as an additional key - // to uniquely identify custom properties. - using PropertyKey = std::pair<std::string, ElementId>; + // PropertyKey is designed to support both native and custom properties. + // 1. Custom properties: The same ElementId will be produced for all custom + // properties for a given element. As such we require the custom property + // name as an additional key to uniquely identify custom properties. + // 2. Native properties: When fetching the current value of a native + // property from property tree, we need the ElementId, plus knowing which + // tree to fetch the value from, and that's why we need the + // |native_property_type|. + // One property key should have either |custom_property_name| or + // |native_property_type|, and should never have both or neither. + struct CC_PAINT_EXPORT PropertyKey { + PropertyKey(const std::string& custom_property_name, ElementId element_id); + PropertyKey(NativePropertyType native_property_type, ElementId element_id); + PropertyKey(const PropertyKey&); + ~PropertyKey(); + + bool operator==(const PropertyKey& other) const; + bool operator!=(const PropertyKey& other) const; + bool operator<(const PropertyKey&) const; + + base::Optional<std::string> custom_property_name; + base::Optional<NativePropertyType> native_property_type; + ElementId element_id; + }; // A structure that can hold either a float or color type value, depending // on the type of custom property. Only one of |float_val| and |color_val| diff --git a/chromium/cc/paint/paint_worklet_input_unittest.cc b/chromium/cc/paint/paint_worklet_input_unittest.cc new file mode 100644 index 00000000000..37965a40a05 --- /dev/null +++ b/chromium/cc/paint/paint_worklet_input_unittest.cc @@ -0,0 +1,24 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/paint/paint_worklet_input.h" + +#include "base/containers/flat_set.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace cc { + +TEST(PaintWorkletInputTest, InsertPropertyKeyToFlatSet) { + base::flat_set<PaintWorkletInput::PropertyKey> property_keys; + + PaintWorkletInput::PropertyKey key1("foo", ElementId(128u)); + property_keys.insert(key1); + EXPECT_EQ(property_keys.size(), 1u); + + PaintWorkletInput::PropertyKey key2("foo", ElementId(130u)); + property_keys.insert(key2); + EXPECT_EQ(property_keys.size(), 2u); +} + +} // namespace cc diff --git a/chromium/cc/paint/paint_worklet_job.h b/chromium/cc/paint/paint_worklet_job.h index e83c0629761..ccba901196d 100644 --- a/chromium/cc/paint/paint_worklet_job.h +++ b/chromium/cc/paint/paint_worklet_job.h @@ -5,6 +5,8 @@ #ifndef CC_PAINT_PAINT_WORKLET_JOB_H_ #define CC_PAINT_PAINT_WORKLET_JOB_H_ +#include <vector> + #include "base/containers/flat_map.h" #include "base/memory/scoped_refptr.h" #include "cc/paint/paint_export.h" @@ -24,7 +26,8 @@ class CC_PAINT_EXPORT PaintWorkletJob { // For a custom property, its name is sufficient to uniquely identify it. // TODO(xidachen): support more property types such as color. using AnimatedPropertyValues = - base::flat_map<std::string, PaintWorkletInput::PropertyValue>; + base::flat_map<PaintWorkletInput::PropertyKey, + PaintWorkletInput::PropertyValue>; PaintWorkletJob(int layer_id, scoped_refptr<const PaintWorkletInput> input, AnimatedPropertyValues animated_property_values); diff --git a/chromium/cc/paint/record_paint_canvas.cc b/chromium/cc/paint/record_paint_canvas.cc index e8758c76972..d4870865146 100644 --- a/chromium/cc/paint/record_paint_canvas.cc +++ b/chromium/cc/paint/record_paint_canvas.cc @@ -108,11 +108,23 @@ void RecordPaintCanvas::rotate(SkScalar degrees) { } void RecordPaintCanvas::concat(const SkMatrix& matrix) { + SkM44 m = SkM44(matrix); + list_->push<ConcatOp>(m); + GetCanvas()->concat(m); +} + +void RecordPaintCanvas::concat(const SkM44& matrix) { list_->push<ConcatOp>(matrix); GetCanvas()->concat(matrix); } void RecordPaintCanvas::setMatrix(const SkMatrix& matrix) { + SkM44 m = SkM44(matrix); + list_->push<SetMatrixOp>(m); + GetCanvas()->setMatrix(m); +} + +void RecordPaintCanvas::setMatrix(const SkM44& matrix) { list_->push<SetMatrixOp>(matrix); GetCanvas()->setMatrix(matrix); } @@ -252,17 +264,19 @@ void RecordPaintCanvas::drawPath(const SkPath& path, const PaintFlags& flags) { void RecordPaintCanvas::drawImage(const PaintImage& image, SkScalar left, SkScalar top, + const SkSamplingOptions& sampling, const PaintFlags* flags) { DCHECK(!image.IsPaintWorklet()); - list_->push<DrawImageOp>(image, left, top, flags); + list_->push<DrawImageOp>(image, left, top, sampling, flags); } void RecordPaintCanvas::drawImageRect(const PaintImage& image, const SkRect& src, const SkRect& dst, + const SkSamplingOptions& sampling, const PaintFlags* flags, SkCanvas::SrcRectConstraint constraint) { - list_->push<DrawImageRectOp>(image, src, dst, flags, constraint); + list_->push<DrawImageRectOp>(image, src, dst, sampling, flags, constraint); } void RecordPaintCanvas::drawSkottie(scoped_refptr<SkottieWrapper> skottie, diff --git a/chromium/cc/paint/record_paint_canvas.h b/chromium/cc/paint/record_paint_canvas.h index 4917088a76e..b08c8c8e064 100644 --- a/chromium/cc/paint/record_paint_canvas.h +++ b/chromium/cc/paint/record_paint_canvas.h @@ -46,8 +46,12 @@ class CC_PAINT_EXPORT RecordPaintCanvas : public PaintCanvas { void translate(SkScalar dx, SkScalar dy) override; void scale(SkScalar sx, SkScalar sy) override; void rotate(SkScalar degrees) override; + // TODO(crbug.com/1167153): The concat and setMatrix methods that take an + // SkMatrix should be removed in favor of the SkM44 versions. void concat(const SkMatrix& matrix) override; void setMatrix(const SkMatrix& matrix) override; + void concat(const SkM44& matrix) override; + void setMatrix(const SkM44& matrix) override; void clipRect(const SkRect& rect, SkClipOp op, bool antialias) override; void clipRRect(const SkRRect& rrect, SkClipOp op, bool antialias) override; @@ -79,10 +83,12 @@ class CC_PAINT_EXPORT RecordPaintCanvas : public PaintCanvas { void drawImage(const PaintImage& image, SkScalar left, SkScalar top, + const SkSamplingOptions&, const PaintFlags* flags) override; void drawImageRect(const PaintImage& image, const SkRect& src, const SkRect& dst, + const SkSamplingOptions&, const PaintFlags* flags, SkCanvas::SrcRectConstraint constraint) override; void drawSkottie(scoped_refptr<SkottieWrapper> skottie, diff --git a/chromium/cc/paint/render_surface_filters.cc b/chromium/cc/paint/render_surface_filters.cc index 913cd402028..d8b243ee1a1 100644 --- a/chromium/cc/paint/render_surface_filters.cc +++ b/chromium/cc/paint/render_surface_filters.cc @@ -12,14 +12,7 @@ #include "cc/paint/filter_operations.h" #include "cc/paint/paint_filter.h" #include "third_party/skia/include/core/SkColorFilter.h" -#include "third_party/skia/include/core/SkImageFilter.h" #include "third_party/skia/include/core/SkRegion.h" -#include "third_party/skia/include/effects/SkAlphaThresholdFilter.h" -#include "third_party/skia/include/effects/SkColorFilterImageFilter.h" -#include "third_party/skia/include/effects/SkColorMatrixFilter.h" -#include "third_party/skia/include/effects/SkComposeImageFilter.h" -#include "third_party/skia/include/effects/SkDropShadowImageFilter.h" -#include "third_party/skia/include/effects/SkMagnifierImageFilter.h" #include "ui/gfx/geometry/size_f.h" #include "ui/gfx/skia_util.h" @@ -209,7 +202,7 @@ sk_sp<PaintFilter> RenderSurfaceFilters::BuildImageFilter( SkIntToScalar(op.drop_shadow_offset().y()), SkIntToScalar(op.amount()), SkIntToScalar(op.amount()), op.drop_shadow_color(), - SkDropShadowImageFilter::kDrawShadowAndForeground_ShadowMode, + DropShadowPaintFilter::ShadowMode::kDrawShadowAndForeground, std::move(image_filter)); break; case FilterOperation::COLOR_MATRIX: diff --git a/chromium/cc/paint/scoped_raster_flags_unittest.cc b/chromium/cc/paint/scoped_raster_flags_unittest.cc index bfd32db2a49..1b2439b9fdc 100644 --- a/chromium/cc/paint/scoped_raster_flags_unittest.cc +++ b/chromium/cc/paint/scoped_raster_flags_unittest.cc @@ -5,7 +5,6 @@ #include "cc/paint/scoped_raster_flags.h" #include <utility> - #include "base/bind.h" #include "base/callback.h" #include "cc/paint/paint_op_buffer.h" @@ -31,7 +30,8 @@ class MockImageProvider : public ImageProvider { return ScopedResult( DecodedDrawImage(image, nullptr, SkSize::MakeEmpty(), - SkSize::Make(1.0f, 1.0f), draw_image.filter_quality()), + SkSize::Make(1.0f, 1.0f), draw_image.filter_quality(), + true), base::BindOnce(&MockImageProvider::UnrefImage, base::Unretained(this))); } @@ -82,11 +82,11 @@ TEST(ScopedRasterFlagsTest, DecodePaintWorkletImageShader) { TEST(ScopedRasterFlagsTest, KeepsDecodesAlive) { auto record = sk_make_sp<PaintOpBuffer>(); record->push<DrawImageOp>(CreateDiscardablePaintImage(gfx::Size(10, 10)), 0.f, - 0.f, nullptr); + 0.f); record->push<DrawImageOp>(CreateDiscardablePaintImage(gfx::Size(10, 10)), 0.f, - 0.f, nullptr); + 0.f); record->push<DrawImageOp>(CreateDiscardablePaintImage(gfx::Size(10, 10)), 0.f, - 0.f, nullptr); + 0.f); auto record_shader = PaintShader::MakePaintRecord( record, SkRect::MakeWH(100, 100), SkTileMode::kClamp, SkTileMode::kClamp, &SkMatrix::I()); diff --git a/chromium/cc/paint/skia_paint_canvas.cc b/chromium/cc/paint/skia_paint_canvas.cc index a9674da95a3..2067070cfe4 100644 --- a/chromium/cc/paint/skia_paint_canvas.cc +++ b/chromium/cc/paint/skia_paint_canvas.cc @@ -93,6 +93,14 @@ void SkiaPaintCanvas::rotate(SkScalar degrees) { canvas_->rotate(degrees); } +void SkiaPaintCanvas::setMatrix(const SkM44& matrix) { + canvas_->setMatrix(matrix); +} + +void SkiaPaintCanvas::concat(const SkM44& matrix) { + canvas_->concat(matrix); +} + void SkiaPaintCanvas::concat(const SkMatrix& matrix) { canvas_->concat(matrix); } @@ -255,6 +263,7 @@ void SkiaPaintCanvas::drawPath(const SkPath& path, const PaintFlags& flags) { void SkiaPaintCanvas::drawImage(const PaintImage& image, SkScalar left, SkScalar top, + const SkSamplingOptions& sampling, const PaintFlags* flags) { DCHECK(!image.IsPaintWorklet()); base::Optional<ScopedRasterFlags> scoped_flags; @@ -266,8 +275,8 @@ void SkiaPaintCanvas::drawImage(const PaintImage& image, } const PaintFlags* raster_flags = scoped_flags ? scoped_flags->flags() : flags; - PlaybackParams params(image_provider_, canvas_->getTotalMatrix()); - DrawImageOp draw_image_op(image, left, top, nullptr); + PlaybackParams params(image_provider_, canvas_->getLocalToDevice()); + DrawImageOp draw_image_op(image, left, top, sampling, nullptr); DrawImageOp::RasterWithFlags(&draw_image_op, raster_flags, canvas_, params); FlushAfterDrawIfNeeded(); } @@ -275,6 +284,7 @@ void SkiaPaintCanvas::drawImage(const PaintImage& image, void SkiaPaintCanvas::drawImageRect(const PaintImage& image, const SkRect& src, const SkRect& dst, + const SkSamplingOptions& sampling, const PaintFlags* flags, SkCanvas::SrcRectConstraint constraint) { base::Optional<ScopedRasterFlags> scoped_flags; @@ -286,8 +296,9 @@ void SkiaPaintCanvas::drawImageRect(const PaintImage& image, } const PaintFlags* raster_flags = scoped_flags ? scoped_flags->flags() : flags; - PlaybackParams params(image_provider_, canvas_->getTotalMatrix()); - DrawImageRectOp draw_image_rect_op(image, src, dst, flags, constraint); + PlaybackParams params(image_provider_, canvas_->getLocalToDevice()); + DrawImageRectOp draw_image_rect_op(image, src, dst, sampling, flags, + constraint); DrawImageRectOp::RasterWithFlags(&draw_image_rect_op, raster_flags, canvas_, params); FlushAfterDrawIfNeeded(); @@ -369,7 +380,7 @@ void SkiaPaintCanvas::drawPicture( ? base::BindRepeating(&SkiaPaintCanvas::FlushAfterDrawIfNeeded, base::Unretained(this)) : PlaybackParams::DidDrawOpCallback(); - PlaybackParams params(image_provider_, canvas_->getTotalMatrix(), + PlaybackParams params(image_provider_, canvas_->getLocalToDevice(), custom_raster_callback, did_draw_op_cb); record->Playback(canvas_, params); } diff --git a/chromium/cc/paint/skia_paint_canvas.h b/chromium/cc/paint/skia_paint_canvas.h index dcc7f46a9a9..ff4f96a0c6a 100644 --- a/chromium/cc/paint/skia_paint_canvas.h +++ b/chromium/cc/paint/skia_paint_canvas.h @@ -70,6 +70,8 @@ class CC_PAINT_EXPORT SkiaPaintCanvas final : public PaintCanvas { void rotate(SkScalar degrees) override; void concat(const SkMatrix& matrix) override; void setMatrix(const SkMatrix& matrix) override; + void concat(const SkM44& matrix) override; + void setMatrix(const SkM44& matrix) override; void clipRect(const SkRect& rect, SkClipOp op, bool do_anti_alias) override; void clipRRect(const SkRRect& rrect, @@ -103,10 +105,12 @@ class CC_PAINT_EXPORT SkiaPaintCanvas final : public PaintCanvas { void drawImage(const PaintImage& image, SkScalar left, SkScalar top, + const SkSamplingOptions&, const PaintFlags* flags) override; void drawImageRect(const PaintImage& image, const SkRect& src, const SkRect& dst, + const SkSamplingOptions&, const PaintFlags* flags, SkCanvas::SrcRectConstraint constraint) override; void drawSkottie(scoped_refptr<SkottieWrapper> skottie, diff --git a/chromium/cc/paint/solid_color_analyzer.cc b/chromium/cc/paint/solid_color_analyzer.cc index 4e3ca84770f..c90cb6ab0eb 100644 --- a/chromium/cc/paint/solid_color_analyzer.cc +++ b/chromium/cc/paint/solid_color_analyzer.cc @@ -93,32 +93,40 @@ bool IsSolidColorPaint(const PaintFlags& flags) { // Returns true if the specified |drawn_shape| will cover the entire canvas // and that the canvas is not clipped (i.e. it covers ALL of the canvas). +// We expect this method to return false most of the time so we take +// conservative early-outs when possible. template <typename T> bool IsFullQuad(const SkCanvas& canvas, const T& drawn_shape) { if (!canvas.isClipRect()) return false; - SkIRect clip_irect; - if (!canvas.getDeviceClipBounds(&clip_irect)) + SkIRect clip_bounds; + if (!canvas.getDeviceClipBounds(&clip_bounds)) return false; // if the clip is smaller than the canvas, we're partly clipped, so abort. - if (!clip_irect.contains(SkIRect::MakeSize(canvas.getBaseLayerSize()))) + if (!clip_bounds.contains(SkIRect::MakeSize(canvas.getBaseLayerSize()))) return false; - const SkMatrix& matrix = canvas.getTotalMatrix(); - // If the transform results in a non-axis aligned - // rect, then be conservative and return false. - if (!matrix.rectStaysRect()) + const SkM44& matrix = canvas.getLocalToDevice(); + // If the transform results in a non-axis aligned rectangle, then be + // conservative and return false. + if (!MathUtil::SkM44Preserves2DAxisAlignment(matrix)) return false; - SkMatrix inverse; + SkM44 inverse; if (!matrix.invert(&inverse)) return false; - SkRect clip_rect = SkRect::Make(clip_irect); - inverse.mapRect(&clip_rect, clip_rect); - return drawn_shape.contains(clip_rect); + // Check that the drawn shape contains the canvas bounds when those bounds + // are transformed into the shape's coordinate space. Since we know the + // transform is axis aligned we only need to test two corners. + SkV4 upper_left = inverse.map(clip_bounds.left(), clip_bounds.top(), 0, 1); + SkV4 lower_right = + inverse.map(clip_bounds.right(), clip_bounds.bottom(), 0, 1); + SkRect transformed_clip_bounds = SkRect::MakeLTRB( + upper_left.x, upper_left.y, lower_right.x, lower_right.y); + return drawn_shape.contains(transformed_clip_bounds); } void CalculateSolidColor(SkColor src_color, @@ -231,12 +239,12 @@ base::Optional<SkColor> SolidColorAnalyzer::DetermineIfSolidColor( struct Frame { Frame(PaintOpBuffer::CompositeIterator iter, - const SkMatrix& original_ctm, + const SkM44& original_ctm, int save_count) : iter(iter), original_ctm(original_ctm), save_count(save_count) {} PaintOpBuffer::CompositeIterator iter; - const SkMatrix original_ctm; + const SkM44 original_ctm; int save_count = 0; }; @@ -249,7 +257,7 @@ base::Optional<SkColor> SolidColorAnalyzer::DetermineIfSolidColor( // constructed. Reserve this to 2, and go from there. stack.reserve(2); stack.emplace_back(PaintOpBuffer::CompositeIterator(buffer, offsets), - canvas.getTotalMatrix(), canvas.getSaveCount()); + canvas.getLocalToDevice(), canvas.getSaveCount()); int num_draw_ops = 0; while (!stack.empty()) { @@ -263,13 +271,13 @@ base::Optional<SkColor> SolidColorAnalyzer::DetermineIfSolidColor( } const PaintOp* op = *frame.iter; - PlaybackParams params(nullptr, frame.original_ctm); + PlaybackParams params(nullptr, SkM44(frame.original_ctm)); switch (op->GetType()) { case PaintOpType::DrawRecord: { const DrawRecordOp* record_op = static_cast<const DrawRecordOp*>(op); stack.emplace_back( PaintOpBuffer::CompositeIterator(record_op->record.get(), nullptr), - canvas.getTotalMatrix(), canvas.getSaveCount()); + canvas.getLocalToDevice(), canvas.getSaveCount()); continue; } diff --git a/chromium/cc/paint/transfer_cache_deserialize_helper.h b/chromium/cc/paint/transfer_cache_deserialize_helper.h index 59b1ca645e2..6c94c515bf1 100644 --- a/chromium/cc/paint/transfer_cache_deserialize_helper.h +++ b/chromium/cc/paint/transfer_cache_deserialize_helper.h @@ -7,6 +7,8 @@ #include <cstdint> +#include <memory> + #include "cc/paint/paint_export.h" #include "cc/paint/transfer_cache_entry.h" @@ -18,7 +20,7 @@ namespace cc { // we need to figure out layering. crbug.com/777622 class CC_PAINT_EXPORT TransferCacheDeserializeHelper { public: - virtual ~TransferCacheDeserializeHelper() {} + virtual ~TransferCacheDeserializeHelper() = default; // Type safe access to an entry in the transfer cache. Returns null if the // entry is missing or of the wrong type. diff --git a/chromium/cc/paint/transfer_cache_entry.h b/chromium/cc/paint/transfer_cache_entry.h index cd209df7315..9b96cd34162 100644 --- a/chromium/cc/paint/transfer_cache_entry.h +++ b/chromium/cc/paint/transfer_cache_entry.h @@ -32,7 +32,7 @@ enum class TransferCacheEntryType : uint32_t { // into raw bytes that can be sent to the service. class CC_PAINT_EXPORT ClientTransferCacheEntry { public: - virtual ~ClientTransferCacheEntry() {} + virtual ~ClientTransferCacheEntry() = default; // Returns the type of this entry. Combined with id, it should form a unique // identifier. @@ -73,7 +73,7 @@ class CC_PAINT_EXPORT ServiceTransferCacheEntry { // Returns true if the entry needs a GrContext during deserialization. static bool UsesGrContext(TransferCacheEntryType type); - virtual ~ServiceTransferCacheEntry() {} + virtual ~ServiceTransferCacheEntry() = default; // Returns the type of this entry. virtual TransferCacheEntryType Type() const = 0; diff --git a/chromium/cc/raster/bitmap_raster_buffer_provider.cc b/chromium/cc/raster/bitmap_raster_buffer_provider.cc index 0e6287c50a7..397fe0867be 100644 --- a/chromium/cc/raster/bitmap_raster_buffer_provider.cc +++ b/chromium/cc/raster/bitmap_raster_buffer_provider.cc @@ -160,8 +160,4 @@ uint64_t BitmapRasterBufferProvider::SetReadyToDrawCallback( void BitmapRasterBufferProvider::Shutdown() {} -bool BitmapRasterBufferProvider::CheckRasterFinishedQueries() { - return false; -} - } // namespace cc diff --git a/chromium/cc/raster/bitmap_raster_buffer_provider.h b/chromium/cc/raster/bitmap_raster_buffer_provider.h index 60a01b00aa3..88a1837acde 100644 --- a/chromium/cc/raster/bitmap_raster_buffer_provider.h +++ b/chromium/cc/raster/bitmap_raster_buffer_provider.h @@ -51,7 +51,6 @@ class CC_EXPORT BitmapRasterBufferProvider : public RasterBufferProvider { base::OnceClosure callback, uint64_t pending_callback_id) const override; void Shutdown() override; - bool CheckRasterFinishedQueries() override; private: std::unique_ptr<base::trace_event::ConvertableToTraceFormat> StateAsValue() diff --git a/chromium/cc/raster/gpu_raster_buffer_provider.cc b/chromium/cc/raster/gpu_raster_buffer_provider.cc index 76ecd0d6366..0f76c1a85c2 100644 --- a/chromium/cc/raster/gpu_raster_buffer_provider.cc +++ b/chromium/cc/raster/gpu_raster_buffer_provider.cc @@ -45,78 +45,6 @@ namespace cc { namespace { -class ScopedSkSurfaceForUnpremultiplyAndDither { - public: - ScopedSkSurfaceForUnpremultiplyAndDither( - viz::RasterContextProvider* context_provider, - sk_sp<SkColorSpace> color_space, - const gfx::Rect& playback_rect, - const gfx::Rect& raster_full_rect, - const gfx::Size& max_tile_size, - GLuint texture_id, - const gfx::Size& texture_size, - bool can_use_lcd_text, - int msaa_sample_count) - : context_provider_(context_provider), - texture_id_(texture_id), - offset_(playback_rect.OffsetFromOrigin() - - raster_full_rect.OffsetFromOrigin()), - size_(playback_rect.size()) { - // Determine the |intermediate_size| to use for our 32-bit texture. If we - // know the max tile size, use that. This prevents GPU cache explosion due - // to using lots of different 32-bit texture sizes. Otherwise just use the - // exact size of the target texture. - gfx::Size intermediate_size; - if (!max_tile_size.IsEmpty()) { - DCHECK_GE(max_tile_size.width(), texture_size.width()); - DCHECK_GE(max_tile_size.height(), texture_size.height()); - intermediate_size = max_tile_size; - } else { - intermediate_size = texture_size; - } - - // Allocate a 32-bit surface for raster. We will copy from that into our - // actual surface in destruction. - SkImageInfo n32Info = SkImageInfo::MakeN32Premul(intermediate_size.width(), - intermediate_size.height(), - std::move(color_space)); - SkSurfaceProps surface_props = - skia::LegacyDisplayGlobals::ComputeSurfaceProps(can_use_lcd_text); - surface_ = SkSurface::MakeRenderTarget( - context_provider->GrContext(), SkBudgeted::kNo, n32Info, - msaa_sample_count, kTopLeft_GrSurfaceOrigin, &surface_props); - } - - ~ScopedSkSurfaceForUnpremultiplyAndDither() { - // In lost-context cases, |surface_| may be null and there's nothing - // meaningful to do here. - if (!surface_) - return; - - GrBackendTexture backend_texture = - surface_->getBackendTexture(SkSurface::kFlushRead_BackendHandleAccess); - if (!backend_texture.isValid()) { - return; - } - GrGLTextureInfo info; - if (!backend_texture.getGLTextureInfo(&info)) { - return; - } - context_provider_->ContextGL()->UnpremultiplyAndDitherCopyCHROMIUM( - info.fID, texture_id_, offset_.x(), offset_.y(), size_.width(), - size_.height()); - } - - SkSurface* surface() { return surface_.get(); } - - private: - viz::RasterContextProvider* context_provider_; - GLuint texture_id_; - gfx::Vector2d offset_; - gfx::Size size_; - sk_sp<SkSurface> surface_; -}; - static void RasterizeSourceOOP( const RasterSource* raster_source, bool resource_has_previous_content, @@ -186,7 +114,6 @@ static void RasterizeSource( const gfx::AxisTransform2d& transform, const RasterSource::PlaybackSettings& playback_settings, viz::RasterContextProvider* context_provider, - bool unpremultiply_and_dither, const gfx::Size& max_tile_size) { gpu::raster::RasterInterface* ri = context_provider->RasterInterface(); if (mailbox->IsZero()) { @@ -211,26 +138,15 @@ static void RasterizeSource( texture_id, GL_SHARED_IMAGE_ACCESS_MODE_READWRITE_CHROMIUM); { ScopedGrContextAccess gr_context_access(context_provider); - base::Optional<viz::ClientResourceProvider::ScopedSkSurface> scoped_surface; - base::Optional<ScopedSkSurfaceForUnpremultiplyAndDither> - scoped_dither_surface; SkSurface* surface; sk_sp<SkColorSpace> sk_color_space = color_space.ToSkColorSpace(); - if (!unpremultiply_and_dither) { - scoped_surface.emplace(context_provider->GrContext(), sk_color_space, - texture_id, texture_target, resource_size, - resource_format, - skia::LegacyDisplayGlobals::ComputeSurfaceProps( - playback_settings.use_lcd_text), - playback_settings.msaa_sample_count); - surface = scoped_surface->surface(); - } else { - scoped_dither_surface.emplace( - context_provider, sk_color_space, playback_rect, raster_full_rect, - max_tile_size, texture_id, resource_size, - playback_settings.use_lcd_text, playback_settings.msaa_sample_count); - surface = scoped_dither_surface->surface(); - } + viz::ClientResourceProvider::ScopedSkSurface scoped_surface( + context_provider->GrContext(), sk_color_space, texture_id, + texture_target, resource_size, resource_format, + skia::LegacyDisplayGlobals::ComputeSurfaceProps( + playback_settings.use_lcd_text), + playback_settings.msaa_sample_count); + surface = scoped_surface.surface(); // Allocating an SkSurface will fail after a lost context. Pretend we // rasterized, as the contents of the resource don't matter anymore. @@ -372,17 +288,18 @@ GpuRasterBufferProvider::GpuRasterBufferProvider( const gfx::Size& max_tile_size, bool unpremultiply_and_dither_low_bit_depth_tiles, bool enable_oop_rasterization, + RasterQueryQueue* const pending_raster_queries, float raster_metric_probability) : compositor_context_provider_(compositor_context_provider), worker_context_provider_(worker_context_provider), use_gpu_memory_buffer_resources_(use_gpu_memory_buffer_resources), tile_format_(tile_format), max_tile_size_(max_tile_size), - unpremultiply_and_dither_low_bit_depth_tiles_( - unpremultiply_and_dither_low_bit_depth_tiles), enable_oop_rasterization_(enable_oop_rasterization), + pending_raster_queries_(pending_raster_queries), random_generator_(static_cast<uint32_t>(base::RandUint64())), bernoulli_distribution_(raster_metric_probability) { + DCHECK(pending_raster_queries); DCHECK(compositor_context_provider); DCHECK(worker_context_provider); } @@ -494,7 +411,7 @@ gpu::SyncToken GpuRasterBufferProvider::PlaybackOnWorkerThread( bool depends_on_at_raster_decodes, bool depends_on_hardware_accelerated_jpeg_candidates, bool depends_on_hardware_accelerated_webp_candidates) { - PendingRasterQuery query; + RasterQuery query; query.depends_on_hardware_accelerated_jpeg_candidates = depends_on_hardware_accelerated_jpeg_candidates; query.depends_on_hardware_accelerated_webp_candidates = @@ -511,13 +428,12 @@ gpu::SyncToken GpuRasterBufferProvider::PlaybackOnWorkerThread( query.raster_buffer_creation_time = raster_buffer_creation_time; // Note that it is important to scope the raster context lock to - // PlaybackOnWorkerThreadInternal and release it before acquiring this lock - // to avoid a deadlock in CheckRasterFinishedQueries which acquires the - // raster context lock while holding this lock. - base::AutoLock hold(pending_raster_queries_lock_); - pending_raster_queries_.push_back(query); + // PlaybackOnWorkerThreadInternal and release it before calling this + // function to avoid a deadlock in + // RasterQueryQueue::CheckRasterFinishedQueries which acquires the raster + // context lock while holding a lock used in the function. + pending_raster_queries_->Append(std::move(query)); } - DCHECK(!query.raster_start_query_id || query.raster_duration_query_id); return raster_finished_token; } @@ -539,7 +455,7 @@ gpu::SyncToken GpuRasterBufferProvider::PlaybackOnWorkerThreadInternal( const RasterSource::PlaybackSettings& playback_settings, const GURL& url, bool depends_on_at_raster_decodes, - PendingRasterQuery* query) { + RasterQuery* query) { viz::RasterContextProvider::ScopedRasterContextLock scoped_context( worker_context_provider_, url.possibly_invalid_spec().c_str()); gpu::raster::RasterInterface* ri = scoped_context.RasterInterface(); @@ -597,7 +513,6 @@ gpu::SyncToken GpuRasterBufferProvider::PlaybackOnWorkerThreadInternal( resource_size, resource_format, color_space, raster_full_rect, playback_rect, transform, playback_settings, worker_context_provider_, - ShouldUnpremultiplyAndDitherResource(resource_format), max_tile_size_); } if (measure_raster_metric) { @@ -612,121 +527,8 @@ gpu::SyncToken GpuRasterBufferProvider::PlaybackOnWorkerThreadInternal( bool GpuRasterBufferProvider::ShouldUnpremultiplyAndDitherResource( viz::ResourceFormat format) const { - switch (format) { - case viz::RGBA_4444: - return unpremultiply_and_dither_low_bit_depth_tiles_; - default: - return false; - } -} - -#define UMA_HISTOGRAM_RASTER_TIME_CUSTOM_MICROSECONDS(name, total_time) \ - UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES( \ - name, total_time, base::TimeDelta::FromMicroseconds(1), \ - base::TimeDelta::FromMilliseconds(100), 100); - -bool GpuRasterBufferProvider::CheckRasterFinishedQueries() { - base::AutoLock hold(pending_raster_queries_lock_); - if (pending_raster_queries_.empty()) - return false; - - viz::RasterContextProvider::ScopedRasterContextLock scoped_context( - worker_context_provider_); - auto* ri = scoped_context.RasterInterface(); - - auto it = pending_raster_queries_.begin(); - while (it != pending_raster_queries_.end()) { - GLuint complete = 0; - ri->GetQueryObjectuivEXT(it->raster_duration_query_id, - GL_QUERY_RESULT_AVAILABLE_NO_FLUSH_CHROMIUM_EXT, - &complete); - if (!complete) - break; - -#if DCHECK_IS_ON() - if (it->raster_start_query_id) { - // We issued the GL_COMMANDS_ISSUED_TIMESTAMP_CHROMIUM query prior to the - // GL_COMMANDS_ISSUED_CHROMIUM query. Therefore, if the result of the - // latter is available, the result of the former should be too. - complete = 0; - ri->GetQueryObjectuivEXT(it->raster_start_query_id, - GL_QUERY_RESULT_AVAILABLE_NO_FLUSH_CHROMIUM_EXT, - &complete); - DCHECK(complete); - } -#endif - - GLuint gpu_raster_duration = 0u; - ri->GetQueryObjectuivEXT(it->raster_duration_query_id, GL_QUERY_RESULT_EXT, - &gpu_raster_duration); - ri->DeleteQueriesEXT(1, &it->raster_duration_query_id); - - base::TimeDelta raster_duration = - it->worker_raster_duration + - base::TimeDelta::FromMicroseconds(gpu_raster_duration); - - // It is safe to use the UMA macros here with runtime generated strings - // because the client name should be initialized once in the process, before - // recording any metrics here. - const char* client_name = GetClientNameForMetrics(); - - if (it->raster_start_query_id) { - GLuint64 gpu_raster_start_time = 0u; - ri->GetQueryObjectui64vEXT(it->raster_start_query_id, GL_QUERY_RESULT_EXT, - &gpu_raster_start_time); - ri->DeleteQueriesEXT(1, &it->raster_start_query_id); - - // The base::checked_cast<int64_t> should not crash as long as the GPU - // process was not compromised: that's because the result of the query - // should have been generated using base::TimeDelta::InMicroseconds() - // there, so the result should fit in an int64_t. - base::TimeDelta raster_scheduling_delay = - base::TimeDelta::FromMicroseconds( - base::checked_cast<int64_t>(gpu_raster_start_time)) - - it->raster_buffer_creation_time.since_origin(); - - // We expect the clock we're using to be monotonic, so we shouldn't get a - // negative scheduling delay. - DCHECK_GE(raster_scheduling_delay.InMicroseconds(), 0u); - UMA_HISTOGRAM_RASTER_TIME_CUSTOM_MICROSECONDS( - base::StringPrintf( - "Renderer4.%s.RasterTaskSchedulingDelayNoAtRasterDecodes.All", - client_name), - raster_scheduling_delay); - if (it->depends_on_hardware_accelerated_jpeg_candidates) { - UMA_HISTOGRAM_RASTER_TIME_CUSTOM_MICROSECONDS( - base::StringPrintf( - "Renderer4.%s.RasterTaskSchedulingDelayNoAtRasterDecodes." - "TilesWithJpegHwDecodeCandidates", - client_name), - raster_scheduling_delay); - } - if (it->depends_on_hardware_accelerated_webp_candidates) { - UMA_HISTOGRAM_RASTER_TIME_CUSTOM_MICROSECONDS( - base::StringPrintf( - "Renderer4.%s.RasterTaskSchedulingDelayNoAtRasterDecodes." - "TilesWithWebPHwDecodeCandidates", - client_name), - raster_scheduling_delay); - } - } - - if (enable_oop_rasterization_) { - UMA_HISTOGRAM_RASTER_TIME_CUSTOM_MICROSECONDS( - base::StringPrintf("Renderer4.%s.RasterTaskTotalDuration.Oop", - client_name), - raster_duration); - } else { - UMA_HISTOGRAM_RASTER_TIME_CUSTOM_MICROSECONDS( - base::StringPrintf("Renderer4.%s.RasterTaskTotalDuration.Gpu", - client_name), - raster_duration); - } - - it = pending_raster_queries_.erase(it); - } - - return pending_raster_queries_.size() > 0u; + // TODO(crbug.com/1151490): Re-enable for OOPR. + return false; } } // namespace cc diff --git a/chromium/cc/raster/gpu_raster_buffer_provider.h b/chromium/cc/raster/gpu_raster_buffer_provider.h index 780f3304f5c..2980c4c3b5c 100644 --- a/chromium/cc/raster/gpu_raster_buffer_provider.h +++ b/chromium/cc/raster/gpu_raster_buffer_provider.h @@ -12,6 +12,7 @@ #include "base/time/time.h" #include "cc/raster/raster_buffer_provider.h" +#include "cc/raster/raster_query_queue.h" #include "gpu/command_buffer/common/sync_token.h" namespace gpu { @@ -38,6 +39,7 @@ class CC_EXPORT GpuRasterBufferProvider : public RasterBufferProvider { const gfx::Size& max_tile_size, bool unpremultiply_and_dither_low_bit_depth_tiles, bool enable_oop_rasterization, + RasterQueryQueue* const pending_raster_queries, float raster_metric_probability = kRasterMetricProbability); GpuRasterBufferProvider(const GpuRasterBufferProvider&) = delete; ~GpuRasterBufferProvider() override; @@ -63,7 +65,6 @@ class CC_EXPORT GpuRasterBufferProvider : public RasterBufferProvider { base::OnceClosure callback, uint64_t pending_callback_id) const override; void Shutdown() override; - bool CheckRasterFinishedQueries() override; gpu::SyncToken PlaybackOnWorkerThread( gpu::Mailbox* mailbox, @@ -138,26 +139,6 @@ class CC_EXPORT GpuRasterBufferProvider : public RasterBufferProvider { base::TimeTicks creation_time_; }; - struct PendingRasterQuery { - // The id for querying the duration in executing the GPU side work. - GLuint raster_duration_query_id = 0u; - - // The duration for executing the work on the raster worker thread. - base::TimeDelta worker_raster_duration; - - // The id for querying the time at which we're about to start issuing raster - // work to the driver. - GLuint raster_start_query_id = 0u; - - // The time at which the raster buffer was created. - base::TimeTicks raster_buffer_creation_time; - - // Whether the raster work depends on candidates for hardware accelerated - // JPEG or WebP decodes. - bool depends_on_hardware_accelerated_jpeg_candidates = false; - bool depends_on_hardware_accelerated_webp_candidates = false; - }; - bool ShouldUnpremultiplyAndDitherResource(viz::ResourceFormat format) const; gpu::SyncToken PlaybackOnWorkerThreadInternal( gpu::Mailbox* mailbox, @@ -176,21 +157,16 @@ class CC_EXPORT GpuRasterBufferProvider : public RasterBufferProvider { const RasterSource::PlaybackSettings& playback_settings, const GURL& url, bool depends_on_at_raster_decodes, - PendingRasterQuery* query); + RasterQuery* query); viz::ContextProvider* const compositor_context_provider_; viz::RasterContextProvider* const worker_context_provider_; const bool use_gpu_memory_buffer_resources_; const viz::ResourceFormat tile_format_; const gfx::Size max_tile_size_; - const bool unpremultiply_and_dither_low_bit_depth_tiles_; const bool enable_oop_rasterization_; - // Note that this lock should never be acquired while holding the raster - // context lock. - base::Lock pending_raster_queries_lock_; - base::circular_deque<PendingRasterQuery> pending_raster_queries_ - GUARDED_BY(pending_raster_queries_lock_); + RasterQueryQueue* const pending_raster_queries_; // Accessed with the worker context lock acquired. std::mt19937 random_generator_; diff --git a/chromium/cc/raster/one_copy_raster_buffer_provider.cc b/chromium/cc/raster/one_copy_raster_buffer_provider.cc index 191a1aa759b..fd09b836dda 100644 --- a/chromium/cc/raster/one_copy_raster_buffer_provider.cc +++ b/chromium/cc/raster/one_copy_raster_buffer_provider.cc @@ -491,8 +491,4 @@ gpu::SyncToken OneCopyRasterBufferProvider::CopyOnWorkerThread( return out_sync_token; } -bool OneCopyRasterBufferProvider::CheckRasterFinishedQueries() { - return false; -} - } // namespace cc diff --git a/chromium/cc/raster/one_copy_raster_buffer_provider.h b/chromium/cc/raster/one_copy_raster_buffer_provider.h index b42952904ba..a5f91e15081 100644 --- a/chromium/cc/raster/one_copy_raster_buffer_provider.h +++ b/chromium/cc/raster/one_copy_raster_buffer_provider.h @@ -66,7 +66,6 @@ class CC_EXPORT OneCopyRasterBufferProvider : public RasterBufferProvider { base::OnceClosure callback, uint64_t pending_callback_id) const override; void Shutdown() override; - bool CheckRasterFinishedQueries() override; // Playback raster source and copy result into |resource|. gpu::SyncToken PlaybackAndCopyOnWorkerThread( diff --git a/chromium/cc/raster/playback_image_provider.cc b/chromium/cc/raster/playback_image_provider.cc index 596166b2745..bb8dc8110c5 100644 --- a/chromium/cc/raster/playback_image_provider.cc +++ b/chromium/cc/raster/playback_image_provider.cc @@ -5,7 +5,6 @@ #include "cc/raster/playback_image_provider.h" #include <utility> - #include "base/bind.h" #include "cc/tiles/image_decode_cache.h" #include "gpu/command_buffer/common/mailbox.h" @@ -66,11 +65,13 @@ ImageProvider::ScopedResult PlaybackImageProvider::GetRasterContent( } else if (settings_->raster_mode == RasterMode::kGpu) { return ScopedResult(DecodedDrawImage( paint_image.GetAcceleratedSkImage(), nullptr, SkSize::Make(0, 0), - SkSize::Make(1.f, 1.f), draw_image.filter_quality())); + SkSize::Make(1.f, 1.f), draw_image.filter_quality(), + true /* is_budgeted */)); } else { return ScopedResult(DecodedDrawImage( paint_image.GetSwSkImage(), nullptr, SkSize::Make(0, 0), - SkSize::Make(1.f, 1.f), draw_image.filter_quality())); + SkSize::Make(1.f, 1.f), draw_image.filter_quality(), + true /* is_budgeted */)); } } diff --git a/chromium/cc/raster/playback_image_provider_unittest.cc b/chromium/cc/raster/playback_image_provider_unittest.cc index 71096682530..9001f7ebb63 100644 --- a/chromium/cc/raster/playback_image_provider_unittest.cc +++ b/chromium/cc/raster/playback_image_provider_unittest.cc @@ -25,7 +25,8 @@ sk_sp<SkImage> CreateRasterImage() { DecodedDrawImage CreateDecode() { return DecodedDrawImage(CreateRasterImage(), nullptr, SkSize::MakeEmpty(), - SkSize::Make(1.0f, 1.0f), kMedium_SkFilterQuality); + SkSize::Make(1.0f, 1.0f), kMedium_SkFilterQuality, + true); } class MockDecodeCache : public StubDecodeCache { diff --git a/chromium/cc/raster/raster_buffer_provider.cc b/chromium/cc/raster/raster_buffer_provider.cc index 49b0f2689a4..465315f763c 100644 --- a/chromium/cc/raster/raster_buffer_provider.cc +++ b/chromium/cc/raster/raster_buffer_provider.cc @@ -38,6 +38,7 @@ bool IsSupportedPlaybackToMemoryFormat(viz::ResourceFormat format) { case viz::RED_8: case viz::LUMINANCE_F16: case viz::R16_EXT: + case viz::RG16_EXT: case viz::BGR_565: case viz::RG_88: case viz::RGBX_8888: @@ -127,7 +128,7 @@ void RasterBufferProvider::PlaybackToMemory( SkPaint paint; paint.setDither(true); paint.setBlendMode(SkBlendMode::kSrc); - surface->draw(dst_canvas.get(), 0, 0, &paint); + surface->draw(dst_canvas.get(), 0, 0, SkSamplingOptions(), &paint); return; } case viz::ETC1: @@ -137,6 +138,7 @@ void RasterBufferProvider::PlaybackToMemory( case viz::RED_8: case viz::LUMINANCE_F16: case viz::R16_EXT: + case viz::RG16_EXT: case viz::BGR_565: case viz::RG_88: case viz::RGBX_8888: diff --git a/chromium/cc/raster/raster_buffer_provider.h b/chromium/cc/raster/raster_buffer_provider.h index 71632ec2fc2..d0da1460aff 100644 --- a/chromium/cc/raster/raster_buffer_provider.h +++ b/chromium/cc/raster/raster_buffer_provider.h @@ -90,14 +90,6 @@ class CC_EXPORT RasterBufferProvider { // Shutdown for doing cleanup. virtual void Shutdown() = 0; - - // Checks whether GPU side queries issued for previous raster work have been - // finished. Note that this will acquire the worker context lock so it can be - // used from any thread. But usage from the compositor thread should be - // avoided to prevent contention with worker threads. - // Returns true if there are pending queries that could not be completed in - // this check. - virtual bool CheckRasterFinishedQueries() = 0; }; } // namespace cc diff --git a/chromium/cc/raster/raster_buffer_provider_perftest.cc b/chromium/cc/raster/raster_buffer_provider_perftest.cc index 8290aa08f19..f4d55dbf180 100644 --- a/chromium/cc/raster/raster_buffer_provider_perftest.cc +++ b/chromium/cc/raster/raster_buffer_provider_perftest.cc @@ -13,6 +13,7 @@ #include "cc/raster/gpu_raster_buffer_provider.h" #include "cc/raster/one_copy_raster_buffer_provider.h" #include "cc/raster/raster_buffer_provider.h" +#include "cc/raster/raster_query_queue.h" #include "cc/raster/synchronous_task_graph_runner.h" #include "cc/raster/zero_copy_raster_buffer_provider.h" #include "cc/resources/resource_pool.h" @@ -354,6 +355,9 @@ class RasterBufferProviderPerfTest public: // Overridden from testing::Test: void SetUp() override { + pending_raster_queries_ = std::make_unique<RasterQueryQueue>( + worker_context_provider_.get(), /*oop_rasterization_enabled=*/false); + switch (GetParam()) { case RASTER_BUFFER_PROVIDER_TYPE_ZERO_COPY: Create3dResourceProvider(); @@ -374,7 +378,8 @@ class RasterBufferProviderPerfTest Create3dResourceProvider(); raster_buffer_provider_ = std::make_unique<GpuRasterBufferProvider>( compositor_context_provider_.get(), worker_context_provider_.get(), - false, viz::RGBA_8888, gfx::Size(), true, false); + false, viz::RGBA_8888, gfx::Size(), true, false, + pending_raster_queries_.get()); break; case RASTER_BUFFER_PROVIDER_TYPE_BITMAP: CreateSoftwareResourceProvider(); @@ -553,6 +558,7 @@ class RasterBufferProviderPerfTest std::unique_ptr<TileTaskManager> tile_task_manager_; std::unique_ptr<RasterBufferProvider> raster_buffer_provider_; viz::TestGpuMemoryBufferManager gpu_memory_buffer_manager_; + std::unique_ptr<RasterQueryQueue> pending_raster_queries_; }; TEST_P(RasterBufferProviderPerfTest, ScheduleTasks) { diff --git a/chromium/cc/raster/raster_buffer_provider_unittest.cc b/chromium/cc/raster/raster_buffer_provider_unittest.cc index a429683cdff..a19a04e98a9 100644 --- a/chromium/cc/raster/raster_buffer_provider_unittest.cc +++ b/chromium/cc/raster/raster_buffer_provider_unittest.cc @@ -35,6 +35,7 @@ #include "cc/raster/bitmap_raster_buffer_provider.h" #include "cc/raster/gpu_raster_buffer_provider.h" #include "cc/raster/one_copy_raster_buffer_provider.h" +#include "cc/raster/raster_query_queue.h" #include "cc/raster/synchronous_task_graph_runner.h" #include "cc/raster/zero_copy_raster_buffer_provider.h" #include "cc/resources/resource_pool.h" @@ -232,13 +233,15 @@ class RasterBufferProviderTest Create3dResourceProvider(); raster_buffer_provider_ = std::make_unique<GpuRasterBufferProvider>( context_provider_.get(), worker_context_provider_.get(), false, - viz::RGBA_8888, gfx::Size(), true, false, 1); + viz::RGBA_8888, gfx::Size(), true, false, + pending_raster_queries_.get(), 1); break; case RASTER_BUFFER_PROVIDER_TYPE_GPU_OOPR: Create3dResourceProvider(); raster_buffer_provider_ = std::make_unique<GpuRasterBufferProvider>( context_provider_.get(), worker_context_provider_.get(), false, - viz::RGBA_8888, gfx::Size(), true, true, 1); + viz::RGBA_8888, gfx::Size(), true, true, + pending_raster_queries_.get(), 1); break; case RASTER_BUFFER_PROVIDER_TYPE_BITMAP: CreateSoftwareResourceProvider(); @@ -381,7 +384,9 @@ class RasterBufferProviderTest context_provider_ = viz::TestContextProvider::Create(std::move(gl_owned)); context_provider_->BindToCurrentThread(); + bool oop_rasterization_enabled = false; if (GetParam() == RASTER_BUFFER_PROVIDER_TYPE_GPU_OOPR) { + oop_rasterization_enabled = true; auto worker_gl_owned = std::make_unique<viz::TestGLES2Interface>(); auto worker_support_owned = std::make_unique<viz::TestContextSupport>(); auto worker_ri_owned = std::make_unique<RasterImplementationForOOPR>( @@ -398,6 +403,9 @@ class RasterBufferProviderTest layer_tree_frame_sink_ = FakeLayerTreeFrameSink::Create3d(); resource_provider_ = std::make_unique<viz::ClientResourceProvider>(); + + pending_raster_queries_ = std::make_unique<RasterQueryQueue>( + worker_context_provider_.get(), oop_rasterization_enabled); } void CreateSoftwareResourceProvider() { @@ -427,6 +435,7 @@ class RasterBufferProviderTest std::vector<RasterTaskResult> completed_tasks_; std::vector<ResourcePool::InUsePoolResource> resources_; TaskGraph graph_; + std::unique_ptr<RasterQueryQueue> pending_raster_queries_; }; TEST_P(RasterBufferProviderTest, Basic) { @@ -655,7 +664,7 @@ TEST_P(RasterBufferProviderTest, MeasureGpuRasterDuration) { histogram_tester.ExpectTotalCount(delay_histogram_jpeg_tiles, 0); histogram_tester.ExpectTotalCount(delay_histogram_webp_tiles, 0); bool has_pending_queries = - raster_buffer_provider_->CheckRasterFinishedQueries(); + pending_raster_queries_->CheckRasterFinishedQueries(); EXPECT_FALSE(has_pending_queries); histogram_tester.ExpectTotalCount(duration_histogram, 9); diff --git a/chromium/cc/raster/raster_query_queue.cc b/chromium/cc/raster/raster_query_queue.cc new file mode 100644 index 00000000000..b77d3c69b97 --- /dev/null +++ b/chromium/cc/raster/raster_query_queue.cc @@ -0,0 +1,145 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/raster/raster_query_queue.h" + +#include <utility> + +#include "cc/base/histograms.h" +#include "components/viz/common/gpu/raster_context_provider.h" +#include "gpu/GLES2/gl2extchromium.h" +#include "gpu/command_buffer/client/raster_interface.h" + +namespace cc { + +RasterQuery::RasterQuery() = default; + +RasterQuery::~RasterQuery() = default; + +RasterQueryQueue::RasterQueryQueue( + viz::RasterContextProvider* const worker_context_provider, + bool oop_rasterization_enabled) + : worker_context_provider_(worker_context_provider), + oop_rasterization_enabled_(oop_rasterization_enabled) {} + +RasterQueryQueue::~RasterQueryQueue() = default; + +void RasterQueryQueue::Append(RasterQuery raster_query) { + // It is important for this method to not be called with the raster context + // lock to avoid a deadlock in CheckRasterFinishedQueries, which acquired + // the raster context lock while holding this lock. + base::AutoLock hold(pending_raster_queries_lock_); + pending_raster_queries_.push_back(std::move(raster_query)); +} + +#define UMA_HISTOGRAM_RASTER_TIME_CUSTOM_MICROSECONDS(name, total_time) \ + UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES( \ + name, total_time, base::TimeDelta::FromMicroseconds(1), \ + base::TimeDelta::FromMilliseconds(100), 100); + +bool RasterQueryQueue::CheckRasterFinishedQueries() { + base::AutoLock hold(pending_raster_queries_lock_); + if (pending_raster_queries_.empty()) + return false; + + viz::RasterContextProvider::ScopedRasterContextLock scoped_context( + worker_context_provider_); + auto* ri = scoped_context.RasterInterface(); + + auto it = pending_raster_queries_.begin(); + while (it != pending_raster_queries_.end()) { + GLuint complete = 0; + ri->GetQueryObjectuivEXT(it->raster_duration_query_id, + GL_QUERY_RESULT_AVAILABLE_NO_FLUSH_CHROMIUM_EXT, + &complete); + if (!complete) + break; + +#if DCHECK_IS_ON() + if (it->raster_start_query_id) { + // We issued the GL_COMMANDS_ISSUED_TIMESTAMP_CHROMIUM query prior to the + // GL_COMMANDS_ISSUED_CHROMIUM query. Therefore, if the result of the + // latter is available, the result of the former should be too. + complete = 0; + ri->GetQueryObjectuivEXT(it->raster_start_query_id, + GL_QUERY_RESULT_AVAILABLE_NO_FLUSH_CHROMIUM_EXT, + &complete); + DCHECK(complete); + } +#endif + + GLuint gpu_raster_duration = 0u; + ri->GetQueryObjectuivEXT(it->raster_duration_query_id, GL_QUERY_RESULT_EXT, + &gpu_raster_duration); + ri->DeleteQueriesEXT(1, &it->raster_duration_query_id); + + base::TimeDelta raster_duration = + it->worker_raster_duration + + base::TimeDelta::FromMicroseconds(gpu_raster_duration); + + // It is safe to use the UMA macros here with runtime generated strings + // because the client name should be initialized once in the process, before + // recording any metrics here. + const char* client_name = GetClientNameForMetrics(); + + if (it->raster_start_query_id) { + GLuint64 gpu_raster_start_time = 0u; + ri->GetQueryObjectui64vEXT(it->raster_start_query_id, GL_QUERY_RESULT_EXT, + &gpu_raster_start_time); + ri->DeleteQueriesEXT(1, &it->raster_start_query_id); + + // The base::checked_cast<int64_t> should not crash as long as the GPU + // process was not compromised: that's because the result of the query + // should have been generated using base::TimeDelta::InMicroseconds() + // there, so the result should fit in an int64_t. + base::TimeDelta raster_scheduling_delay = + base::TimeDelta::FromMicroseconds( + base::checked_cast<int64_t>(gpu_raster_start_time)) - + it->raster_buffer_creation_time.since_origin(); + + // We expect the clock we're using to be monotonic, so we shouldn't get a + // negative scheduling delay. + DCHECK_GE(raster_scheduling_delay.InMicroseconds(), 0u); + UMA_HISTOGRAM_RASTER_TIME_CUSTOM_MICROSECONDS( + base::StringPrintf( + "Renderer4.%s.RasterTaskSchedulingDelayNoAtRasterDecodes.All", + client_name), + raster_scheduling_delay); + if (it->depends_on_hardware_accelerated_jpeg_candidates) { + UMA_HISTOGRAM_RASTER_TIME_CUSTOM_MICROSECONDS( + base::StringPrintf( + "Renderer4.%s.RasterTaskSchedulingDelayNoAtRasterDecodes." + "TilesWithJpegHwDecodeCandidates", + client_name), + raster_scheduling_delay); + } + if (it->depends_on_hardware_accelerated_webp_candidates) { + UMA_HISTOGRAM_RASTER_TIME_CUSTOM_MICROSECONDS( + base::StringPrintf( + "Renderer4.%s.RasterTaskSchedulingDelayNoAtRasterDecodes." + "TilesWithWebPHwDecodeCandidates", + client_name), + raster_scheduling_delay); + } + } + + if (oop_rasterization_enabled_) { + UMA_HISTOGRAM_RASTER_TIME_CUSTOM_MICROSECONDS( + base::StringPrintf("Renderer4.%s.RasterTaskTotalDuration.Oop", + client_name), + raster_duration); + } else { + UMA_HISTOGRAM_RASTER_TIME_CUSTOM_MICROSECONDS( + base::StringPrintf("Renderer4.%s.RasterTaskTotalDuration.Gpu", + client_name), + raster_duration); + } + + it = pending_raster_queries_.erase(it); + } + + return pending_raster_queries_.size() > 0u; +} + +} // namespace cc diff --git a/chromium/cc/raster/raster_query_queue.h b/chromium/cc/raster/raster_query_queue.h new file mode 100644 index 00000000000..e81400a30e1 --- /dev/null +++ b/chromium/cc/raster/raster_query_queue.h @@ -0,0 +1,69 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CC_RASTER_RASTER_QUERY_QUEUE_H_ +#define CC_RASTER_RASTER_QUERY_QUEUE_H_ + +#include "base/containers/circular_deque.h" +#include "base/synchronization/lock.h" +#include "base/thread_annotations.h" +#include "base/time/time.h" +#include "cc/cc_export.h" +#include "third_party/khronos/GLES2/gl2.h" + +namespace viz { +class RasterContextProvider; +} // namespace viz + +namespace cc { + +struct CC_EXPORT RasterQuery { + RasterQuery(); + ~RasterQuery(); + + // The id for querying the duration in executing the GPU side work. + GLuint raster_duration_query_id = 0u; + + // The duration for executing the work on the raster worker thread. + base::TimeDelta worker_raster_duration; + + // The id for querying the time at which we're about to start issuing raster + // work to the driver. + GLuint raster_start_query_id = 0u; + + // The time at which the raster buffer was created. + base::TimeTicks raster_buffer_creation_time; + + // Whether the raster work depends on candidates for hardware accelerated + // JPEG or WebP decodes. + bool depends_on_hardware_accelerated_jpeg_candidates = false; + bool depends_on_hardware_accelerated_webp_candidates = false; +}; + +class CC_EXPORT RasterQueryQueue { + public: + RasterQueryQueue(viz::RasterContextProvider* const worker_context_provider, + bool oop_rasterization_enabled); + virtual ~RasterQueryQueue(); + + // These functions should never be called with the raster context lock + // acquired. + void Append(RasterQuery raster_query); + // This function is only virtual for testing purposes. + virtual bool CheckRasterFinishedQueries(); + + private: + viz::RasterContextProvider* const worker_context_provider_; + const bool oop_rasterization_enabled_; + + // Note that this lock should never be acquired while holding the raster + // context lock. + base::Lock pending_raster_queries_lock_; + base::circular_deque<RasterQuery> pending_raster_queries_ + GUARDED_BY(pending_raster_queries_lock_); +}; + +} // namespace cc + +#endif // CC_RASTER_RASTER_QUERY_QUEUE_H_ diff --git a/chromium/cc/raster/raster_source.cc b/chromium/cc/raster/raster_source.cc index 84f3f59ff68..f02d0e2cf7b 100644 --- a/chromium/cc/raster/raster_source.cc +++ b/chromium/cc/raster/raster_source.cc @@ -149,8 +149,9 @@ RasterSource::TakeDecodingModeMap() { return display_list_->TakeDecodingModeMap(); } -bool RasterSource::CoversRect(const gfx::Rect& layer_rect, - const PictureLayerTilingClient& client) const { +bool RasterSource::IntersectsRect( + const gfx::Rect& layer_rect, + const PictureLayerTilingClient& client) const { if (size_.IsEmpty()) return false; @@ -163,7 +164,7 @@ bool RasterSource::CoversRect(const gfx::Rect& layer_rect, gfx::Rect bounded_rect = layer_rect; bounded_rect.Intersect(gfx::Rect(size_)); - return recorded_viewport_.Contains(bounded_rect); + return recorded_viewport_.Intersects(bounded_rect); } gfx::Size RasterSource::GetSize() const { diff --git a/chromium/cc/raster/raster_source.h b/chromium/cc/raster/raster_source.h index 5b08b9cc445..99138adb46c 100644 --- a/chromium/cc/raster/raster_source.h +++ b/chromium/cc/raster/raster_source.h @@ -89,8 +89,8 @@ class CC_EXPORT RasterSource : public base::RefCountedThreadSafe<RasterSource> { // Return true iff this raster source can raster the given rect in layer // space. - bool CoversRect(const gfx::Rect& layer_rect, - const PictureLayerTilingClient& client) const; + bool IntersectsRect(const gfx::Rect& layer_rect, + const PictureLayerTilingClient& client) const; // Returns true if this raster source has anything to rasterize. bool HasRecordings() const; diff --git a/chromium/cc/raster/task_graph_work_queue.cc b/chromium/cc/raster/task_graph_work_queue.cc index a041d0ad42f..48575f7cdbe 100644 --- a/chromium/cc/raster/task_graph_work_queue.cc +++ b/chromium/cc/raster/task_graph_work_queue.cc @@ -12,7 +12,7 @@ #include <unordered_map> #include <utility> -#include "base/stl_util.h" +#include "base/containers/contains.h" #include "base/trace_event/trace_event.h" namespace cc { diff --git a/chromium/cc/raster/zero_copy_raster_buffer_provider.cc b/chromium/cc/raster/zero_copy_raster_buffer_provider.cc index e8e5c214638..cca62553915 100644 --- a/chromium/cc/raster/zero_copy_raster_buffer_provider.cc +++ b/chromium/cc/raster/zero_copy_raster_buffer_provider.cc @@ -236,8 +236,4 @@ uint64_t ZeroCopyRasterBufferProvider::SetReadyToDrawCallback( void ZeroCopyRasterBufferProvider::Shutdown() {} -bool ZeroCopyRasterBufferProvider::CheckRasterFinishedQueries() { - return false; -} - } // namespace cc diff --git a/chromium/cc/raster/zero_copy_raster_buffer_provider.h b/chromium/cc/raster/zero_copy_raster_buffer_provider.h index 18ab5a9ce57..f2322a81f5e 100644 --- a/chromium/cc/raster/zero_copy_raster_buffer_provider.h +++ b/chromium/cc/raster/zero_copy_raster_buffer_provider.h @@ -57,7 +57,6 @@ class CC_EXPORT ZeroCopyRasterBufferProvider : public RasterBufferProvider { base::OnceClosure callback, uint64_t pending_callback_id) const override; void Shutdown() override; - bool CheckRasterFinishedQueries() override; private: std::unique_ptr<base::trace_event::ConvertableToTraceFormat> StateAsValue() diff --git a/chromium/cc/resources/resource_pool.cc b/chromium/cc/resources/resource_pool.cc index e854ca403b6..9b22734ca4a 100644 --- a/chromium/cc/resources/resource_pool.cc +++ b/chromium/cc/resources/resource_pool.cc @@ -8,7 +8,9 @@ #include <stdint.h> #include <algorithm> +#include <limits> #include <memory> +#include <string> #include <utility> #include "base/atomic_sequence_num.h" @@ -301,7 +303,7 @@ void ResourcePool::OnResourceReleased(size_t unique_id, return; } - resource->set_resource_id(0); + resource->set_resource_id(viz::kInvalidResourceId); if (context_provider_) resource->gpu_backing()->returned_sync_token = sync_token; DidFinishUsingResource(std::move(*busy_it)); @@ -320,7 +322,7 @@ bool ResourcePool::PrepareForExport(const InUsePoolResource& in_use_resource) { // This can happen if we failed to allocate a GpuMemoryBuffer. Avoid // sending an invalid resource to the parent in that case, and avoid // caching/reusing the resource. - resource->set_resource_id(0); + resource->set_resource_id(viz::kInvalidResourceId); resource->mark_avoid_reuse(); return false; } diff --git a/chromium/cc/resources/resource_pool.h b/chromium/cc/resources/resource_pool.h index 7d30ea14ed3..c275aa0d738 100644 --- a/chromium/cc/resources/resource_pool.h +++ b/chromium/cc/resources/resource_pool.h @@ -10,6 +10,7 @@ #include <map> #include <memory> +#include <utility> #include "base/containers/circular_deque.h" #include "base/memory/memory_pressure_listener.h" @@ -336,7 +337,7 @@ class CC_EXPORT ResourcePool : public base::trace_event::MemoryDumpProvider { bool avoid_reuse_ = false; // An id used to name the backing for transfer to the display compositor. - viz::ResourceId resource_id_ = 0; + viz::ResourceId resource_id_ = viz::kInvalidResourceId; // The backing for gpu resources. Initially null for resources given // out by ResourcePool, to be filled in by the client. Is destroyed on the diff --git a/chromium/cc/resources/ui_resource_bitmap.cc b/chromium/cc/resources/ui_resource_bitmap.cc index 974706def78..f63f5e38d7f 100644 --- a/chromium/cc/resources/ui_resource_bitmap.cc +++ b/chromium/cc/resources/ui_resource_bitmap.cc @@ -11,6 +11,7 @@ #include "base/check_op.h" #include "base/notreached.h" #include "base/numerics/checked_math.h" +#include "third_party/skia/include/core/SkImage.h" #include "third_party/skia/include/core/SkImageInfo.h" #include "third_party/skia/include/core/SkMallocPixelRef.h" #include "third_party/skia/include/core/SkPixelRef.h" @@ -55,7 +56,7 @@ void UIResourceBitmap::DrawToCanvas(SkCanvas* canvas, SkPaint* paint) { SkBitmap bitmap; bitmap.setInfo(info_, pixel_ref_.get()->rowBytes()); bitmap.setPixelRef(pixel_ref_, 0, 0); - canvas->drawBitmap(bitmap, 0, 0, paint); + canvas->drawImage(bitmap.asImage(), 0, 0, SkSamplingOptions(), paint); canvas->flush(); } diff --git a/chromium/cc/scheduler/begin_frame_tracker.cc b/chromium/cc/scheduler/begin_frame_tracker.cc index 0480a2c24b2..2b8b164650d 100644 --- a/chromium/cc/scheduler/begin_frame_tracker.cc +++ b/chromium/cc/scheduler/begin_frame_tracker.cc @@ -18,9 +18,11 @@ BeginFrameTracker::~BeginFrameTracker() = default; void BeginFrameTracker::Start(const viz::BeginFrameArgs& new_args) { // Trace the frame time being passed between BeginFrameTrackers. - TRACE_EVENT_FLOW_STEP0( - TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler.frames"), "BeginFrameArgs", - new_args.frame_time.since_origin().InMicroseconds(), location_string_); + TRACE_EVENT_WITH_FLOW1(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler.frames"), + "BeginFrameArgs", + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, + new_args.frame_time.since_origin().InMicroseconds(), + "location", location_string_); // Trace this specific begin frame tracker Start/Finish times. TRACE_EVENT_COPY_ASYNC_BEGIN2( diff --git a/chromium/cc/scheduler/scheduler.cc b/chromium/cc/scheduler/scheduler.cc index 8f51ef9b467..bacb2272f72 100644 --- a/chromium/cc/scheduler/scheduler.cc +++ b/chromium/cc/scheduler/scheduler.cc @@ -163,7 +163,6 @@ void Scheduler::DidSubmitCompositorFrame(uint32_t frame_token, void Scheduler::DidReceiveCompositorFrameAck() { DCHECK_GT(state_machine_.pending_submit_frames(), 0); - compositor_timing_history_->DidReceiveCompositorFrameAck(); state_machine_.DidReceiveCompositorFrameAck(); ProcessScheduledActions(); } @@ -225,7 +224,6 @@ void Scheduler::DidCreateAndInitializeLayerTreeFrameSink() { DCHECK(!observing_begin_frame_source_); DCHECK(begin_impl_frame_deadline_task_.IsCancelled()); state_machine_.DidCreateAndInitializeLayerTreeFrameSink(); - compositor_timing_history_->DidCreateAndInitializeLayerTreeFrameSink(); UpdateCompositorTimingHistoryRecordingEnabled(); ProcessScheduledActions(); } @@ -356,9 +354,9 @@ bool Scheduler::OnBeginFrameDerivedImpl(const viz::BeginFrameArgs& args) { } // Trace this begin frame time through the Chrome stack - TRACE_EVENT_FLOW_BEGIN0( - TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler.frames"), - "viz::BeginFrameArgs", args.frame_time.since_origin().InMicroseconds()); + TRACE_EVENT_WITH_FLOW0(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler.frames"), + "viz::BeginFrameArgs", TRACE_EVENT_FLAG_FLOW_OUT, + args.frame_time.since_origin().InMicroseconds()); if (settings_.using_synchronous_renderer_compositor) { BeginImplFrameSynchronous(args); @@ -600,10 +598,11 @@ void Scheduler::FinishImplFrame() { // ack for any pending begin frame if we are going idle after this. This // ensures that the acks are sent in order. if (!state_machine_.did_submit_in_last_frame()) { + bool has_pending_tree = state_machine_.has_pending_tree(); bool is_waiting_on_main = state_machine_.begin_main_frame_state() != SchedulerStateMachine::BeginMainFrameState::IDLE; SendDidNotProduceFrame(begin_impl_frame_tracker_.Current(), - is_waiting_on_main + is_waiting_on_main || has_pending_tree ? FrameSkippedReason::kWaitingOnMain : FrameSkippedReason::kNoDamage); } diff --git a/chromium/cc/scheduler/scheduler_unittest.cc b/chromium/cc/scheduler/scheduler_unittest.cc index b6755ae97e4..c7647b28a06 100644 --- a/chromium/cc/scheduler/scheduler_unittest.cc +++ b/chromium/cc/scheduler/scheduler_unittest.cc @@ -3896,7 +3896,9 @@ TEST_F(SchedulerTest, BeginFrameAckForFinishedImplFrame) { has_damage = false; EXPECT_EQ(viz::BeginFrameAck(args, has_damage), client_->last_begin_frame_ack()); - EXPECT_EQ(FrameSkippedReason::kNoDamage, + // The pending tree is not activated yet so the frame is still waiting on + // Main thread update + EXPECT_EQ(FrameSkippedReason::kWaitingOnMain, client_->last_frame_skipped_reason()); } diff --git a/chromium/cc/tiles/gpu_image_decode_cache.cc b/chromium/cc/tiles/gpu_image_decode_cache.cc index 016a7c4ba31..37a6489c1c0 100644 --- a/chromium/cc/tiles/gpu_image_decode_cache.cc +++ b/chromium/cc/tiles/gpu_image_decode_cache.cc @@ -105,31 +105,6 @@ SkFilterQuality CalculateDesiredFilterQuality(const DrawImage& draw_image) { return std::min(kMedium_SkFilterQuality, draw_image.filter_quality()); } -// Calculate the mip level to upload-scale the image to before uploading. We use -// mip levels rather than exact scales to increase re-use of scaled images. -int CalculateUploadScaleMipLevel(const DrawImage& draw_image, - bool enable_clipped_image_scaling = false) { - // Images which are being clipped will have color-bleeding if scaled. - // TODO(ericrk): Investigate uploading clipped images to handle this case and - // provide further optimization. crbug.com/620899 - const bool is_clipped = draw_image.src_rect() != - SkIRect::MakeWH(draw_image.paint_image().width(), - draw_image.paint_image().height()); - if (is_clipped && !enable_clipped_image_scaling) - return 0; - - gfx::Size base_size(draw_image.paint_image().width(), - draw_image.paint_image().height()); - // Ceil our scaled size so that the mip map generated is guaranteed to be - // larger. Take the abs of the scale, as mipmap functions don't handle - // (and aren't impacted by) negative image dimensions. - gfx::Size scaled_size = - gfx::ScaleToCeiledSize(base_size, std::abs(draw_image.scale().width()), - std::abs(draw_image.scale().height())); - - return MipMapUtil::GetLevelForSize(base_size, scaled_size); -} - // Calculates the scale factor which can be used to scale an image to a given // mip level. SkSize CalculateScaleFactorForMipLevel(const DrawImage& draw_image, @@ -326,7 +301,12 @@ bool DrawAndScaleImage( : supported_size; decode_info = info.makeWH(decode_size.width(), decode_size.height()); } - SkFilterQuality filter_quality = CalculateDesiredFilterQuality(draw_image); + + const SkFilterQuality filter_quality = + CalculateDesiredFilterQuality(draw_image); + const SkSamplingOptions sampling(filter_quality, + SkSamplingOptions::kMedium_asMipmapLinear); + bool decode_to_f16_using_n32_intermediate = decode_info.colorType() == kRGBA_F16_SkColorType && !ImageDecodeCacheUtils::CanResizeF16Image(filter_quality); @@ -383,12 +363,12 @@ bool DrawAndScaleImage( v_info_scaled.minRowBytes()); const bool all_planes_scaled_successfully = - unscaled_yuva_pixmaps.plane(0).scalePixels(*pixmap_y, filter_quality) && - unscaled_yuva_pixmaps.plane(1).scalePixels(*pixmap_u, filter_quality) && - unscaled_yuva_pixmaps.plane(2).scalePixels(*pixmap_v, filter_quality); + unscaled_yuva_pixmaps.plane(0).scalePixels(*pixmap_y, sampling) && + unscaled_yuva_pixmaps.plane(1).scalePixels(*pixmap_u, sampling) && + unscaled_yuva_pixmaps.plane(2).scalePixels(*pixmap_v, sampling); return all_planes_scaled_successfully; } - return decode_pixmap.scalePixels(pixmap, filter_quality); + return decode_pixmap.scalePixels(pixmap, sampling); } // Takes ownership of the backing texture of an SkImage. This allows us to @@ -473,57 +453,14 @@ sk_sp<SkImage> MakeTextureImage(viz::RasterContextProvider* context, return uploaded_image; } -size_t GetUploadedTextureSizeFromSkImage(const sk_sp<SkImage>& plane, - const GrMipMapped mipped) { - const size_t plane_size = GrDirectContext::ComputeImageSize(plane, mipped); - return plane_size; -} - -// Preserves the division of channels into planes and channel order within -// planes but removes chroma subsampling. If there is no such config this will -// return SkYUVAInfo::PlanarConfig::kUnknown. -SkYUVAInfo::PlanarConfig ConvertPlanarConfigTo444( - SkYUVAInfo::PlanarConfig config) { - switch (config) { - case SkYUVAInfo::PlanarConfig::kY_U_V_444: - case SkYUVAInfo::PlanarConfig::kY_U_V_422: - case SkYUVAInfo::PlanarConfig::kY_U_V_420: - case SkYUVAInfo::PlanarConfig::kY_U_V_440: - case SkYUVAInfo::PlanarConfig::kY_U_V_411: - case SkYUVAInfo::PlanarConfig::kY_U_V_410: - return SkYUVAInfo::PlanarConfig::kY_U_V_444; - - case SkYUVAInfo::PlanarConfig::kYUV_444: - return SkYUVAInfo::PlanarConfig::kYUV_444; - case SkYUVAInfo::PlanarConfig::kUYV_444: - return SkYUVAInfo::PlanarConfig::kUYV_444; - case SkYUVAInfo::PlanarConfig::kYUVA_4444: - return SkYUVAInfo::PlanarConfig::kYUVA_4444; - case SkYUVAInfo::PlanarConfig::kUYVA_4444: - return SkYUVAInfo::PlanarConfig::kUYVA_4444; - - // There are planar configs with no 444[4] equivalent, none of which are - // used by GpuImageDecoderCache currently. For example, there is kY_UV_420 - // but no kY_UV_444. Skia could easily have add the equivalents if required. - default: - NOTREACHED(); - return SkYUVAInfo::PlanarConfig::kUnknown; - } -} - } // namespace -// static -GpuImageDecodeCache::InUseCacheKey -GpuImageDecodeCache::InUseCacheKey::FromDrawImage(const DrawImage& draw_image) { - return InUseCacheKey(draw_image); -} - // Extract the information to uniquely identify a DrawImage for the purposes of // the |in_use_cache_|. -GpuImageDecodeCache::InUseCacheKey::InUseCacheKey(const DrawImage& draw_image) +GpuImageDecodeCache::InUseCacheKey::InUseCacheKey(const DrawImage& draw_image, + int mip_level) : frame_key(draw_image.frame_key()), - upload_scale_mip_level(CalculateUploadScaleMipLevel(draw_image)), + upload_scale_mip_level(mip_level), filter_quality(CalculateDesiredFilterQuality(draw_image)), target_color_space(draw_image.target_color_space()) {} @@ -906,10 +843,7 @@ GpuImageDecodeCache::ImageData::ImageData( bool is_bitmap_backed, bool can_do_hardware_accelerated_decode, bool do_hardware_accelerated_decode, - bool is_yuv_format, - SkYUVColorSpace yuv_cs, - SkYUVAPixmapInfo::PlanarConfig yuv_config, - SkYUVAPixmapInfo::DataType yuv_dt) + base::Optional<SkYUVAPixmapInfo> yuva_info) : paint_image_id(paint_image_id), mode(mode), size(size), @@ -918,17 +852,14 @@ GpuImageDecodeCache::ImageData::ImageData( upload_scale_mip_level(upload_scale_mip_level), needs_mips(needs_mips), is_bitmap_backed(is_bitmap_backed), - is_yuv(is_yuv_format), + yuva_pixmap_info(yuva_info), decode(is_bitmap_backed, can_do_hardware_accelerated_decode, do_hardware_accelerated_decode) { - // Only fill out the base::Optional |yuv_color_space| if doing YUV decoding. - // Otherwise it was filled out with a default "identity" value by the decoder. - if (is_yuv) { - DCHECK_LE(yuv_cs, SkYUVColorSpace::kLastEnum_SkYUVColorSpace); - yuv_color_space = yuv_cs; - yuv_planar_config = yuv_config; - yuv_data_type = yuv_dt; + if (yuva_pixmap_info.has_value()) { + // This is the only plane config supported currently. + DCHECK_EQ(yuva_pixmap_info->yuvaInfo().planeConfig(), + SkYUVAInfo::PlaneConfig::kY_U_V); } } @@ -955,7 +886,7 @@ bool GpuImageDecodeCache::ImageData::HasUploadedData() const { if (upload.image()) { // TODO(915968): Be smarter about being able to re-upload planes // selectively if only some get deleted from under us. - DCHECK(!is_yuv || upload.has_yuv_planes()); + DCHECK(!yuva_pixmap_info.has_value() || upload.has_yuv_planes()); return true; } return false; @@ -1054,7 +985,7 @@ GpuImageDecodeCache::GpuImageDecodeCache( TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"), "GpuImageDecodeCache::DarkModeFilter", "dark_mode_filter", - dark_mode_filter_); + static_cast<void*>(dark_mode_filter_)); } GpuImageDecodeCache::~GpuImageDecodeCache() { @@ -1099,7 +1030,7 @@ ImageDecodeCache::TaskResult GpuImageDecodeCache::GetTaskForImageAndRefInternal( } base::AutoLock lock(lock_); - const InUseCacheKey cache_key = InUseCacheKey::FromDrawImage(draw_image); + const InUseCacheKey cache_key = InUseCacheKeyFromDrawImage(draw_image); ImageData* image_data = GetImageDataForDrawImage(draw_image, cache_key); scoped_refptr<ImageData> new_data; if (!image_data) { @@ -1186,7 +1117,7 @@ void GpuImageDecodeCache::UnrefImage(const DrawImage& draw_image) { TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), "GpuImageDecodeCache::UnrefImage"); base::AutoLock lock(lock_); - UnrefImageInternal(draw_image, InUseCacheKey::FromDrawImage(draw_image)); + UnrefImageInternal(draw_image, InUseCacheKeyFromDrawImage(draw_image)); } bool GpuImageDecodeCache::UseCacheForDrawImage( @@ -1211,7 +1142,7 @@ DecodedDrawImage GpuImageDecodeCache::GetDecodedImageForDraw( return DecodedDrawImage(); base::AutoLock lock(lock_); - const InUseCacheKey cache_key = InUseCacheKey::FromDrawImage(draw_image); + const InUseCacheKey cache_key = InUseCacheKeyFromDrawImage(draw_image); ImageData* image_data = GetImageDataForDrawImage(draw_image, cache_key); if (!image_data) { // We didn't find the image, create a new entry. @@ -1255,7 +1186,8 @@ DecodedDrawImage GpuImageDecodeCache::GetDecodedImageForDraw( draw_image, image_data->upload_scale_mip_level); DecodedDrawImage decoded_draw_image( id, std::move(dark_mode_color_filter), SkSize(), scale_factor, - CalculateDesiredFilterQuality(draw_image), image_data->needs_mips); + CalculateDesiredFilterQuality(draw_image), image_data->needs_mips, + image_data->is_budgeted); return decoded_draw_image; } else { DCHECK(!use_transfer_cache_); @@ -1268,7 +1200,8 @@ DecodedDrawImage GpuImageDecodeCache::GetDecodedImageForDraw( draw_image, image_data->upload_scale_mip_level); DecodedDrawImage decoded_draw_image( std::move(image), std::move(dark_mode_color_filter), SkSize(), - scale_factor, CalculateDesiredFilterQuality(draw_image)); + scale_factor, CalculateDesiredFilterQuality(draw_image), + image_data->is_budgeted); return decoded_draw_image; } } @@ -1291,7 +1224,7 @@ void GpuImageDecodeCache::DrawWithImageFinished( return; base::AutoLock lock(lock_); - UnrefImageInternal(draw_image, InUseCacheKey::FromDrawImage(draw_image)); + UnrefImageInternal(draw_image, InUseCacheKeyFromDrawImage(draw_image)); // We are mid-draw and holding the context lock, ensure we clean up any // textures (especially at-raster), which may have just been marked for @@ -1440,7 +1373,7 @@ void GpuImageDecodeCache::MemoryDumpYUVImage( const std::string& dump_base_name, size_t locked_size) const { using base::trace_event::MemoryAllocatorDump; - DCHECK(image_data->is_yuv); + DCHECK(image_data->yuva_pixmap_info.has_value()); DCHECK(image_data->upload.has_yuv_planes()); struct PlaneMemoryDumpInfo { @@ -1448,18 +1381,13 @@ void GpuImageDecodeCache::MemoryDumpYUVImage( GrGLuint gl_id; }; std::vector<PlaneMemoryDumpInfo> plane_dump_infos; - const GrMipMapped mipped = - image_data->needs_mips ? GrMipMapped::kYes : GrMipMapped::kNo; // TODO(crbug.com/910276): Also include alpha plane if applicable. - plane_dump_infos.push_back( - {GetUploadedTextureSizeFromSkImage(image_data->upload.y_image(), mipped), - image_data->upload.gl_y_id()}); - plane_dump_infos.push_back( - {GetUploadedTextureSizeFromSkImage(image_data->upload.u_image(), mipped), - image_data->upload.gl_u_id()}); - plane_dump_infos.push_back( - {GetUploadedTextureSizeFromSkImage(image_data->upload.v_image(), mipped), - image_data->upload.gl_v_id()}); + plane_dump_infos.push_back({image_data->upload.y_image()->textureSize(), + image_data->upload.gl_y_id()}); + plane_dump_infos.push_back({image_data->upload.u_image()->textureSize(), + image_data->upload.gl_u_id()}); + plane_dump_infos.push_back({image_data->upload.v_image()->textureSize(), + image_data->upload.gl_v_id()}); for (size_t i = 0u; i < plane_dump_infos.size(); ++i) { auto plane_dump_info = plane_dump_infos.at(i); @@ -1523,7 +1451,7 @@ bool GpuImageDecodeCache::OnMemoryDump( auto* context_support = context_->ContextSupport(); // If the discardable system has deleted this out from under us, log a // size of 0 to match software discardable. - if (image_data->is_yuv && + if (image_data->yuva_pixmap_info.has_value() && context_support->ThreadsafeDiscardableTextureIsDeletedForTracing( image_data->upload.gl_y_id()) && context_support->ThreadsafeDiscardableTextureIsDeletedForTracing( @@ -1542,7 +1470,7 @@ bool GpuImageDecodeCache::OnMemoryDump( reinterpret_cast<uintptr_t>(this), image_id); size_t locked_size = image_data->upload.is_locked() ? discardable_size : 0u; - if (image_data->is_yuv) { + if (image_data->yuva_pixmap_info.has_value()) { MemoryDumpYUVImage(pmd, image_data, gpu_dump_base_name, locked_size); } else { AddTextureDump(pmd, gpu_dump_base_name, discardable_size, @@ -1560,7 +1488,7 @@ void GpuImageDecodeCache::DecodeImageInTask(const DrawImage& draw_image, "GpuImageDecodeCache::DecodeImage"); base::AutoLock lock(lock_); ImageData* image_data = GetImageDataForDrawImage( - draw_image, InUseCacheKey::FromDrawImage(draw_image)); + draw_image, InUseCacheKeyFromDrawImage(draw_image)); DCHECK(image_data); DCHECK(image_data->is_budgeted) << "Must budget an image for pre-decoding"; DecodeImageAndGenerateDarkModeFilterIfNecessary(draw_image, image_data, @@ -1580,7 +1508,7 @@ void GpuImageDecodeCache::UploadImageInTask(const DrawImage& draw_image) { gr_context_access.emplace(context_); base::AutoLock lock(lock_); - auto cache_key = InUseCacheKey::FromDrawImage(draw_image); + auto cache_key = InUseCacheKeyFromDrawImage(draw_image); ImageData* image_data = GetImageDataForDrawImage(draw_image, cache_key); DCHECK(image_data); DCHECK(image_data->is_budgeted) << "Must budget an image for pre-decoding"; @@ -1597,7 +1525,7 @@ void GpuImageDecodeCache::OnImageDecodeTaskCompleted( TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), "GpuImageDecodeCache::OnImageDecodeTaskCompleted"); base::AutoLock lock(lock_); - auto cache_key = InUseCacheKey::FromDrawImage(draw_image); + auto cache_key = InUseCacheKeyFromDrawImage(draw_image); // Decode task is complete, remove our reference to it. ImageData* image_data = GetImageDataForDrawImage(draw_image, cache_key); DCHECK(image_data); @@ -1621,7 +1549,7 @@ void GpuImageDecodeCache::OnImageUploadTaskCompleted( "GpuImageDecodeCache::OnImageUploadTaskCompleted"); base::AutoLock lock(lock_); // Upload task is complete, remove our reference to it. - InUseCacheKey cache_key = InUseCacheKey::FromDrawImage(draw_image); + InUseCacheKey cache_key = InUseCacheKeyFromDrawImage(draw_image); ImageData* image_data = GetImageDataForDrawImage(draw_image, cache_key); DCHECK(image_data); DCHECK(image_data->upload.task); @@ -1634,6 +1562,37 @@ void GpuImageDecodeCache::OnImageUploadTaskCompleted( UnrefImageInternal(draw_image, cache_key); } +int GpuImageDecodeCache::CalculateUploadScaleMipLevel( + const DrawImage& draw_image) const { + // Images which are being clipped will have color-bleeding if scaled. + // TODO(ericrk): Investigate uploading clipped images to handle this case and + // provide further optimization. crbug.com/620899 + if (!enable_clipped_image_scaling_) { + const bool is_clipped = draw_image.src_rect() != + SkIRect::MakeWH(draw_image.paint_image().width(), + draw_image.paint_image().height()); + if (is_clipped) + return 0; + } + + gfx::Size base_size(draw_image.paint_image().width(), + draw_image.paint_image().height()); + // Ceil our scaled size so that the mip map generated is guaranteed to be + // larger. Take the abs of the scale, as mipmap functions don't handle + // (and aren't impacted by) negative image dimensions. + gfx::Size scaled_size = + gfx::ScaleToCeiledSize(base_size, std::abs(draw_image.scale().width()), + std::abs(draw_image.scale().height())); + + return MipMapUtil::GetLevelForSize(base_size, scaled_size); +} + +GpuImageDecodeCache::InUseCacheKey +GpuImageDecodeCache::InUseCacheKeyFromDrawImage( + const DrawImage& draw_image) const { + return InUseCacheKey(draw_image, CalculateUploadScaleMipLevel(draw_image)); +} + // Checks if an image decode needs a decode task and returns it. scoped_refptr<TileTask> GpuImageDecodeCache::GetImageDecodeTaskAndRef( const DrawImage& draw_image, @@ -1643,7 +1602,7 @@ scoped_refptr<TileTask> GpuImageDecodeCache::GetImageDecodeTaskAndRef( "GpuImageDecodeCache::GetImageDecodeTaskAndRef"); lock_.AssertAcquired(); - auto cache_key = InUseCacheKey::FromDrawImage(draw_image); + auto cache_key = InUseCacheKeyFromDrawImage(draw_image); // This ref is kept alive while an upload task may need this decode. We // release this ref in UploadTaskCompleted. @@ -1930,7 +1889,7 @@ bool GpuImageDecodeCache::NeedsDarkModeFilter(const DrawImage& draw_image, DCHECK(dark_mode_filter_); // TODO(prashant.n): RSDM - Add support for YUV decoded data. - if (image_data->is_yuv) + if (image_data->yuva_pixmap_info.has_value()) return false; // Dark mode filter is already generated and cached. @@ -1986,7 +1945,7 @@ void GpuImageDecodeCache::DecodeImageIfNecessary( if (image_data->is_bitmap_backed) { DCHECK(!draw_image.paint_image().IsLazyGenerated()); - if (image_data->is_yuv) { + if (image_data->yuva_pixmap_info.has_value()) { DLOG(ERROR) << "YUV + Bitmap is unknown and unimplemented!"; NOTREACHED(); } else { @@ -2028,15 +1987,15 @@ void GpuImageDecodeCache::DecodeImageIfNecessary( // Set |pixmap| to the desired colorspace to decode into. pixmap.setColorSpace(color_space); - if (image_data->is_yuv) { + if (image_data->yuva_pixmap_info.has_value()) { DVLOG(3) << "GpuImageDecodeCache wants to do YUV decoding/rendering"; SkPixmap pixmap_y; SkPixmap pixmap_u; SkPixmap pixmap_v; - if (!DrawAndScaleImage(draw_image, &pixmap, generator_client_id_, - image_data->is_yuv, yuva_supported_data_types_, - image_data->yuv_data_type.value(), &pixmap_y, - &pixmap_u, &pixmap_v)) { + if (!DrawAndScaleImage(draw_image, &pixmap, generator_client_id_, true, + yuva_supported_data_types_, + image_data->yuva_pixmap_info->dataType(), + &pixmap_y, &pixmap_u, &pixmap_v)) { DLOG(ERROR) << "DrawAndScaleImage failed."; backing_memory->Unlock(); backing_memory.reset(); @@ -2046,8 +2005,8 @@ void GpuImageDecodeCache::DecodeImageIfNecessary( image_v = SkImage::MakeFromRaster(pixmap_v, release_proc, nullptr); } } else { // RGBX decoding is the default path. - if (!DrawAndScaleImage(draw_image, &pixmap, generator_client_id_, - image_data->is_yuv, yuva_supported_data_types_)) { + if (!DrawAndScaleImage(draw_image, &pixmap, generator_client_id_, false, + yuva_supported_data_types_)) { DLOG(ERROR) << "DrawAndScaleImage failed."; backing_memory->Unlock(); backing_memory.reset(); @@ -2058,8 +2017,8 @@ void GpuImageDecodeCache::DecodeImageIfNecessary( } if (image_data->decode.data()) { - // An at-raster task decoded this before us. Ingore our decode. - if (image_data->is_yuv) { + // An at-raster task decoded this before us. Ignore our decode. + if (image_data->yuva_pixmap_info.has_value()) { DCHECK(image_data->decode.y_image()); DCHECK(image_data->decode.u_image()); DCHECK(image_data->decode.v_image()); @@ -2079,7 +2038,7 @@ void GpuImageDecodeCache::DecodeImageIfNecessary( return; } - if (image_data->is_yuv) { + if (image_data->yuva_pixmap_info.has_value()) { image_data->decode.SetLockedData( std::move(backing_memory), std::move(image_y), std::move(image_u), std::move(image_v), task_type == TaskType::kOutOfRaster); @@ -2216,19 +2175,19 @@ void GpuImageDecodeCache::UploadImageIfNecessary(const DrawImage& draw_image, } // Non-hardware-accelerated path. - if (image_data->is_yuv) { - DCHECK(image_data->yuv_color_space); - SkPixmap y_pixmap; - SkPixmap u_pixmap; - SkPixmap v_pixmap; - if (!image_data->decode.y_image()->peekPixels(&y_pixmap) || - !image_data->decode.u_image()->peekPixels(&u_pixmap) || - !image_data->decode.v_image()->peekPixels(&v_pixmap)) { + if (image_data->yuva_pixmap_info.has_value()) { + SkPixmap yuv_pixmaps[3]; + if (!image_data->decode.y_image()->peekPixels(&yuv_pixmaps[0]) || + !image_data->decode.u_image()->peekPixels(&yuv_pixmaps[1]) || + !image_data->decode.v_image()->peekPixels(&yuv_pixmaps[2])) { return; } ClientImageTransferCacheEntry image_entry( - &y_pixmap, &u_pixmap, &v_pixmap, decoded_target_colorspace.get(), - image_data->yuv_color_space.value(), image_data->needs_mips); + yuv_pixmaps, image_data->yuva_pixmap_info->yuvaInfo().planeConfig(), + image_data->yuva_pixmap_info->yuvaInfo().subsampling(), + decoded_target_colorspace.get(), + image_data->yuva_pixmap_info->yuvaInfo().yuvColorSpace(), + image_data->needs_mips); InsertTransferCacheEntry(image_entry, image_data); } else { SkPixmap pixmap; @@ -2254,8 +2213,7 @@ void GpuImageDecodeCache::UploadImageIfNecessary(const DrawImage& draw_image, GrMipMapped image_needs_mips = image_data->needs_mips ? GrMipMapped::kYes : GrMipMapped::kNo; - if (image_data->is_yuv) { - DCHECK(image_data->yuv_color_space.has_value()); + if (image_data->yuva_pixmap_info.has_value()) { // Grab a reference to our decoded image. For the kCpu path, we will use // this directly as our "uploaded" data. sk_sp<SkImage> uploaded_y_image = image_data->decode.y_image(); @@ -2282,8 +2240,9 @@ void GpuImageDecodeCache::UploadImageIfNecessary(const DrawImage& draw_image, uploaded_image = CreateImageFromYUVATexturesInternal( uploaded_y_image.get(), uploaded_u_image.get(), uploaded_v_image.get(), image_width, image_height, - image_data->yuv_planar_config.value(), - image_data->yuv_color_space.value(), color_space, + image_data->yuva_pixmap_info->yuvaInfo().planeConfig(), + image_data->yuva_pixmap_info->yuvaInfo().subsampling(), + image_data->yuva_pixmap_info->yuvaInfo().yuvColorSpace(), color_space, decoded_target_colorspace); } @@ -2319,7 +2278,8 @@ void GpuImageDecodeCache::UploadImageIfNecessary(const DrawImage& draw_image, uploaded_v_image = TakeOwnershipOfSkImageBacking( context_->GrContext(), std::move(uploaded_v_image)); - image_data->upload.SetImage(std::move(uploaded_image), image_data->is_yuv); + image_data->upload.SetImage(std::move(uploaded_image), + image_data->yuva_pixmap_info.has_value()); image_data->upload.SetYuvImage(std::move(uploaded_y_image), std::move(uploaded_u_image), std::move(uploaded_v_image)); @@ -2478,36 +2438,33 @@ GpuImageDecodeCache::CreateImageData(const DrawImage& draw_image, mode != DecodedDataMode::kCpu && !image_larger_than_max_texture; - SkYUVAInfo::PlanarConfig yuv_planar_config = - SkYUVAInfo::PlanarConfig::kUnknown; - // TODO(crbug.com/910276): Change after alpha support. + base::Optional<SkYUVAPixmapInfo> optional_yuva_pixmap_info; if (is_yuv) { + DCHECK(yuva_pixmap_info.isValid()); if (upload_scale_mip_level > 0) { // Scaled decode. We always promote to 4:4:4 when scaling YUV to avoid - // blurriness. See comment in DrawAndScaleImage() for details. - const base::CheckedNumeric<size_t> y_plane_size = - image_info.makeColorType(yuva_pixmap_info.planeInfo(0).colorType()) - .computeMinByteSize(); - DCHECK(!SkImageInfo::ByteSizeOverflowed(y_plane_size.ValueOrDie())); - yuv_planar_config = - ConvertPlanarConfigTo444(yuva_pixmap_info.yuvaInfo().planarConfig()); - DCHECK_NE(yuv_planar_config, SkYUVAInfo::PlanarConfig::kUnknown); - data_size = (3 * y_plane_size).ValueOrDie(); + // blurriness. See comment in DrawAndScaleImage() for details 0 + SkYUVAInfo yuva_info = yuva_pixmap_info.yuvaInfo().makeSubsampling( + SkYUVAInfo::Subsampling::k444); + size_t row_bytes[SkYUVAInfo::kMaxPlanes] = {}; + for (int i = 0; i < yuva_info.numPlanes(); ++i) { + row_bytes[i] = yuva_pixmap_info.rowBytes(0); + } + optional_yuva_pixmap_info = + SkYUVAPixmapInfo(yuva_info, yuva_pixmap_info.dataType(), row_bytes); } else { // Original size decode. - yuv_planar_config = yuva_pixmap_info.yuvaInfo().planarConfig(); - data_size = yuva_pixmap_info.computeTotalBytes(); - DCHECK(!SkImageInfo::ByteSizeOverflowed(data_size)); + optional_yuva_pixmap_info = yuva_pixmap_info; } + data_size = optional_yuva_pixmap_info->computeTotalBytes(); + DCHECK(!SkImageInfo::ByteSizeOverflowed(data_size)); } - return base::WrapRefCounted(new ImageData( draw_image.paint_image().stable_id(), mode, data_size, draw_image.target_color_space(), CalculateDesiredFilterQuality(draw_image), upload_scale_mip_level, needs_mips, is_bitmap_backed, can_do_hardware_accelerated_decode, - do_hardware_accelerated_decode, is_yuv, yuva_pixmap_info.yuvColorSpace(), - yuv_planar_config, yuva_pixmap_info.dataType())); + do_hardware_accelerated_decode, optional_yuva_pixmap_info)); } void GpuImageDecodeCache::WillAddCacheEntry(const DrawImage& draw_image) { @@ -2564,7 +2521,7 @@ void GpuImageDecodeCache::DeleteImage(ImageData* image_data) { if (image_data->HasUploadedData()) { DCHECK(!image_data->upload.is_locked()); if (image_data->mode == DecodedDataMode::kGpu) { - if (image_data->is_yuv) { + if (image_data->yuva_pixmap_info.has_value()) { images_pending_deletion_.push_back(image_data->upload.y_image()); images_pending_deletion_.push_back(image_data->upload.u_image()); images_pending_deletion_.push_back(image_data->upload.v_image()); @@ -2582,7 +2539,7 @@ void GpuImageDecodeCache::DeleteImage(ImageData* image_data) { void GpuImageDecodeCache::UnlockImage(ImageData* image_data) { DCHECK(image_data->HasUploadedData()); if (image_data->mode == DecodedDataMode::kGpu) { - if (image_data->is_yuv) { + if (image_data->yuva_pixmap_info.has_value()) { images_pending_unlock_.push_back(image_data->upload.y_image().get()); images_pending_unlock_.push_back(image_data->upload.u_image().get()); images_pending_unlock_.push_back(image_data->upload.v_image().get()); @@ -2600,7 +2557,7 @@ void GpuImageDecodeCache::UnlockImage(ImageData* image_data) { // it is guarenteed to have no-refs. auto unmipped_image = image_data->upload.take_unmipped_image(); if (unmipped_image) { - if (image_data->is_yuv) { + if (image_data->yuva_pixmap_info.has_value()) { auto unmipped_y_image = image_data->upload.take_unmipped_y_image(); auto unmipped_u_image = image_data->upload.take_unmipped_u_image(); auto unmipped_v_image = image_data->upload.take_unmipped_v_image(); @@ -2736,7 +2693,7 @@ bool GpuImageDecodeCache::TryLockImage(HaveContextLock have_context_lock, // If |have_context_lock|, we can immediately lock the image and send // the lock command to the GPU process. // TODO(crbug.com/914622): Add Chrome GL extension to upload texture array. - if (data->is_yuv && + if (data->yuva_pixmap_info.has_value() && ri->LockDiscardableTextureCHROMIUM(data->upload.gl_y_id()) && ri->LockDiscardableTextureCHROMIUM(data->upload.gl_u_id()) && ri->LockDiscardableTextureCHROMIUM(data->upload.gl_v_id())) { @@ -2744,7 +2701,7 @@ bool GpuImageDecodeCache::TryLockImage(HaveContextLock have_context_lock, DCHECK(data->mode == DecodedDataMode::kGpu); data->upload.OnLock(); return true; - } else if (!(data->is_yuv) && + } else if (!data->yuva_pixmap_info.has_value() && ri->LockDiscardableTextureCHROMIUM(data->upload.gl_id())) { DCHECK(!use_transfer_cache_); DCHECK(data->mode == DecodedDataMode::kGpu); @@ -2762,7 +2719,7 @@ bool GpuImageDecodeCache::TryLockImage(HaveContextLock have_context_lock, // UploadImageIfNecessary, which is guaranteed to run before the texture // is used. auto* context_support = context_->ContextSupport(); - if (data->is_yuv && + if (data->yuva_pixmap_info.has_value() && context_support->ThreadSafeShallowLockDiscardableTexture( data->upload.gl_y_id()) && context_support->ThreadSafeShallowLockDiscardableTexture( @@ -2776,7 +2733,7 @@ bool GpuImageDecodeCache::TryLockImage(HaveContextLock have_context_lock, images_pending_complete_lock_.push_back(data->upload.u_image().get()); images_pending_complete_lock_.push_back(data->upload.v_image().get()); return true; - } else if (!(data->is_yuv) && + } else if (!data->yuva_pixmap_info.has_value() && context_support->ThreadSafeShallowLockDiscardableTexture( data->upload.gl_id())) { DCHECK(!use_transfer_cache_); @@ -2827,9 +2784,8 @@ GpuImageDecodeCache::ImageData* GpuImageDecodeCache::GetImageDataForDrawImage( bool GpuImageDecodeCache::IsCompatible(const ImageData* image_data, const DrawImage& draw_image) const { bool is_scaled = image_data->upload_scale_mip_level != 0; - bool scale_is_compatible = - CalculateUploadScaleMipLevel(draw_image, enable_clipped_image_scaling_) >= - image_data->upload_scale_mip_level; + bool scale_is_compatible = CalculateUploadScaleMipLevel(draw_image) >= + image_data->upload_scale_mip_level; bool quality_is_compatible = CalculateDesiredFilterQuality(draw_image) <= image_data->quality; bool color_is_compatible = @@ -2868,7 +2824,7 @@ bool GpuImageDecodeCache::DiscardableIsLockedForTesting( bool GpuImageDecodeCache::IsInInUseCacheForTesting( const DrawImage& image) const { - auto found = in_use_cache_.find(InUseCacheKey::FromDrawImage(image)); + auto found = in_use_cache_.find(InUseCacheKeyFromDrawImage(image)); return found != in_use_cache_.end(); } @@ -2884,7 +2840,7 @@ sk_sp<SkImage> GpuImageDecodeCache::GetSWImageDecodeForTesting( auto found = persistent_cache_.Peek(image.frame_key()); DCHECK(found != persistent_cache_.end()); ImageData* image_data = found->second.get(); - DCHECK(!image_data->is_yuv); + DCHECK(!image_data->yuva_pixmap_info.has_value()); return image_data->decode.ImageForTesting(); } @@ -2896,8 +2852,8 @@ sk_sp<SkImage> GpuImageDecodeCache::GetUploadedPlaneForTesting( YUVIndex index) { base::AutoLock lock(lock_); ImageData* image_data = GetImageDataForDrawImage( - draw_image, InUseCacheKey::FromDrawImage(draw_image)); - if (!image_data->is_yuv) + draw_image, InUseCacheKeyFromDrawImage(draw_image)); + if (!image_data->yuva_pixmap_info.has_value()) return nullptr; switch (index) { case YUVIndex::kY: @@ -2915,7 +2871,7 @@ size_t GpuImageDecodeCache::GetDarkModeImageCacheSizeForTesting( const DrawImage& draw_image) { base::AutoLock lock(lock_); ImageData* image_data = GetImageDataForDrawImage( - draw_image, InUseCacheKey::FromDrawImage(draw_image)); + draw_image, InUseCacheKeyFromDrawImage(draw_image)); return image_data ? image_data->decode.dark_mode_color_filter_cache.size() : 0u; } @@ -2924,7 +2880,7 @@ bool GpuImageDecodeCache::NeedsDarkModeFilterForTesting( const DrawImage& draw_image) { base::AutoLock lock(lock_); ImageData* image_data = GetImageDataForDrawImage( - draw_image, InUseCacheKey::FromDrawImage(draw_image)); + draw_image, InUseCacheKeyFromDrawImage(draw_image)); return NeedsDarkModeFilter(draw_image, image_data); } @@ -2995,15 +2951,16 @@ sk_sp<SkImage> GpuImageDecodeCache::CreateImageFromYUVATexturesInternal( const SkImage* uploaded_v_image, const size_t image_width, const size_t image_height, - const SkYUVAInfo::PlanarConfig yuva_planar_config, + const SkYUVAInfo::PlaneConfig yuva_plane_config, + const SkYUVAInfo::Subsampling yuva_subsampling, const SkYUVColorSpace yuv_color_space, sk_sp<SkColorSpace> target_color_space, sk_sp<SkColorSpace> decoded_color_space) const { DCHECK(uploaded_y_image); DCHECK(uploaded_u_image); DCHECK(uploaded_v_image); - SkYUVAInfo yuva_info({image_width, image_height}, yuva_planar_config, - yuv_color_space); + SkYUVAInfo yuva_info({image_width, image_height}, yuva_plane_config, + yuva_subsampling, yuv_color_space); GrBackendTexture yuv_textures[3]{}; yuv_textures[0] = uploaded_y_image->getBackendTexture(false); yuv_textures[1] = uploaded_u_image->getBackendTexture(false); @@ -3047,7 +3004,7 @@ void GpuImageDecodeCache::UpdateMipsIfNeeded(const DrawImage& draw_image, image_data->mode != DecodedDataMode::kGpu) return; - if (image_data->is_yuv) { + if (image_data->yuva_pixmap_info.has_value()) { // Need to generate mips. Take a reference on the planes we're about to // delete, delaying deletion. // TODO(crbug.com/910276): Change after alpha support. @@ -3106,14 +3063,14 @@ void GpuImageDecodeCache::UpdateMipsIfNeeded(const DrawImage& draw_image, NeedsColorSpaceAdjustedForUpload(draw_image) ? ColorSpaceForImageUpload(draw_image) : ColorSpaceForImageDecode(draw_image, image_data->mode); - DCHECK(image_data->yuv_color_space.has_value()); sk_sp<SkImage> yuv_image_with_mips_owned = CreateImageFromYUVATexturesInternal( image_y_with_mips_owned.get(), image_u_with_mips_owned.get(), image_v_with_mips_owned.get(), width, height, - image_data->yuv_planar_config.value(), - image_data->yuv_color_space.value(), color_space, - upload_color_space); + image_data->yuva_pixmap_info->yuvaInfo().planeConfig(), + image_data->yuva_pixmap_info->yuvaInfo().subsampling(), + image_data->yuva_pixmap_info->yuvaInfo().yuvColorSpace(), + color_space, upload_color_space); // In case of lost context if (!yuv_image_with_mips_owned) { DLOG(WARNING) << "TODO(crbug.com/740737): Context was lost. Early out."; diff --git a/chromium/cc/tiles/gpu_image_decode_cache.h b/chromium/cc/tiles/gpu_image_decode_cache.h index ec28a20dd9a..3acfc4dc7e2 100644 --- a/chromium/cc/tiles/gpu_image_decode_cache.h +++ b/chromium/cc/tiles/gpu_image_decode_cache.h @@ -520,10 +520,7 @@ class CC_EXPORT GpuImageDecodeCache bool is_bitmap_backed, bool can_do_hardware_accelerated_decode, bool do_hardware_accelerated_decode, - bool is_yuv_format, - SkYUVColorSpace yuv_cs, - SkYUVAInfo::PlanarConfig yuv_config, - SkYUVAPixmapInfo::DataType yuv_dt); + base::Optional<SkYUVAPixmapInfo> yuva_pixmap_info); bool IsGpuOrTransferCache() const; bool HasUploadedData() const; @@ -537,11 +534,8 @@ class CC_EXPORT GpuImageDecodeCache int upload_scale_mip_level; bool needs_mips = false; bool is_bitmap_backed; - bool is_yuv; bool is_budgeted = false; - base::Optional<SkYUVColorSpace> yuv_color_space; - base::Optional<SkYUVAInfo::PlanarConfig> yuv_planar_config; - base::Optional<SkYUVAPixmapInfo::DataType> yuv_data_type; + base::Optional<SkYUVAPixmapInfo> yuva_pixmap_info; // If true, this image is no longer in our |persistent_cache_| and will be // deleted as soon as its ref count reaches zero. @@ -571,12 +565,12 @@ class CC_EXPORT GpuImageDecodeCache // the |in_use_cache_|. struct InUseCacheKeyHash; struct InUseCacheKey { - static InUseCacheKey FromDrawImage(const DrawImage& draw_image); + InUseCacheKey(const DrawImage& draw_image, int mip_level); + bool operator==(const InUseCacheKey& other) const; private: friend struct GpuImageDecodeCache::InUseCacheKeyHash; - explicit InUseCacheKey(const DrawImage& draw_image); PaintImage::FrameKey frame_key; int upload_scale_mip_level; @@ -591,6 +585,13 @@ class CC_EXPORT GpuImageDecodeCache // functions also require the |context_| lock. These are indicated by // additional comments. + // Calculate the mip level to upload-scale the image to before uploading. We + // use mip levels rather than exact scales to increase re-use of scaled + // images. + int CalculateUploadScaleMipLevel(const DrawImage& draw_image) const; + + InUseCacheKey InUseCacheKeyFromDrawImage(const DrawImage& draw_image) const; + // Similar to GetTaskForImageAndRef, but gets the dependent decode task // rather than the upload task, if necessary. scoped_refptr<TileTask> GetImageDecodeTaskAndRef( @@ -642,7 +643,8 @@ class CC_EXPORT GpuImageDecodeCache const SkImage* uploaded_v_image, const size_t image_width, const size_t image_height, - const SkYUVAInfo::PlanarConfig yuva_planar_config, + const SkYUVAInfo::PlaneConfig yuva_plane_config, + const SkYUVAInfo::Subsampling yuva_subsampling, const SkYUVColorSpace yuva_color_space, sk_sp<SkColorSpace> target_color_space, sk_sp<SkColorSpace> decoded_color_space) const; diff --git a/chromium/cc/tiles/gpu_image_decode_cache_perftest.cc b/chromium/cc/tiles/gpu_image_decode_cache_perftest.cc index 20725f8c49a..a2c6d6833ab 100644 --- a/chromium/cc/tiles/gpu_image_decode_cache_perftest.cc +++ b/chromium/cc/tiles/gpu_image_decode_cache_perftest.cc @@ -154,11 +154,11 @@ TEST_P(GpuImageDecodeCachePerfTestNoSw, DecodeWithMips) { DecodedDrawImage decoded_image = cache_->GetDecodedImageForDraw(image); if (GetParam() == TestMode::kGpu) { - SkPaint paint; - paint.setFilterQuality(kMedium_SkFilterQuality); - surface->getCanvas()->drawImageRect(decoded_image.image().get(), - SkRect::MakeWH(1024, 2048), - SkRect::MakeWH(614, 1229), &paint); + SkSamplingOptions sampling(SkFilterMode::kLinear, SkMipmapMode::kLinear); + surface->getCanvas()->drawImageRect( + decoded_image.image().get(), SkRect::MakeWH(1024, 2048), + SkRect::MakeWH(614, 1229), sampling, nullptr, + SkCanvas::kStrict_SrcRectConstraint); surface->flushAndSubmit(); } diff --git a/chromium/cc/tiles/gpu_image_decode_cache_unittest.cc b/chromium/cc/tiles/gpu_image_decode_cache_unittest.cc index 79ebeeeade8..bbdcfc15716 100644 --- a/chromium/cc/tiles/gpu_image_decode_cache_unittest.cc +++ b/chromium/cc/tiles/gpu_image_decode_cache_unittest.cc @@ -583,7 +583,7 @@ class GpuImageDecodeCacheTest DecodedDrawImage new_draw_image( image_entry->image(), draw_image.dark_mode_color_filter(), draw_image.src_rect_offset(), draw_image.scale_adjustment(), - draw_image.filter_quality()); + draw_image.filter_quality(), draw_image.is_budgeted()); return new_draw_image; } @@ -1149,6 +1149,7 @@ TEST_P(GpuImageDecodeCacheTest, GetDecodedImageForDraw) { EnsureImageBacked(cache->GetDecodedImageForDraw(draw_image)); EXPECT_TRUE(decoded_draw_image.image()); EXPECT_TRUE(decoded_draw_image.image()->isTextureBacked()); + EXPECT_TRUE(decoded_draw_image.is_budgeted()); EXPECT_FALSE(cache->DiscardableIsLockedForTesting(draw_image)); cache->DrawWithImageFinished(draw_image, decoded_draw_image); @@ -1194,6 +1195,7 @@ TEST_P(GpuImageDecodeCacheTest, GetHdrDecodedImageForDrawToHdr) { EnsureImageBacked(cache->GetDecodedImageForDraw(draw_image)); EXPECT_TRUE(decoded_draw_image.image()); EXPECT_TRUE(decoded_draw_image.image()->isTextureBacked()); + EXPECT_TRUE(decoded_draw_image.is_budgeted()); EXPECT_EQ(decoded_draw_image.image()->colorType(), kRGBA_F16_SkColorType); auto cs = gfx::ColorSpace(*decoded_draw_image.image()->colorSpace()); @@ -1244,6 +1246,7 @@ TEST_P(GpuImageDecodeCacheTest, GetHdrDecodedImageForDrawToSdr) { EnsureImageBacked(cache->GetDecodedImageForDraw(draw_image)); EXPECT_TRUE(decoded_draw_image.image()); EXPECT_TRUE(decoded_draw_image.image()->isTextureBacked()); + EXPECT_TRUE(decoded_draw_image.is_budgeted()); EXPECT_NE(decoded_draw_image.image()->colorType(), kRGBA_F16_SkColorType); EXPECT_FALSE(cache->DiscardableIsLockedForTesting(draw_image)); @@ -1271,6 +1274,7 @@ TEST_P(GpuImageDecodeCacheTest, GetLargeDecodedImageForDraw) { DecodedDrawImage decoded_draw_image = EnsureImageBacked(cache->GetDecodedImageForDraw(draw_image)); EXPECT_TRUE(decoded_draw_image.image()); + EXPECT_TRUE(decoded_draw_image.is_budgeted()); EXPECT_FALSE(decoded_draw_image.image()->isTextureBacked()); EXPECT_TRUE_IF_NOT_USING_TRANSFER_CACHE( cache->DiscardableIsLockedForTesting(draw_image)); @@ -1299,7 +1303,9 @@ TEST_P(GpuImageDecodeCacheTest, GetDecodedImageForDrawAtRasterDecode) { DecodedDrawImage decoded_draw_image = EnsureImageBacked(cache->GetDecodedImageForDraw(draw_image)); EXPECT_TRUE(decoded_draw_image.image()); + EXPECT_FALSE(decoded_draw_image.is_budgeted()); EXPECT_TRUE(decoded_draw_image.image()->isTextureBacked()); + EXPECT_FALSE(decoded_draw_image.is_budgeted()); EXPECT_FALSE(cache->DiscardableIsLockedForTesting(draw_image)); cache->DrawWithImageFinished(draw_image, decoded_draw_image); @@ -1338,6 +1344,7 @@ TEST_P(GpuImageDecodeCacheTest, GetDecodedImageForDrawLargerScale) { viz::ContextProvider::ScopedContextLock context_lock(context_provider()); DecodedDrawImage decoded_draw_image = EnsureImageBacked(cache->GetDecodedImageForDraw(draw_image)); + EXPECT_TRUE(decoded_draw_image.is_budgeted()); EXPECT_TRUE(decoded_draw_image.image()); EXPECT_TRUE(decoded_draw_image.image()->isTextureBacked()); EXPECT_FALSE(cache->DiscardableIsLockedForTesting(draw_image)); @@ -1345,6 +1352,7 @@ TEST_P(GpuImageDecodeCacheTest, GetDecodedImageForDrawLargerScale) { DecodedDrawImage larger_decoded_draw_image = EnsureImageBacked(cache->GetDecodedImageForDraw(larger_draw_image)); EXPECT_TRUE(larger_decoded_draw_image.image()); + EXPECT_TRUE(larger_decoded_draw_image.is_budgeted()); EXPECT_TRUE(larger_decoded_draw_image.image()->isTextureBacked()); EXPECT_FALSE(cache->DiscardableIsLockedForTesting(draw_image)); @@ -1384,6 +1392,7 @@ TEST_P(GpuImageDecodeCacheTest, GetDecodedImageForDrawHigherQuality) { DecodedDrawImage decoded_draw_image = EnsureImageBacked(cache->GetDecodedImageForDraw(draw_image)); EXPECT_TRUE(decoded_draw_image.image()); + EXPECT_TRUE(decoded_draw_image.is_budgeted()); EXPECT_TRUE(decoded_draw_image.image()->isTextureBacked()); EXPECT_FALSE(cache->DiscardableIsLockedForTesting(draw_image)); @@ -1421,6 +1430,7 @@ TEST_P(GpuImageDecodeCacheTest, GetDecodedImageForDrawNegative) { DecodedDrawImage decoded_draw_image = EnsureImageBacked(cache->GetDecodedImageForDraw(draw_image)); EXPECT_TRUE(decoded_draw_image.image()); + EXPECT_TRUE(decoded_draw_image.is_budgeted()); const int expected_width = image.width() * std::abs(draw_image.scale().width()); const int expected_height = @@ -1455,6 +1465,7 @@ TEST_P(GpuImageDecodeCacheTest, GetLargeScaledDecodedImageForDraw) { DecodedDrawImage decoded_draw_image = EnsureImageBacked(cache->GetDecodedImageForDraw(draw_image)); EXPECT_TRUE(decoded_draw_image.image()); + EXPECT_TRUE(decoded_draw_image.is_budgeted()); // The mip level scale should never go below 0 in any dimension. EXPECT_EQ(GetLargeImageSize().width(), decoded_draw_image.image()->width()); EXPECT_EQ(GetLargeImageSize().height(), decoded_draw_image.image()->height()); @@ -1490,6 +1501,7 @@ TEST_P(GpuImageDecodeCacheTest, AtRasterUsedDirectlyIfSpaceAllows) { EnsureImageBacked(cache->GetDecodedImageForDraw(draw_image)); EXPECT_TRUE(decoded_draw_image.image()); EXPECT_TRUE(decoded_draw_image.image()->isTextureBacked()); + EXPECT_FALSE(decoded_draw_image.is_budgeted()); EXPECT_FALSE(cache->DiscardableIsLockedForTesting(draw_image)); cache->DrawWithImageFinished(draw_image, decoded_draw_image); @@ -1522,10 +1534,12 @@ TEST_P(GpuImageDecodeCacheTest, EnsureImageBacked(cache->GetDecodedImageForDraw(draw_image)); EXPECT_TRUE(decoded_draw_image.image()); EXPECT_TRUE(decoded_draw_image.image()->isTextureBacked()); + EXPECT_FALSE(decoded_draw_image.is_budgeted()); EXPECT_FALSE(cache->DiscardableIsLockedForTesting(draw_image)); DecodedDrawImage another_decoded_draw_image = EnsureImageBacked(cache->GetDecodedImageForDraw(draw_image)); + EXPECT_FALSE(another_decoded_draw_image.is_budgeted()); EXPECT_EQ(decoded_draw_image.image()->uniqueID(), another_decoded_draw_image.image()->uniqueID()); @@ -1551,6 +1565,7 @@ TEST_P(GpuImageDecodeCacheTest, DecodedDrawImage decoded_draw_image = EnsureImageBacked(cache->GetDecodedImageForDraw(draw_image)); EXPECT_TRUE(decoded_draw_image.image()); + EXPECT_FALSE(decoded_draw_image.is_budgeted()); EXPECT_FALSE(decoded_draw_image.image()->isTextureBacked()); EXPECT_TRUE_IF_NOT_USING_TRANSFER_CACHE( cache->DiscardableIsLockedForTesting(draw_image)); @@ -1561,6 +1576,7 @@ TEST_P(GpuImageDecodeCacheTest, DecodedDrawImage second_decoded_draw_image = EnsureImageBacked(cache->GetDecodedImageForDraw(draw_image)); EXPECT_TRUE(second_decoded_draw_image.image()); + EXPECT_FALSE(decoded_draw_image.is_budgeted()); EXPECT_FALSE(second_decoded_draw_image.image()->isTextureBacked()); EXPECT_TRUE_IF_NOT_USING_TRANSFER_CACHE( cache->DiscardableIsLockedForTesting(draw_image)); @@ -2244,6 +2260,7 @@ TEST_P(GpuImageDecodeCacheTest, AlreadyBudgetedImagesAreNotAtRaster) { viz::ContextProvider::ScopedContextLock context_lock(context_provider()); DecodedDrawImage decoded_draw_image = EnsureImageBacked(cache->GetDecodedImageForDraw(draw_image)); + EXPECT_TRUE(decoded_draw_image.is_budgeted()); cache->DrawWithImageFinished(draw_image, decoded_draw_image); cache->UnrefImage(draw_image); cache->UnrefImage(draw_image); @@ -2267,6 +2284,7 @@ TEST_P(GpuImageDecodeCacheTest, ImageBudgetingByCount) { DecodedDrawImage decoded_draw_image = EnsureImageBacked(cache->GetDecodedImageForDraw(draw_image)); EXPECT_TRUE(decoded_draw_image.image()); + EXPECT_TRUE(decoded_draw_image.is_budgeted()); // Try another image, it shouldn't be budgeted and should be at-raster. PaintImage second_paint_image = @@ -2282,6 +2300,7 @@ TEST_P(GpuImageDecodeCacheTest, ImageBudgetingByCount) { DecodedDrawImage second_decoded_draw_image = EnsureImageBacked(cache->GetDecodedImageForDraw(second_draw_image)); EXPECT_TRUE(second_decoded_draw_image.image()); + EXPECT_FALSE(second_decoded_draw_image.is_budgeted()); cache->DrawWithImageFinished(draw_image, decoded_draw_image); cache->DrawWithImageFinished(second_draw_image, second_decoded_draw_image); @@ -2307,6 +2326,7 @@ TEST_P(GpuImageDecodeCacheTest, ImageBudgetingBySize) { DecodedDrawImage decoded_draw_image = EnsureImageBacked(cache->GetDecodedImageForDraw(draw_image)); EXPECT_TRUE(decoded_draw_image.image()); + EXPECT_TRUE(decoded_draw_image.is_budgeted()); // Try another image, it shouldn't be budgeted and should be at-raster. PaintImage test_paint_image = CreatePaintImageInternal(test_image_size); @@ -2321,6 +2341,7 @@ TEST_P(GpuImageDecodeCacheTest, ImageBudgetingBySize) { DecodedDrawImage second_decoded_draw_image = EnsureImageBacked(cache->GetDecodedImageForDraw(second_draw_image)); EXPECT_TRUE(second_decoded_draw_image.image()); + EXPECT_FALSE(second_decoded_draw_image.is_budgeted()); cache->DrawWithImageFinished(draw_image, decoded_draw_image); cache->DrawWithImageFinished(second_draw_image, second_decoded_draw_image); @@ -2438,6 +2459,7 @@ TEST_P(GpuImageDecodeCacheTest, NonLazyImageUploadNoScale) { DecodedDrawImage decoded_draw_image = EnsureImageBacked(cache->GetDecodedImageForDraw(draw_image)); EXPECT_TRUE(decoded_draw_image.image()); + EXPECT_TRUE(decoded_draw_image.is_budgeted()); cache->DrawWithImageFinished(draw_image, decoded_draw_image); // For non-lazy images used at the original scale, no cpu component should be // cached @@ -2500,6 +2522,7 @@ TEST_P(GpuImageDecodeCacheTest, NonLazyImageLargeImageColorConverted) { DecodedDrawImage decoded_draw_image = EnsureImageBacked(cache->GetDecodedImageForDraw(draw_image)); EXPECT_TRUE(decoded_draw_image.image()); + EXPECT_TRUE(decoded_draw_image.is_budgeted()); cache->DrawWithImageFinished(draw_image, decoded_draw_image); // For non-lazy images color converted during scaling, cpu component should be // cached. @@ -2525,6 +2548,7 @@ TEST_P(GpuImageDecodeCacheTest, NonLazyImageUploadDownscaled) { DecodedDrawImage decoded_draw_image = EnsureImageBacked(cache->GetDecodedImageForDraw(draw_image)); EXPECT_TRUE(decoded_draw_image.image()); + EXPECT_TRUE(decoded_draw_image.is_budgeted()); cache->DrawWithImageFinished(draw_image, decoded_draw_image); } @@ -3385,6 +3409,7 @@ TEST_P(GpuImageDecodeCacheTest, GetBorderlineLargeDecodedImageForDraw) { EnsureImageBacked(cache->GetDecodedImageForDraw(draw_image)); EXPECT_TRUE(decoded_draw_image.image()); EXPECT_TRUE(decoded_draw_image.image()->isTextureBacked()); + EXPECT_TRUE(decoded_draw_image.is_budgeted()); EXPECT_FALSE(cache->DiscardableIsLockedForTesting(draw_image)); cache->DrawWithImageFinished(draw_image, decoded_draw_image); diff --git a/chromium/cc/tiles/image_decode_cache_utils.cc b/chromium/cc/tiles/image_decode_cache_utils.cc index fb0e1bc04ca..8dbd6b32d45 100644 --- a/chromium/cc/tiles/image_decode_cache_utils.cc +++ b/chromium/cc/tiles/image_decode_cache_utils.cc @@ -41,7 +41,10 @@ bool ImageDecodeCacheUtils::ScaleToHalfFloatPixmapUsingN32Intermediate( n32_pixmap.info().makeWH(scaled_pixmap->width(), scaled_pixmap->height()); if (!n32_resized_bitmap.tryAllocPixels(n32_resize_info)) return false; - if (!n32_pixmap.scalePixels(n32_resized_bitmap.pixmap(), filter_quality)) + if (!n32_pixmap.scalePixels( + n32_resized_bitmap.pixmap(), + SkSamplingOptions(filter_quality, + SkSamplingOptions::kMedium_asMipmapLinear))) return false; // Convert back to f16 and return return n32_resized_bitmap.readPixels(*scaled_pixmap, 0, 0); diff --git a/chromium/cc/tiles/picture_layer_tiling.cc b/chromium/cc/tiles/picture_layer_tiling.cc index 25c0e84378a..d0c83d660b7 100644 --- a/chromium/cc/tiles/picture_layer_tiling.cc +++ b/chromium/cc/tiles/picture_layer_tiling.cc @@ -83,7 +83,7 @@ Tile* PictureLayerTiling::CreateTile(const Tile::CreateInfo& info) { TileMapKey key(i, j); DCHECK(tiles_.find(key) == tiles_.end()); - if (!raster_source_->CoversRect(info.enclosing_layer_rect, *client_)) + if (!raster_source_->IntersectsRect(info.enclosing_layer_rect, *client_)) return nullptr; all_tiles_done_ = false; @@ -355,8 +355,8 @@ bool PictureLayerTiling::ShouldCreateTileAt( // If the active tree can't create a tile, because of its raster source, then // the pending tree should create one. - if (!active_twin->raster_source()->CoversRect(info.enclosing_layer_rect, - *active_twin->client())) + if (!active_twin->raster_source()->IntersectsRect(info.enclosing_layer_rect, + *active_twin->client())) return true; const Region* layer_invalidation = client_->GetPendingInvalidation(); @@ -879,7 +879,8 @@ PrioritizedTile PictureLayerTiling::MakePrioritizedTile( Tile* tile, PriorityRectType priority_rect_type) const { DCHECK(tile); - DCHECK(raster_source()->CoversRect(tile->enclosing_layer_rect(), *client_)) + DCHECK( + raster_source()->IntersectsRect(tile->enclosing_layer_rect(), *client_)) << "Recording rect: " << EnclosingLayerRectFromContentsRect(tile->content_rect()).ToString(); diff --git a/chromium/cc/tiles/picture_layer_tiling_set.cc b/chromium/cc/tiles/picture_layer_tiling_set.cc index aec6390cc19..f958c316e26 100644 --- a/chromium/cc/tiles/picture_layer_tiling_set.cc +++ b/chromium/cc/tiles/picture_layer_tiling_set.cc @@ -13,6 +13,7 @@ #include <utility> #include <vector> +#include "base/containers/contains.h" #include "base/memory/ptr_util.h" #include "base/stl_util.h" #include "base/trace_event/trace_event.h" diff --git a/chromium/cc/tiles/software_image_decode_cache.cc b/chromium/cc/tiles/software_image_decode_cache.cc index 7ae825ab67a..d7e6678eead 100644 --- a/chromium/cc/tiles/software_image_decode_cache.cc +++ b/chromium/cc/tiles/software_image_decode_cache.cc @@ -565,9 +565,10 @@ DecodedDrawImage SoftwareImageDecodeCache::GetDecodedImageForDrawInternal( if (!decoded_image) return DecodedDrawImage(); - auto decoded_draw_image = DecodedDrawImage( - std::move(decoded_image), nullptr, cache_entry->src_rect_offset(), - GetScaleAdjustment(key), GetDecodedFilterQuality(key)); + auto decoded_draw_image = + DecodedDrawImage(std::move(decoded_image), nullptr, + cache_entry->src_rect_offset(), GetScaleAdjustment(key), + GetDecodedFilterQuality(key), cache_entry->is_budgeted); return decoded_draw_image; } diff --git a/chromium/cc/tiles/software_image_decode_cache_utils.cc b/chromium/cc/tiles/software_image_decode_cache_utils.cc index 9f04ed5ff32..93598178fd7 100644 --- a/chromium/cc/tiles/software_image_decode_cache_utils.cc +++ b/chromium/cc/tiles/software_image_decode_cache_utils.cc @@ -154,7 +154,10 @@ SoftwareImageDecodeCacheUtils::GenerateCacheEntryFromCandidate( result = ImageDecodeCacheUtils::ScaleToHalfFloatPixmapUsingN32Intermediate( decoded_pixmap, &target_pixmap, filter_quality); } else { - result = decoded_pixmap.scalePixels(target_pixmap, filter_quality); + result = decoded_pixmap.scalePixels( + target_pixmap, + SkSamplingOptions(filter_quality, + SkSamplingOptions::kMedium_asMipmapLinear)); } DCHECK(result) << key.ToString(); diff --git a/chromium/cc/tiles/tile_manager.cc b/chromium/cc/tiles/tile_manager.cc index c6c7cd5bd79..474ac906a21 100644 --- a/chromium/cc/tiles/tile_manager.cc +++ b/chromium/cc/tiles/tile_manager.cc @@ -338,18 +338,21 @@ class DidFinishRunningAllTilesTask : public TileTask { public: using CompletionCb = base::OnceCallback<void(bool has_pending_queries)>; DidFinishRunningAllTilesTask(base::SequencedTaskRunner* task_runner, - RasterBufferProvider* raster_buffer_provider, + RasterQueryQueue* pending_raster_queries, CompletionCb completion_cb) : TileTask(TileTask::SupportsConcurrentExecution::kNo, TileTask::SupportsBackgroundThreadPriority::kYes), task_runner_(task_runner), - raster_buffer_provider_(raster_buffer_provider), + pending_raster_queries_(pending_raster_queries), completion_cb_(std::move(completion_cb)) {} void RunOnWorkerThread() override { TRACE_EVENT0("cc", "TaskSetFinishedTaskImpl::RunOnWorkerThread"); - bool has_pending_queries = - raster_buffer_provider_->CheckRasterFinishedQueries(); + bool has_pending_queries = false; + if (pending_raster_queries_) { + has_pending_queries = + pending_raster_queries_->CheckRasterFinishedQueries(); + } task_runner_->PostTask(FROM_HERE, base::BindOnce(std::move(completion_cb_), has_pending_queries)); } @@ -361,7 +364,7 @@ class DidFinishRunningAllTilesTask : public TileTask { private: base::SequencedTaskRunner* task_runner_; - RasterBufferProvider* raster_buffer_provider_; + RasterQueryQueue* pending_raster_queries_; CompletionCb completion_cb_; }; @@ -465,12 +468,14 @@ void TileManager::SetResources(ResourcePool* resource_pool, TaskGraphRunner* task_graph_runner, RasterBufferProvider* raster_buffer_provider, bool use_gpu_rasterization, - bool use_oop_rasterization) { + bool use_oop_rasterization, + RasterQueryQueue* pending_raster_queries) { DCHECK(!tile_task_manager_); DCHECK(task_graph_runner); use_gpu_rasterization_ = use_gpu_rasterization; use_oop_rasterization_ = use_oop_rasterization; + pending_raster_queries_ = pending_raster_queries; resource_pool_ = resource_pool; image_controller_.SetImageDecodeCache(image_decode_cache); tile_task_manager_ = TileTaskManagerImpl::Create(task_graph_runner); @@ -1020,7 +1025,7 @@ void TileManager::ScheduleTasks(PrioritizedWorkToSchedule work_to_schedule) { task_set_finished_weak_ptr_factory_.GetWeakPtr()); scoped_refptr<TileTask> all_done_task = base::MakeRefCounted<DidFinishRunningAllTilesTask>( - task_runner_, raster_buffer_provider_, std::move(all_done_cb)); + task_runner_, pending_raster_queries_, std::move(all_done_cb)); // Build a new task queue containing all task currently needed. Tasks // are added in order of priority, highest priority task first. @@ -1477,7 +1482,11 @@ void TileManager::CheckRasterFinishedQueries() { if (has_scheduled_tile_tasks_ || !signals_.all_tile_tasks_completed) return; - has_pending_queries_ = raster_buffer_provider_->CheckRasterFinishedQueries(); + has_pending_queries_ = false; + if (pending_raster_queries_) { + has_pending_queries_ = + pending_raster_queries_->CheckRasterFinishedQueries(); + } if (has_pending_queries_) ScheduleCheckRasterFinishedQueries(); } diff --git a/chromium/cc/tiles/tile_manager.h b/chromium/cc/tiles/tile_manager.h index 0495ba0865a..39cf268672a 100644 --- a/chromium/cc/tiles/tile_manager.h +++ b/chromium/cc/tiles/tile_manager.h @@ -19,6 +19,7 @@ #include "base/values.h" #include "cc/base/unique_notifier.h" #include "cc/raster/raster_buffer_provider.h" +#include "cc/raster/raster_query_queue.h" #include "cc/raster/raster_source.h" #include "cc/resources/memory_history.h" #include "cc/resources/resource_pool.h" @@ -180,7 +181,8 @@ class CC_EXPORT TileManager : CheckerImageTrackerClient { TaskGraphRunner* task_graph_runner, RasterBufferProvider* raster_buffer_provider, bool use_gpu_rasterization, - bool use_oop_rasterization); + bool use_oop_rasterization, + RasterQueryQueue* pending_raster_queries); // This causes any completed raster work to finalize, so that tiles get up to // date draw information. @@ -260,6 +262,11 @@ class CC_EXPORT TileManager : CheckerImageTrackerClient { raster_buffer_provider_ = raster_buffer_provider; } + void SetPendingRasterQueriesForTesting( + RasterQueryQueue* pending_raster_queries) { + pending_raster_queries_ = pending_raster_queries; + } + std::vector<Tile*> AllTilesForTesting() const { std::vector<Tile*> tiles; for (auto& tile_pair : tiles_) @@ -442,6 +449,7 @@ class CC_EXPORT TileManager : CheckerImageTrackerClient { const TileManagerSettings tile_manager_settings_; bool use_gpu_rasterization_; bool use_oop_rasterization_; + RasterQueryQueue* pending_raster_queries_ = nullptr; std::unordered_map<Tile::Id, Tile*> tiles_; diff --git a/chromium/cc/tiles/tile_manager_unittest.cc b/chromium/cc/tiles/tile_manager_unittest.cc index e557b0976c2..b263d53826d 100644 --- a/chromium/cc/tiles/tile_manager_unittest.cc +++ b/chromium/cc/tiles/tile_manager_unittest.cc @@ -10,8 +10,8 @@ #include "base/bind.h" #include "base/callback.h" #include "base/callback_helpers.h" +#include "base/containers/contains.h" #include "base/run_loop.h" -#include "base/stl_util.h" #include "base/test/test_simple_task_runner.h" #include "base/threading/thread_task_runner_handle.h" #include "cc/layers/recording_source.h" @@ -26,6 +26,7 @@ #include "cc/test/fake_paint_image_generator.h" #include "cc/test/fake_picture_layer_impl.h" #include "cc/test/fake_picture_layer_tiling_client.h" +#include "cc/test/fake_raster_query_queue.h" #include "cc/test/fake_raster_source.h" #include "cc/test/fake_recording_source.h" #include "cc/test/fake_tile_manager.h" @@ -3520,23 +3521,31 @@ TEST_F(HdrImageTileManagerTest, DecodeHdrImagesToSdrP3) { class TileManagerCheckRasterQueriesTest : public TileManagerTest { public: + TileManagerCheckRasterQueriesTest() + : pending_raster_queries_(viz::TestContextProvider::CreateWorker().get(), + /*oop_rasterization_enabled=*/false) {} ~TileManagerCheckRasterQueriesTest() override { // Ensure that the host impl doesn't outlive |raster_buffer_provider_|. TakeHostImpl(); } void SetUp() override { TileManagerTest::SetUp(); - host_impl()->tile_manager()->SetRasterBufferProviderForTesting( - &raster_buffer_provider_); + host_impl()->tile_manager()->SetPendingRasterQueriesForTesting( + &pending_raster_queries_); } protected: - class MockRasterBufferProvider : public FakeRasterBufferProviderImpl { + class MockRasterQueryQueue : public FakeRasterQueryQueue { public: + MockRasterQueryQueue( + viz::RasterContextProvider* const worker_context_provider, + bool oop_rasterization_enabled) + : FakeRasterQueryQueue(worker_context_provider, + oop_rasterization_enabled) {} MOCK_METHOD0(CheckRasterFinishedQueries, bool()); }; - MockRasterBufferProvider raster_buffer_provider_; + MockRasterQueryQueue pending_raster_queries_; }; TEST_F(TileManagerCheckRasterQueriesTest, @@ -3545,7 +3554,7 @@ TEST_F(TileManagerCheckRasterQueriesTest, EXPECT_FALSE(host_impl()->tile_manager()->HasScheduledTileTasksForTesting()); EXPECT_CALL(MockHostImpl(), NotifyAllTileTasksCompleted()) .WillOnce(testing::Invoke([&run_loop]() { run_loop.Quit(); })); - EXPECT_CALL(raster_buffer_provider_, CheckRasterFinishedQueries()).Times(1); + EXPECT_CALL(pending_raster_queries_, CheckRasterFinishedQueries()).Times(1); host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state()); EXPECT_TRUE(host_impl()->tile_manager()->HasScheduledTileTasksForTesting()); run_loop.Run(); diff --git a/chromium/cc/trees/animated_paint_worklet_tracker.cc b/chromium/cc/trees/animated_paint_worklet_tracker.cc index 36b6d18f8a8..b312c24180d 100644 --- a/chromium/cc/trees/animated_paint_worklet_tracker.cc +++ b/chromium/cc/trees/animated_paint_worklet_tracker.cc @@ -4,6 +4,10 @@ #include "cc/trees/animated_paint_worklet_tracker.h" +#include <string> +#include <utility> +#include <vector> + #include "cc/layers/picture_layer_impl.h" namespace cc { @@ -25,25 +29,22 @@ AnimatedPaintWorkletTracker::PropertyState::PropertyState( AnimatedPaintWorkletTracker::PropertyState::~PropertyState() = default; void AnimatedPaintWorkletTracker::OnCustomPropertyMutated( - ElementId element_id, - const std::string& custom_property_name, - PaintWorkletInput::PropertyValue custom_property_value) { - // This function is called to update custom property value only. - DCHECK(!custom_property_name.empty()); - PaintWorkletInput::PropertyKey key{custom_property_name, element_id}; - auto iter = input_properties_.find(key); + PaintWorkletInput::PropertyKey property_key, + PaintWorkletInput::PropertyValue property_value) { + auto iter = input_properties_.find(property_key); // OnCustomPropertyMutated is called for all composited custom property - // animations, but there may not be a matching PaintWorklet, and thus no entry + // animations and some types of native properties that uses the paint worklet + // infra, but there may not be a matching PaintWorklet, and thus no entry // in |input_properties_|. // TODO(xidachen): Only create composited custom property animations if they // affect paint worklet. if (iter == input_properties_.end()) return; - iter->second.animation_value = std::move(custom_property_value); + iter->second.animation_value = std::move(property_value); // Keep track of which input properties have been changed so that the // associated PaintWorklets can be invalidated before activating the pending // tree. - input_properties_animated_on_impl_.insert(key); + input_properties_animated_on_impl_.insert(property_key); } bool AnimatedPaintWorkletTracker::InvalidatePaintWorkletsOnPendingTree() { diff --git a/chromium/cc/trees/animated_paint_worklet_tracker.h b/chromium/cc/trees/animated_paint_worklet_tracker.h index 996a6edc2f6..353daa78e2a 100644 --- a/chromium/cc/trees/animated_paint_worklet_tracker.h +++ b/chromium/cc/trees/animated_paint_worklet_tracker.h @@ -6,6 +6,7 @@ #define CC_TREES_ANIMATED_PAINT_WORKLET_TRACKER_H_ #include <memory> +#include <vector> #include "base/containers/flat_map.h" #include "base/containers/flat_set.h" @@ -46,10 +47,8 @@ class CC_EXPORT AnimatedPaintWorkletTracker { // Called when the value of a property is changed by the CC animation system. // Responsible for updating the property value in |input_properties_|, and // marking any relevant PaintWorkletInputs as needs-invalidation. - void OnCustomPropertyMutated( - ElementId element_id, - const std::string& custom_property_name, - PaintWorkletInput::PropertyValue custom_property_value); + void OnCustomPropertyMutated(PaintWorkletInput::PropertyKey property_key, + PaintWorkletInput::PropertyValue property_value); // Invalidate all the paint worklets that uses the set of dirtied properties. // Returns whether the set of dirtied properties is empty or not. bool InvalidatePaintWorkletsOnPendingTree(); diff --git a/chromium/cc/trees/compositor_commit_data.cc b/chromium/cc/trees/compositor_commit_data.cc index 939ed2d8347..1dd546bb327 100644 --- a/chromium/cc/trees/compositor_commit_data.cc +++ b/chromium/cc/trees/compositor_commit_data.cc @@ -8,15 +8,7 @@ namespace cc { -CompositorCommitData::CompositorCommitData() - : page_scale_delta(1.f), - is_pinch_gesture_active(false), - top_controls_delta(0.f), - bottom_controls_delta(0.f), - browser_controls_constraint(BrowserControlsState::kBoth), - browser_controls_constraint_changed(false), - scroll_gesture_did_end(false), - manipulation_info(kManipulationInfoNone) {} +CompositorCommitData::CompositorCommitData() = default; CompositorCommitData::~CompositorCommitData() = default; diff --git a/chromium/cc/trees/compositor_commit_data.h b/chromium/cc/trees/compositor_commit_data.h index ccee2259983..c55747cad1a 100644 --- a/chromium/cc/trees/compositor_commit_data.h +++ b/chromium/cc/trees/compositor_commit_data.h @@ -57,8 +57,9 @@ struct CC_EXPORT CompositorCommitData { ScrollUpdateInfo inner_viewport_scroll; std::vector<ScrollUpdateInfo> scrolls; - float page_scale_delta; - bool is_pinch_gesture_active; + float page_scale_delta = 1.f; + bool is_pinch_gesture_active = false; + bool is_scroll_active = false; // Elastic overscroll effect offset delta. This is used only on Mac and shows // the pixels that the page is rubber-banned/stretched by. @@ -72,8 +73,8 @@ struct CC_EXPORT CompositorCommitData { // send overscroll/scrollend DOM events to proper targets whenever needed. ElementId scroll_latched_element_id; - float top_controls_delta; - float bottom_controls_delta; + float top_controls_delta = 0.f; + float bottom_controls_delta = 0.f; // Used to communicate scrollbar visibility from Impl thread to Blink. // Scrollbar input is handled by Blink but the compositor thread animates @@ -91,12 +92,13 @@ struct CC_EXPORT CompositorCommitData { std::vector<ScrollbarsUpdateInfo> scrollbars; std::vector<std::unique_ptr<SwapPromise>> swap_promises; - BrowserControlsState browser_controls_constraint; - bool browser_controls_constraint_changed; + BrowserControlsState browser_controls_constraint = + BrowserControlsState::kBoth; + bool browser_controls_constraint_changed = false; // Set to true when a scroll gesture being handled on the compositor has // ended. - bool scroll_gesture_did_end; + bool scroll_gesture_did_end = false; // Tracks whether there is an ongoing compositor-driven animation for a // scroll. @@ -104,7 +106,7 @@ struct CC_EXPORT CompositorCommitData { // Tracks different methods of scrolling (e.g. wheel, touch, precision // touchpad, etc.). - ManipulationInfo manipulation_info; + ManipulationInfo manipulation_info = kManipulationInfoNone; }; } // namespace cc diff --git a/chromium/cc/trees/damage_tracker.cc b/chromium/cc/trees/damage_tracker.cc index 48499b8b57e..a86bf950bec 100644 --- a/chromium/cc/trees/damage_tracker.cc +++ b/chromium/cc/trees/damage_tracker.cc @@ -106,11 +106,6 @@ void DamageTracker::UpdateDamageTracking(LayerTreeImpl* layer_tree_impl) { render_surface->damage_tracker()->PrepareForUpdate(); } - // Surfaces with backdrop blur filter that might be potentially optimized with - // caching, paired with each's surface rect in target space. - std::vector<std::pair<RenderSurfaceImpl*, gfx::Rect>> - surfaces_with_backdrop_blur_filter; - EffectTree& effect_tree = layer_tree_impl->property_trees()->effect_tree; int current_target_effect_id = EffectTree::kContentsRootNodeId; DCHECK(effect_tree.GetRenderSurface(current_target_effect_id)); @@ -131,11 +126,10 @@ void DamageTracker::UpdateDamageTracking(LayerTreeImpl* layer_tree_impl) { // in draw order. RenderSurfaceImpl* current_target = effect_tree.GetRenderSurface(current_target_effect_id); - current_target->damage_tracker()->ComputeSurfaceDamage( - current_target, surfaces_with_backdrop_blur_filter); + current_target->damage_tracker()->ComputeSurfaceDamage(current_target); RenderSurfaceImpl* parent_target = current_target->render_target(); parent_target->damage_tracker()->AccumulateDamageFromRenderSurface( - current_target, surfaces_with_backdrop_blur_filter); + current_target); current_target_effect_id = effect_tree.Node(current_target_effect_id)->target_id; } @@ -156,23 +150,17 @@ void DamageTracker::UpdateDamageTracking(LayerTreeImpl* layer_tree_impl) { RenderSurfaceImpl* current_target = effect_tree.GetRenderSurface(current_target_effect_id); while (true) { - current_target->damage_tracker()->ComputeSurfaceDamage( - current_target, surfaces_with_backdrop_blur_filter); + current_target->damage_tracker()->ComputeSurfaceDamage(current_target); if (current_target->EffectTreeIndex() == EffectTree::kContentsRootNodeId) break; RenderSurfaceImpl* next_target = current_target->render_target(); next_target->damage_tracker()->AccumulateDamageFromRenderSurface( - current_target, surfaces_with_backdrop_blur_filter); + current_target); current_target = next_target; } - - DCHECK(surfaces_with_backdrop_blur_filter.empty()); } -void DamageTracker::ComputeSurfaceDamage( - RenderSurfaceImpl* render_surface, - std::vector<std::pair<RenderSurfaceImpl*, gfx::Rect>>& - surfaces_with_backdrop_blur_filter) { +void DamageTracker::ComputeSurfaceDamage(RenderSurfaceImpl* render_surface) { // All damage from contributing layers and surfaces must already have been // added to damage_for_this_update_ through calls to AccumulateDamageFromLayer // and AccumulateDamageFromRenderSurface. @@ -185,6 +173,35 @@ void DamageTracker::ComputeSurfaceDamage( has_damage_from_contributing_content_ |= !damage_from_leftover_rects.IsEmpty(); + gfx::Rect expanded_damage_rect; + bool valid = damage_from_leftover_rects.GetAsRect(&expanded_damage_rect); + bool expanded = false; + // Iterate through the surfaces rendering to the current target back to + // front, intersect their surface rects with the damage from leftover rects. + // Update surfaces' |intersects_damage_under| flags accordingly and expand the + // damage by surface rects for surfaces with pixel-moving backdrop filters + // when appropriate. + for (auto& contributing_surface : contributing_surfaces_) { + RenderSurfaceImpl* surface = contributing_surface.render_surface; + bool has_pixel_moving_backdrop_filters = + surface->BackdropFilters().HasFilterThatMovesPixels(); + if (!surface->intersects_damage_under() || + has_pixel_moving_backdrop_filters) { + if (!valid || contributing_surface.rect_in_target_space.Intersects( + expanded_damage_rect)) { + surface->set_intersects_damage_under(true); + if (has_pixel_moving_backdrop_filters) { + expanded_damage_rect.Union(contributing_surface.rect_in_target_space); + expanded = true; + } + } + } + } + if (expanded) + damage_for_this_update_.Union(expanded_damage_rect); + + contributing_surfaces_.clear(); + if (render_surface->SurfacePropertyChangedOnlyFromDescendant()) { damage_for_this_update_ = DamageAccumulator(); damage_for_this_update_.Union(render_surface->content_rect()); @@ -208,23 +225,6 @@ void DamageTracker::ComputeSurfaceDamage( // Damage accumulates until we are notified that we actually did draw on that // frame. current_damage_.Union(damage_for_this_update_); - - if (!surfaces_with_backdrop_blur_filter.empty()) { - gfx::Rect leftover_damage_rect; - bool valid = damage_from_leftover_rects.GetAsRect(&leftover_damage_rect); - std::vector<std::pair<RenderSurfaceImpl*, gfx::Rect>>::iterator it = - surfaces_with_backdrop_blur_filter.begin(); - while (it != surfaces_with_backdrop_blur_filter.end()) { - RenderSurfaceImpl* surface = it->first; - if (surface->render_target() == render_surface) { - surface->set_can_use_cached_backdrop_filtered_result( - !it->second.Intersects(leftover_damage_rect) && valid); - it = surfaces_with_backdrop_blur_filter.erase(it); - } else { - ++it; - } - } - } } bool DamageTracker::GetDamageRectIfValid(gfx::Rect* rect) { @@ -267,6 +267,7 @@ void DamageTracker::PrepareForUpdate() { mailboxId_++; damage_for_this_update_ = DamageAccumulator(); has_damage_from_contributing_content_ = false; + contributing_surfaces_.clear(); } DamageTracker::DamageAccumulator DamageTracker::TrackDamageFromLeftoverRects() { @@ -408,9 +409,7 @@ void DamageTracker::AccumulateDamageFromLayer(LayerImpl* layer) { } void DamageTracker::AccumulateDamageFromRenderSurface( - RenderSurfaceImpl* render_surface, - std::vector<std::pair<RenderSurfaceImpl*, gfx::Rect>>& - surfaces_with_backdrop_blur_filter) { + RenderSurfaceImpl* render_surface) { // There are two ways a "descendant surface" can damage regions of the "target // surface": // 1. Property change: @@ -434,6 +433,20 @@ void DamageTracker::AccumulateDamageFromRenderSurface( gfx::Rect surface_rect_in_target_space = gfx::ToEnclosingRect(render_surface->DrawableContentRect()); data.Update(surface_rect_in_target_space, mailboxId_); + contributing_surfaces_.emplace_back(render_surface, + surface_rect_in_target_space); + + // If the render surface has pixel-moving backdrop filters and the surface + // rect intersects current accumulated damage, expand the damage by surface + // rect. + gfx::Rect damage_on_target; + bool valid = damage_for_this_update_.GetAsRect(&damage_on_target); + bool intersects_damage_under = + !valid || damage_on_target.Intersects(surface_rect_in_target_space); + if (render_surface->BackdropFilters().HasFilterThatMovesPixels() && + intersects_damage_under) { + damage_for_this_update_.Union(surface_rect_in_target_space); + } if (surface_is_new || render_surface->SurfacePropertyChanged()) { // The entire surface contributes damage. @@ -441,17 +454,8 @@ void DamageTracker::AccumulateDamageFromRenderSurface( // The surface's old region is now exposed on the target surface, too. damage_for_this_update_.Union(old_surface_rect); - render_surface->set_can_use_cached_backdrop_filtered_result(false); + intersects_damage_under = true; } else { - // Check if current accumulated damage intersects the render surface. - gfx::Rect damage_on_target; - bool valid = damage_for_this_update_.GetAsRect(&damage_on_target); - if (valid && !damage_on_target.Intersects(surface_rect_in_target_space)) { - surfaces_with_backdrop_blur_filter.emplace_back( - std::make_pair(render_surface, surface_rect_in_target_space)); - } else { - render_surface->set_can_use_cached_backdrop_filtered_result(false); - } // Only the surface's damage_rect will damage the target surface. gfx::Rect damage_rect_in_local_space; bool is_valid_rect = render_surface->damage_tracker()->GetDamageRectIfValid( @@ -470,6 +474,7 @@ void DamageTracker::AccumulateDamageFromRenderSurface( } } + render_surface->set_intersects_damage_under(intersects_damage_under); // True if any changes from contributing render surface. has_damage_from_contributing_content_ |= !damage_for_this_update_.IsEmpty(); } diff --git a/chromium/cc/trees/damage_tracker.h b/chromium/cc/trees/damage_tracker.h index d5b83bededc..70a8356ce58 100644 --- a/chromium/cc/trees/damage_tracker.h +++ b/chromium/cc/trees/damage_tracker.h @@ -5,7 +5,9 @@ #ifndef CC_TREES_DAMAGE_TRACKER_H_ #define CC_TREES_DAMAGE_TRACKER_H_ +#include <algorithm> #include <memory> +#include <utility> #include <vector> #include "cc/cc_export.h" @@ -94,14 +96,8 @@ class CC_EXPORT DamageTracker { // These helper functions are used only during UpdateDamageTracking(). void PrepareForUpdate(); void AccumulateDamageFromLayer(LayerImpl* layer); - void AccumulateDamageFromRenderSurface( - RenderSurfaceImpl* render_surface, - std::vector<std::pair<RenderSurfaceImpl*, gfx::Rect>>& - surfaces_with_backdrop_blur_filter); - void ComputeSurfaceDamage( - RenderSurfaceImpl* render_surface, - std::vector<std::pair<RenderSurfaceImpl*, gfx::Rect>>& - surfaces_with_backdrop_blur_filter); + void AccumulateDamageFromRenderSurface(RenderSurfaceImpl* render_surface); + void ComputeSurfaceDamage(RenderSurfaceImpl* render_surface); void ExpandDamageInsideRectWithFilters(const gfx::Rect& pre_filter_rect, const FilterOperations& filters); @@ -157,6 +153,15 @@ class CC_EXPORT DamageTracker { // Damage accumulated since the last call to PrepareForUpdate(). DamageAccumulator damage_for_this_update_; + + struct SurfaceWithRect { + SurfaceWithRect(RenderSurfaceImpl* rs, const gfx::Rect& rect) + : render_surface(rs), rect_in_target_space(rect) {} + RenderSurfaceImpl* render_surface; + const gfx::Rect rect_in_target_space; + }; + + std::vector<SurfaceWithRect> contributing_surfaces_; }; } // namespace cc diff --git a/chromium/cc/trees/damage_tracker_unittest.cc b/chromium/cc/trees/damage_tracker_unittest.cc index 4b9213336ec..00fa95b025d 100644 --- a/chromium/cc/trees/damage_tracker_unittest.cc +++ b/chromium/cc/trees/damage_tracker_unittest.cc @@ -904,9 +904,8 @@ TEST_F(DamageTrackerTest, VerifyDamageForImageFilter) { child->SetDrawsContent(true); FilterOperations filters; - filters.Append( - FilterOperation::CreateReferenceFilter(sk_make_sp<BlurPaintFilter>( - 2, 2, BlurPaintFilter::TileMode::kClampToBlack_TileMode, nullptr))); + filters.Append(FilterOperation::CreateReferenceFilter( + sk_make_sp<BlurPaintFilter>(2, 2, SkTileMode::kDecal, nullptr))); // Setting the filter will damage the whole surface. CreateTransformNode(child).post_translation = @@ -988,9 +987,8 @@ TEST_F(DamageTrackerTest, VerifyDamageForTransformedImageFilter) { child->SetDrawsContent(true); FilterOperations filters; - filters.Append( - FilterOperation::CreateReferenceFilter(sk_make_sp<BlurPaintFilter>( - 2, 2, BlurPaintFilter::TileMode::kClampToBlack_TileMode, nullptr))); + filters.Append(FilterOperation::CreateReferenceFilter( + sk_make_sp<BlurPaintFilter>(2, 2, SkTileMode::kDecal, nullptr))); // Setting the filter will damage the whole surface. gfx::Transform transform; @@ -2054,27 +2052,23 @@ TEST_F(DamageTrackerTest, CanUseCachedBackdropFilterResultTest) { filters.Append(FilterOperation::CreateBlurFilter(2.f)); SetBackdropFilter(child1_, filters); child1_->NoteLayerPropertyChanged(); - // can_use_cached_backdrop_filtered_result_ is false by default. - EXPECT_FALSE( - GetRenderSurface(child1_)->can_use_cached_backdrop_filtered_result()); + // intersects_damage_under_ is false by default. + EXPECT_TRUE(GetRenderSurface(child1_)->intersects_damage_under()); EmulateDrawingOneFrame(root); // child1_'s render target has changed its surface property. - EXPECT_FALSE( - GetRenderSurface(child1_)->can_use_cached_backdrop_filtered_result()); + EXPECT_TRUE(GetRenderSurface(child1_)->intersects_damage_under()); // Let run for one update and there should be no damage left. EmulateDrawingOneFrame(root); - EXPECT_TRUE( - GetRenderSurface(child1_)->can_use_cached_backdrop_filtered_result()); + EXPECT_FALSE(GetRenderSurface(child1_)->intersects_damage_under()); // CASE 1.1: Setting a non-intersecting update rect on the root // doesn't invalidate child1_'s cached backdrop-filtered result. // Damage rect at 0,0 20x20 doesn't intersect 270,270 36x38. root->UnionUpdateRect(gfx::Rect(0, 0, 20, 20)); EmulateDrawingOneFrame(root); - EXPECT_TRUE( - GetRenderSurface(child1_)->can_use_cached_backdrop_filtered_result()); + EXPECT_FALSE(GetRenderSurface(child1_)->intersects_damage_under()); // CASE 1.2: Setting an intersecting update rect on the root invalidates // child1_'s cached backdrop-filtered result. @@ -2082,8 +2076,7 @@ TEST_F(DamageTrackerTest, CanUseCachedBackdropFilterResultTest) { ClearDamageForAllSurfaces(root); root->UnionUpdateRect(gfx::Rect(260, 260, 20, 20)); EmulateDrawingOneFrame(root); - EXPECT_FALSE( - GetRenderSurface(child1_)->can_use_cached_backdrop_filtered_result()); + EXPECT_TRUE(GetRenderSurface(child1_)->intersects_damage_under()); // CASE 1.3: Damage on layers above the surface with the backdrop filter // doesn't invalidate cached backdrop-filtered result. Move child2_ to overlap @@ -2091,8 +2084,7 @@ TEST_F(DamageTrackerTest, CanUseCachedBackdropFilterResultTest) { ClearDamageForAllSurfaces(root); child2_->SetOffsetToTransformParent(gfx::Vector2dF(180.f, 180.f)); EmulateDrawingOneFrame(root); - EXPECT_TRUE(GetRenderSurface(grand_child1_) - ->can_use_cached_backdrop_filtered_result()); + EXPECT_FALSE(GetRenderSurface(grand_child1_)->intersects_damage_under()); // CASE 2: Adding or removing a backdrop filter would invalidate cached // backdrop-filtered result of the corresponding render surfaces. @@ -2104,15 +2096,12 @@ TEST_F(DamageTrackerTest, CanUseCachedBackdropFilterResultTest) { SetBackdropFilter(grand_child4_, filters); grand_child4_->NoteLayerPropertyChanged(); EmulateDrawingOneFrame(root); - EXPECT_FALSE(GetRenderSurface(grand_child4_) - ->can_use_cached_backdrop_filtered_result()); - EXPECT_FALSE(GetRenderSurface(grand_child1_) - ->can_use_cached_backdrop_filtered_result()); + EXPECT_TRUE(GetRenderSurface(grand_child4_)->intersects_damage_under()); + EXPECT_TRUE(GetRenderSurface(grand_child1_)->intersects_damage_under()); // Let run for one update and there should be no damage left. EmulateDrawingOneFrame(root); - EXPECT_TRUE(GetRenderSurface(grand_child4_) - ->can_use_cached_backdrop_filtered_result()); + EXPECT_FALSE(GetRenderSurface(grand_child4_)->intersects_damage_under()); // CASE 3.1: Adding a non-intersecting damage rect to a sibling layer under // the render surface with the backdrop filter doesn't invalidate cached @@ -2121,8 +2110,7 @@ TEST_F(DamageTrackerTest, CanUseCachedBackdropFilterResultTest) { ClearDamageForAllSurfaces(root); grand_child1_->AddDamageRect(gfx::Rect(2, 2, 1.f, 1.f)); EmulateDrawingOneFrame(root); - EXPECT_TRUE(GetRenderSurface(grand_child4_) - ->can_use_cached_backdrop_filtered_result()); + EXPECT_FALSE(GetRenderSurface(grand_child4_)->intersects_damage_under()); // CASE 3.2: Adding an intersecting damage rect to a sibling layer under the // render surface with the backdrop filter invalidates cached @@ -2131,8 +2119,7 @@ TEST_F(DamageTrackerTest, CanUseCachedBackdropFilterResultTest) { ClearDamageForAllSurfaces(root); grand_child2_->AddDamageRect(gfx::Rect(0, 0, 1.f, 1.f)); EmulateDrawingOneFrame(root); - EXPECT_FALSE(GetRenderSurface(grand_child4_) - ->can_use_cached_backdrop_filtered_result()); + EXPECT_TRUE(GetRenderSurface(grand_child4_)->intersects_damage_under()); // CASE 4.1: Non-intersecting damage rect on a sibling surface under the // render surface with the backdrop filter doesn't invalidate cached @@ -2147,8 +2134,7 @@ TEST_F(DamageTrackerTest, CanUseCachedBackdropFilterResultTest) { EXPECT_EQ(gfx::Rect(170, 170, 1.f, 1.f), damage_rect); // Damage rect at 170,170 1x1 in render target local space doesn't intersect // 180,180 15x16. - EXPECT_TRUE(GetRenderSurface(grand_child4_) - ->can_use_cached_backdrop_filtered_result()); + EXPECT_FALSE(GetRenderSurface(grand_child4_)->intersects_damage_under()); // CASE 4.2: Intersecting damage rect on a sibling surface under the render // surface with the backdrop filter invalidates cached backdrop-filtered @@ -2162,8 +2148,7 @@ TEST_F(DamageTrackerTest, CanUseCachedBackdropFilterResultTest) { EXPECT_EQ(gfx::Rect(170, 170, 11.f, 11.f), damage_rect); // Damage rect at 170,170 11x11 in render target local space intersects // 180,180 15x16 - EXPECT_FALSE(GetRenderSurface(grand_child4_) - ->can_use_cached_backdrop_filtered_result()); + EXPECT_TRUE(GetRenderSurface(grand_child4_)->intersects_damage_under()); // CASE 5.1: Removing a non-intersecting sibling layer under the render // surface with the backdrop filter doesn't invalidate cached @@ -2185,8 +2170,7 @@ TEST_F(DamageTrackerTest, CanUseCachedBackdropFilterResultTest) { host_impl()->active_tree()->AddLayer(std::move(layers[5])); host_impl()->active_tree()->AddLayer(std::move(layers[6])); EmulateDrawingOneFrame(root); - EXPECT_TRUE(GetRenderSurface(grand_child4_) - ->can_use_cached_backdrop_filtered_result()); + EXPECT_FALSE(GetRenderSurface(grand_child4_)->intersects_damage_under()); // CASE 5.2: Removing an intersecting sibling layer under the render surface // with the backdrop filter invalidates cached backdrop-filtered result. @@ -2205,14 +2189,12 @@ TEST_F(DamageTrackerTest, CanUseCachedBackdropFilterResultTest) { host_impl()->active_tree()->AddLayer(std::move(layers[5])); EmulateDrawingOneFrame(root); - EXPECT_FALSE(GetRenderSurface(grand_child4_) - ->can_use_cached_backdrop_filtered_result()); + EXPECT_TRUE(GetRenderSurface(grand_child4_)->intersects_damage_under()); // Let run for one update and there should be no damage left. ClearDamageForAllSurfaces(root); EmulateDrawingOneFrame(root); - EXPECT_TRUE(GetRenderSurface(grand_child4_) - ->can_use_cached_backdrop_filtered_result()); + EXPECT_FALSE(GetRenderSurface(grand_child4_)->intersects_damage_under()); // CASE 6: Removing a intersecting sibling surface under the render // surface with the backdrop filter invalidate cached backdrop-filtered @@ -2221,8 +2203,7 @@ TEST_F(DamageTrackerTest, CanUseCachedBackdropFilterResultTest) { SetRenderSurfaceReason(grand_child3_, RenderSurfaceReason::kNone); grand_child3_->SetDrawsContent(false); EmulateDrawingOneFrame(root); - EXPECT_FALSE(GetRenderSurface(grand_child4_) - ->can_use_cached_backdrop_filtered_result()); + EXPECT_TRUE(GetRenderSurface(grand_child4_)->intersects_damage_under()); } TEST_F(DamageTrackerTest, DamageRectOnlyVisibleContentsMoveToOutside) { @@ -2344,5 +2325,45 @@ TEST_F(DamageTrackerTest, ->has_damage_from_contributing_content()); } +TEST_F(DamageTrackerTest, VerifyDamageExpansionWithBackdropBlurFilters) { + LayerImpl* root = CreateAndSetUpTestTreeWithTwoSurfaces(); + + // Allow us to set damage on child1_. + child1_->SetDrawsContent(true); + + FilterOperations filters; + filters.Append(FilterOperation::CreateBlurFilter(2.f)); + + // Setting the filter will damage the whole surface. + ClearDamageForAllSurfaces(root); + SetBackdropFilter(child1_, filters); + child1_->NoteLayerPropertyChanged(); + EmulateDrawingOneFrame(root); + + ClearDamageForAllSurfaces(root); + root->UnionUpdateRect(gfx::Rect(297, 297, 2, 2)); + EmulateDrawingOneFrame(root); + + // child1_'s render surface has a size of 206x208 due to the contributions + // from grand_child1_ and grand_child2_. The blur filter on child1_ intersects + // the damage from root and expands it to (100,100 206x208). + gfx::Rect expected_damage_rect = gfx::Rect(100, 100, 206, 208); + gfx::Rect root_damage_rect; + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( + &root_damage_rect)); + EXPECT_EQ(expected_damage_rect, root_damage_rect); + + ClearDamageForAllSurfaces(root); + gfx::Rect damage_rect(97, 97, 2, 2); + root->UnionUpdateRect(damage_rect); + EmulateDrawingOneFrame(root); + + // The blur filter on child1_ doesn't intersect the damage from root so the + // damage remains unchanged. + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( + &root_damage_rect)); + EXPECT_EQ(damage_rect, root_damage_rect); +} + } // namespace } // namespace cc diff --git a/chromium/cc/trees/draw_properties_unittest.cc b/chromium/cc/trees/draw_properties_unittest.cc index d461d6ae08d..83e66c1a946 100644 --- a/chromium/cc/trees/draw_properties_unittest.cc +++ b/chromium/cc/trees/draw_properties_unittest.cc @@ -10,14 +10,11 @@ #include <tuple> #include <vector> +#include "base/containers/contains.h" #include "base/memory/ptr_util.h" -#include "base/stl_util.h" #include "cc/animation/animation.h" #include "cc/animation/animation_host.h" #include "cc/animation/animation_id_provider.h" -#include "cc/animation/keyframed_animation_curve.h" -#include "cc/animation/transform_operations.h" -#include "cc/base/math_util.h" #include "cc/layers/content_layer_client.h" #include "cc/layers/effect_tree_layer_list_iterator.h" #include "cc/layers/layer.h" @@ -38,11 +35,14 @@ #include "components/viz/common/frame_sinks/copy_output_request.h" #include "components/viz/common/frame_sinks/copy_output_result.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/gfx/animation/keyframe/keyframed_animation_curve.h" #include "ui/gfx/geometry/quad_f.h" #include "ui/gfx/geometry/rect_conversions.h" #include "ui/gfx/geometry/size_conversions.h" #include "ui/gfx/geometry/vector2d_conversions.h" #include "ui/gfx/transform.h" +#include "ui/gfx/transform_operations.h" +#include "ui/gfx/transform_util.h" namespace cc { namespace { @@ -67,20 +67,15 @@ class DrawPropertiesTestBase : public LayerTreeImplTestBase { layer_impl->element_id()); } - static float GetMaximumAnimationScale(LayerImpl* layer_impl) { + static float MaximumAnimationToScreenScale(LayerImpl* layer_impl) { return layer_impl->layer_tree_impl() ->property_trees() - ->GetAnimationScales(layer_impl->transform_tree_index(), - layer_impl->layer_tree_impl()) - .maximum_animation_scale; + ->MaximumAnimationToScreenScale(layer_impl->transform_tree_index()); } - - static float GetStartingAnimationScale(LayerImpl* layer_impl) { + static bool AnimationAffectedByInvalidScale(LayerImpl* layer_impl) { return layer_impl->layer_tree_impl() ->property_trees() - ->GetAnimationScales(layer_impl->transform_tree_index(), - layer_impl->layer_tree_impl()) - .starting_animation_scale; + ->AnimationAffectedByInvalidScale(layer_impl->transform_tree_index()); } void UpdateMainDrawProperties(float device_scale_factor = 1.0f) { @@ -291,8 +286,8 @@ TEST_F(DrawPropertiesTest, TransformsForSingleLayer) { // Case 5: The layer transform should occur with respect to the anchor point. gfx::Transform translation_to_anchor; translation_to_anchor.Translate(5.0, 0.0); - gfx::Transform expected_result = - translation_to_anchor * layer_transform * Inverse(translation_to_anchor); + gfx::Transform expected_result = translation_to_anchor * layer_transform * + gfx::InvertAndCheck(translation_to_anchor); SetTransformOrigin(layer, gfx::Point3F(5.f, 0.f, 0.f)); UpdateActiveTreeDrawProperties(); EXPECT_TRANSFORMATION_MATRIX_EQ( @@ -306,7 +301,8 @@ TEST_F(DrawPropertiesTest, TransformsForSingleLayer) { // current implementation of CalculateDrawProperties does this implicitly, but // it is still worth testing to detect accidental regressions. expected_result = position_transform * translation_to_anchor * - layer_transform * Inverse(translation_to_anchor); + layer_transform * + gfx::InvertAndCheck(translation_to_anchor); SetPostTranslation(layer, gfx::Vector2dF(0.f, 1.2f)); UpdateActiveTreeDrawProperties(); EXPECT_TRANSFORMATION_MATRIX_EQ( @@ -465,7 +461,7 @@ TEST_F(DrawPropertiesTest, TransformsForSimpleHierarchy) { parent_translation_to_anchor.Translate(2.5, 3.0); gfx::Transform parent_composite_transform = parent_translation_to_anchor * parent_layer_transform * - Inverse(parent_translation_to_anchor); + gfx::InvertAndCheck(parent_translation_to_anchor); SetTransform(parent, parent_layer_transform); SetPostTranslation(parent, gfx::Vector2dF()); UpdateActiveTreeDrawProperties(); @@ -497,15 +493,15 @@ TEST_F(DrawPropertiesTest, TransformsForSingleRenderSurface) { gfx::Transform parent_composite_transform = parent_translation_to_anchor * parent_layer_transform * - Inverse(parent_translation_to_anchor); + gfx::InvertAndCheck(parent_translation_to_anchor); gfx::Vector2dF parent_composite_scale = - MathUtil::ComputeTransform2dScaleComponents(parent_composite_transform, - 1.f); + gfx::ComputeTransform2dScaleComponents(parent_composite_transform, 1.f); gfx::Transform surface_sublayer_transform; surface_sublayer_transform.Scale(parent_composite_scale.x(), parent_composite_scale.y()); gfx::Transform surface_sublayer_composite_transform = - parent_composite_transform * Inverse(surface_sublayer_transform); + parent_composite_transform * + gfx::InvertAndCheck(surface_sublayer_transform); root->SetBounds(gfx::Size(1, 2)); parent->SetBounds(gfx::Size(100, 120)); @@ -586,11 +582,11 @@ TEST_F(DrawPropertiesTest, TransformsForRenderSurfaceHierarchy) { gfx::Transform layer_transform; layer_transform.Translate(1.0, 1.0); - gfx::Transform A = - translation_to_anchor * layer_transform * Inverse(translation_to_anchor); + gfx::Transform A = translation_to_anchor * layer_transform * + gfx::InvertAndCheck(translation_to_anchor); gfx::Vector2dF surface1_parent_transform_scale = - MathUtil::ComputeTransform2dScaleComponents(A, 1.f); + gfx::ComputeTransform2dScaleComponents(A, 1.f); gfx::Transform surface1_sublayer_transform; surface1_sublayer_transform.Scale(surface1_parent_transform_scale.x(), surface1_parent_transform_scale.y()); @@ -599,10 +595,10 @@ TEST_F(DrawPropertiesTest, TransformsForRenderSurfaceHierarchy) { gfx::Transform SS1 = surface1_sublayer_transform; // S1 = transform to move from render_surface1 pixels to the layer space of // the owning layer - gfx::Transform S1 = Inverse(surface1_sublayer_transform); + gfx::Transform S1 = gfx::InvertAndCheck(surface1_sublayer_transform); gfx::Vector2dF surface2_parent_transform_scale = - MathUtil::ComputeTransform2dScaleComponents(SS1 * A, 1.f); + gfx::ComputeTransform2dScaleComponents(SS1 * A, 1.f); gfx::Transform surface2_sublayer_transform; surface2_sublayer_transform.Scale(surface2_parent_transform_scale.x(), surface2_parent_transform_scale.y()); @@ -611,7 +607,7 @@ TEST_F(DrawPropertiesTest, TransformsForRenderSurfaceHierarchy) { gfx::Transform SS2 = surface2_sublayer_transform; // S2 = transform to move from render_surface2 pixels to the layer space of // the owning layer - gfx::Transform S2 = Inverse(surface2_sublayer_transform); + gfx::Transform S2 = gfx::InvertAndCheck(surface2_sublayer_transform); root->SetBounds(gfx::Size(1, 2)); parent->SetBounds(gfx::Size(10, 10)); @@ -1746,8 +1742,8 @@ TEST_F(DrawPropertiesTest, // Add a transform animation with a start delay to |grand_child|. std::unique_ptr<KeyframeModel> keyframe_model = KeyframeModel::Create( - std::unique_ptr<AnimationCurve>(new FakeTransformTransition(1.0)), 0, 1, - TargetProperty::TRANSFORM); + std::unique_ptr<gfx::AnimationCurve>(new FakeTransformTransition(1.0)), 0, + 1, KeyframeModel::TargetPropertyId(TargetProperty::TRANSFORM)); keyframe_model->set_fill_mode(KeyframeModel::FillMode::NONE); keyframe_model->set_time_offset(base::TimeDelta::FromMilliseconds(-1000)); AddKeyframeModelToElementWithAnimation( @@ -2026,7 +2022,7 @@ TEST_F(DrawPropertiesDrawRectsTest, DrawRectsFor3dPerspectiveWhenClippedByW) { static bool ProjectionClips(const gfx::Transform& map_transform, const gfx::RectF& mapped_rect) { - gfx::Transform inverse(Inverse(map_transform)); + gfx::Transform inverse(gfx::InvertAndCheck(map_transform)); bool clipped = false; if (!clipped) MathUtil::ProjectPoint(inverse, mapped_rect.top_right(), &clipped); @@ -3237,14 +3233,14 @@ TEST_F(DrawPropertiesScalingTest, SurfaceLayerTransformsInHighDPI) { transform.Scale(device_scale_factor * page_scale_factor, device_scale_factor * page_scale_factor); gfx::Vector2dF scales = - MathUtil::ComputeTransform2dScaleComponents(transform, 0.f); + gfx::ComputeTransform2dScaleComponents(transform, 0.f); float max_2d_scale = std::max(scales.x(), scales.y()); EXPECT_FLOAT_EQ(max_2d_scale, scale_surface->GetIdealContentsScale()); // The ideal scale will draw 1:1 with its render target space along // the larger-scale axis. gfx::Vector2dF target_space_transform_scales = - MathUtil::ComputeTransform2dScaleComponents( + gfx::ComputeTransform2dScaleComponents( scale_surface->draw_properties().target_space_transform, 0.f); EXPECT_FLOAT_EQ(max_2d_scale, std::max(target_space_transform_scales.x(), target_space_transform_scales.y())); @@ -4638,9 +4634,9 @@ TEST_F(DrawPropertiesTest, ScrollSnappingWithAnimatedScreenSpaceTransform) { gfx::Transform end_scale; end_scale.Scale(2.f, 2.f); - TransformOperations start_operations; + gfx::TransformOperations start_operations; start_operations.AppendMatrix(start_scale); - TransformOperations end_operations; + gfx::TransformOperations end_operations; end_operations.AppendMatrix(end_scale); AddAnimatedTransformToElementWithAnimation(animated_layer->element_id(), @@ -5482,17 +5478,17 @@ TEST_F(DrawPropertiesTest, MaximumAnimationScaleFactor) { UpdateActiveTreeDrawProperties(); // No layers have animations. - EXPECT_EQ(kNotScaled, GetMaximumAnimationScale(grand_parent)); - EXPECT_EQ(kNotScaled, GetMaximumAnimationScale(parent)); - EXPECT_EQ(kNotScaled, GetMaximumAnimationScale(child)); - EXPECT_EQ(kNotScaled, GetMaximumAnimationScale(grand_child)); + EXPECT_EQ(1.f, MaximumAnimationToScreenScale(grand_child)); + EXPECT_EQ(1.f, MaximumAnimationToScreenScale(child)); + EXPECT_EQ(1.f, MaximumAnimationToScreenScale(parent)); + EXPECT_EQ(1.f, MaximumAnimationToScreenScale(grand_parent)); - EXPECT_EQ(kNotScaled, GetStartingAnimationScale(grand_parent)); - EXPECT_EQ(kNotScaled, GetStartingAnimationScale(parent)); - EXPECT_EQ(kNotScaled, GetStartingAnimationScale(child)); - EXPECT_EQ(kNotScaled, GetStartingAnimationScale(grand_child)); + EXPECT_FALSE(AnimationAffectedByInvalidScale(grand_child)); + EXPECT_FALSE(AnimationAffectedByInvalidScale(child)); + EXPECT_FALSE(AnimationAffectedByInvalidScale(parent)); + EXPECT_FALSE(AnimationAffectedByInvalidScale(grand_parent)); - TransformOperations translation; + gfx::TransformOperations translation; translation.AppendTranslate(1.f, 2.f, 3.f); scoped_refptr<Animation> grand_parent_animation = @@ -5516,68 +5512,70 @@ TEST_F(DrawPropertiesTest, MaximumAnimationScaleFactor) { grand_child_animation->AttachElement(grand_child->element_id()); AddAnimatedTransformToAnimation(parent_animation.get(), 1.0, - TransformOperations(), translation); + gfx::TransformOperations(), translation); // No layers have scale-affecting animations. - EXPECT_EQ(kNotScaled, GetMaximumAnimationScale(grand_parent)); - EXPECT_EQ(kNotScaled, GetMaximumAnimationScale(parent)); - EXPECT_EQ(kNotScaled, GetMaximumAnimationScale(child)); - EXPECT_EQ(kNotScaled, GetMaximumAnimationScale(grand_child)); + EXPECT_EQ(1.f, MaximumAnimationToScreenScale(grand_child)); + EXPECT_EQ(1.f, MaximumAnimationToScreenScale(child)); + EXPECT_EQ(1.f, MaximumAnimationToScreenScale(parent)); + EXPECT_EQ(1.f, MaximumAnimationToScreenScale(grand_parent)); - EXPECT_EQ(kNotScaled, GetStartingAnimationScale(grand_parent)); - EXPECT_EQ(kNotScaled, GetStartingAnimationScale(parent)); - EXPECT_EQ(kNotScaled, GetStartingAnimationScale(child)); - EXPECT_EQ(kNotScaled, GetStartingAnimationScale(grand_child)); + EXPECT_FALSE(AnimationAffectedByInvalidScale(grand_child)); + EXPECT_FALSE(AnimationAffectedByInvalidScale(child)); + EXPECT_FALSE(AnimationAffectedByInvalidScale(parent)); + EXPECT_FALSE(AnimationAffectedByInvalidScale(grand_parent)); - TransformOperations scale; + gfx::TransformOperations scale; scale.AppendScale(5.f, 4.f, 3.f); AddAnimatedTransformToAnimation(child_animation.get(), 1.0, - TransformOperations(), scale); + gfx::TransformOperations(), scale); UpdateActiveTreeDrawProperties(); // Only |child| has a scale-affecting animation. - EXPECT_EQ(kNotScaled, GetMaximumAnimationScale(grand_parent)); - EXPECT_EQ(kNotScaled, GetMaximumAnimationScale(parent)); - EXPECT_EQ(5.f, GetMaximumAnimationScale(child)); - EXPECT_EQ(5.f, GetMaximumAnimationScale(grand_child)); + EXPECT_EQ(5.f, MaximumAnimationToScreenScale(grand_child)); + EXPECT_EQ(5.f, MaximumAnimationToScreenScale(child)); + EXPECT_EQ(1.f, MaximumAnimationToScreenScale(parent)); + EXPECT_EQ(1.f, MaximumAnimationToScreenScale(grand_parent)); - EXPECT_EQ(kNotScaled, GetStartingAnimationScale(grand_parent)); - EXPECT_EQ(kNotScaled, GetStartingAnimationScale(parent)); - EXPECT_EQ(1.f, GetStartingAnimationScale(child)); - EXPECT_EQ(1.f, GetStartingAnimationScale(grand_child)); + EXPECT_FALSE(AnimationAffectedByInvalidScale(grand_child)); + EXPECT_FALSE(AnimationAffectedByInvalidScale(child)); + EXPECT_FALSE(AnimationAffectedByInvalidScale(parent)); + EXPECT_FALSE(AnimationAffectedByInvalidScale(grand_parent)); AddAnimatedTransformToAnimation(grand_parent_animation.get(), 1.0, - TransformOperations(), scale); + gfx::TransformOperations(), scale); UpdateActiveTreeDrawProperties(); // |grand_parent| and |child| have scale-affecting animations. - EXPECT_EQ(5.f, GetMaximumAnimationScale(grand_parent)); - EXPECT_EQ(5.f, GetMaximumAnimationScale(parent)); - // We don't support combining animated scales from two nodes; 0.f means - // that the maximum scale could not be computed. - EXPECT_EQ(kNotScaled, GetMaximumAnimationScale(child)); - EXPECT_EQ(kNotScaled, GetMaximumAnimationScale(grand_child)); - - EXPECT_EQ(1.f, GetStartingAnimationScale(grand_parent)); - EXPECT_EQ(1.f, GetStartingAnimationScale(parent)); - EXPECT_EQ(kNotScaled, GetStartingAnimationScale(child)); - EXPECT_EQ(kNotScaled, GetStartingAnimationScale(grand_child)); + // With nested animated scales, the child will use the parent's maximum + // animation scale, without combining the multiple animated scales. + EXPECT_EQ(5.f, MaximumAnimationToScreenScale(grand_child)); + EXPECT_EQ(5.f, MaximumAnimationToScreenScale(child)); + EXPECT_EQ(5.f, MaximumAnimationToScreenScale(parent)); + EXPECT_EQ(5.f, MaximumAnimationToScreenScale(grand_parent)); + + EXPECT_FALSE(AnimationAffectedByInvalidScale(grand_child)); + EXPECT_FALSE(AnimationAffectedByInvalidScale(child)); + EXPECT_FALSE(AnimationAffectedByInvalidScale(parent)); + EXPECT_FALSE(AnimationAffectedByInvalidScale(grand_parent)); AddAnimatedTransformToAnimation(parent_animation.get(), 1.0, - TransformOperations(), scale); + gfx::TransformOperations(), scale); UpdateActiveTreeDrawProperties(); // |grand_parent|, |parent|, and |child| have scale-affecting animations. - EXPECT_EQ(5.f, GetMaximumAnimationScale(grand_parent)); - EXPECT_EQ(kNotScaled, GetMaximumAnimationScale(parent)); - EXPECT_EQ(kNotScaled, GetMaximumAnimationScale(child)); - EXPECT_EQ(kNotScaled, GetMaximumAnimationScale(grand_child)); - - EXPECT_EQ(1.f, GetStartingAnimationScale(grand_parent)); - EXPECT_EQ(kNotScaled, GetStartingAnimationScale(parent)); - EXPECT_EQ(kNotScaled, GetStartingAnimationScale(child)); - EXPECT_EQ(kNotScaled, GetStartingAnimationScale(grand_child)); + // For nested scale animations, the child uses the parent's maximum scale + // instead of combining them. + EXPECT_EQ(5.f, MaximumAnimationToScreenScale(grand_child)); + EXPECT_EQ(5.f, MaximumAnimationToScreenScale(child)); + EXPECT_EQ(5.f, MaximumAnimationToScreenScale(parent)); + EXPECT_EQ(5.f, MaximumAnimationToScreenScale(grand_parent)); + + EXPECT_FALSE(AnimationAffectedByInvalidScale(grand_child)); + EXPECT_FALSE(AnimationAffectedByInvalidScale(child)); + EXPECT_FALSE(AnimationAffectedByInvalidScale(parent)); + EXPECT_FALSE(AnimationAffectedByInvalidScale(grand_parent)); grand_parent_animation->AbortKeyframeModelsWithProperty( TargetProperty::TRANSFORM, false); @@ -5586,24 +5584,30 @@ TEST_F(DrawPropertiesTest, MaximumAnimationScaleFactor) { child_animation->AbortKeyframeModelsWithProperty(TargetProperty::TRANSFORM, false); - TransformOperations perspective; + // Recreate child_animation containing keyframes with perspective. + timeline_impl()->DetachAnimation(child_animation); + child_animation = Animation::Create(AnimationIdProvider::NextAnimationId()); + timeline_impl()->AttachAnimation(child_animation); + child_animation->AttachElement(child->element_id()); + + gfx::TransformOperations perspective; perspective.AppendPerspective(10.f); - AddAnimatedTransformToAnimation(child_animation.get(), 1.0, - TransformOperations(), perspective); + AddAnimatedTransformToAnimation(child_animation.get(), 1.0, perspective, + perspective); UpdateActiveTreeDrawProperties(); // |child| has a scale-affecting animation but computing the maximum of this // animation is not supported. - EXPECT_EQ(kNotScaled, GetMaximumAnimationScale(grand_parent)); - EXPECT_EQ(kNotScaled, GetMaximumAnimationScale(parent)); - EXPECT_EQ(kNotScaled, GetMaximumAnimationScale(child)); - EXPECT_EQ(kNotScaled, GetMaximumAnimationScale(grand_child)); + EXPECT_EQ(1.f, MaximumAnimationToScreenScale(grand_child)); + EXPECT_EQ(1.f, MaximumAnimationToScreenScale(child)); + EXPECT_EQ(1.f, MaximumAnimationToScreenScale(parent)); + EXPECT_EQ(1.f, MaximumAnimationToScreenScale(grand_parent)); - EXPECT_EQ(kNotScaled, GetStartingAnimationScale(grand_parent)); - EXPECT_EQ(kNotScaled, GetStartingAnimationScale(parent)); - EXPECT_EQ(kNotScaled, GetStartingAnimationScale(child)); - EXPECT_EQ(kNotScaled, GetStartingAnimationScale(grand_child)); + EXPECT_TRUE(AnimationAffectedByInvalidScale(grand_child)); + EXPECT_TRUE(AnimationAffectedByInvalidScale(child)); + EXPECT_FALSE(AnimationAffectedByInvalidScale(parent)); + EXPECT_FALSE(AnimationAffectedByInvalidScale(grand_parent)); child_animation->AbortKeyframeModelsWithProperty(TargetProperty::TRANSFORM, false); @@ -5612,70 +5616,88 @@ TEST_F(DrawPropertiesTest, MaximumAnimationScaleFactor) { scale_matrix.Scale(1.f, 2.f); SetTransform(grand_parent, scale_matrix); SetTransform(parent, scale_matrix); + SetTransform(child, scale_matrix); AddAnimatedTransformToAnimation(parent_animation.get(), 1.0, - TransformOperations(), scale); + gfx::TransformOperations(), scale); UpdateActiveTreeDrawProperties(); - // |grand_parent| and |parent| each have scale 2.f. |parent| has a scale - // animation with maximum scale 5.f. - EXPECT_EQ(kNotScaled, GetMaximumAnimationScale(grand_parent)); - EXPECT_EQ(10.f, GetMaximumAnimationScale(parent)); - EXPECT_EQ(10.f, GetMaximumAnimationScale(child)); - EXPECT_EQ(10.f, GetMaximumAnimationScale(grand_child)); + // |grand_parent|, |parent| and |child| each has scale 2.f. |parent| has a + // scale animation with maximum scale 5.f. + EXPECT_EQ(20.f, MaximumAnimationToScreenScale(grand_child)); + EXPECT_EQ(20.f, MaximumAnimationToScreenScale(child)); + EXPECT_EQ(10.f, MaximumAnimationToScreenScale(parent)); + EXPECT_EQ(2.f, MaximumAnimationToScreenScale(grand_parent)); - EXPECT_EQ(kNotScaled, GetStartingAnimationScale(grand_parent)); - EXPECT_EQ(2.f, GetStartingAnimationScale(parent)); - EXPECT_EQ(2.f, GetStartingAnimationScale(child)); - EXPECT_EQ(2.f, GetStartingAnimationScale(grand_child)); + EXPECT_FALSE(AnimationAffectedByInvalidScale(grand_child)); + EXPECT_FALSE(AnimationAffectedByInvalidScale(child)); + EXPECT_FALSE(AnimationAffectedByInvalidScale(parent)); + EXPECT_FALSE(AnimationAffectedByInvalidScale(grand_parent)); gfx::Transform perspective_matrix; perspective_matrix.ApplyPerspectiveDepth(2.f); SetTransform(child, perspective_matrix); UpdateActiveTreeDrawProperties(); - // |child| has a transform that's neither a translation nor a scale. - EXPECT_EQ(kNotScaled, GetMaximumAnimationScale(grand_parent)); - EXPECT_EQ(10.f, GetMaximumAnimationScale(parent)); - EXPECT_EQ(kNotScaled, GetMaximumAnimationScale(child)); - EXPECT_EQ(kNotScaled, GetMaximumAnimationScale(grand_child)); + // |child| has a transform with perspective. Use |parent|'s maximum animation + // scale. + EXPECT_EQ(10.f, MaximumAnimationToScreenScale(grand_child)); + EXPECT_EQ(10.f, MaximumAnimationToScreenScale(child)); + EXPECT_EQ(10.f, MaximumAnimationToScreenScale(parent)); + EXPECT_EQ(2.f, MaximumAnimationToScreenScale(grand_parent)); - EXPECT_EQ(kNotScaled, GetStartingAnimationScale(grand_parent)); - EXPECT_EQ(2.f, GetStartingAnimationScale(parent)); - EXPECT_EQ(kNotScaled, GetStartingAnimationScale(child)); - EXPECT_EQ(kNotScaled, GetStartingAnimationScale(grand_child)); + EXPECT_TRUE(AnimationAffectedByInvalidScale(grand_child)); + EXPECT_TRUE(AnimationAffectedByInvalidScale(child)); + EXPECT_FALSE(AnimationAffectedByInvalidScale(parent)); + EXPECT_FALSE(AnimationAffectedByInvalidScale(grand_parent)); SetTransform(parent, perspective_matrix); UpdateActiveTreeDrawProperties(); - // |parent| and |child| have transforms that are neither translations nor - // scales. - EXPECT_EQ(kNotScaled, GetMaximumAnimationScale(grand_parent)); - EXPECT_EQ(kNotScaled, GetMaximumAnimationScale(parent)); - EXPECT_EQ(kNotScaled, GetMaximumAnimationScale(child)); - EXPECT_EQ(kNotScaled, GetMaximumAnimationScale(grand_child)); + // |parent| and |child| have transforms with perspective. Use |grand_parent|'s + // maximum animation scale. + EXPECT_EQ(2.f, MaximumAnimationToScreenScale(grand_child)); + EXPECT_EQ(2.f, MaximumAnimationToScreenScale(child)); + EXPECT_EQ(2.f, MaximumAnimationToScreenScale(parent)); + EXPECT_EQ(2.f, MaximumAnimationToScreenScale(grand_parent)); - EXPECT_EQ(kNotScaled, GetStartingAnimationScale(grand_parent)); - EXPECT_EQ(kNotScaled, GetStartingAnimationScale(parent)); - EXPECT_EQ(kNotScaled, GetStartingAnimationScale(child)); - EXPECT_EQ(kNotScaled, GetStartingAnimationScale(grand_child)); + EXPECT_TRUE(AnimationAffectedByInvalidScale(grand_child)); + EXPECT_TRUE(AnimationAffectedByInvalidScale(child)); + EXPECT_TRUE(AnimationAffectedByInvalidScale(parent)); + EXPECT_FALSE(AnimationAffectedByInvalidScale(grand_parent)); SetTransform(parent, gfx::Transform()); SetTransform(child, gfx::Transform()); SetTransform(grand_parent, perspective_matrix); + UpdateActiveTreeDrawProperties(); + + // |grand_parent| has a transform with perspective. + EXPECT_EQ(1.f, MaximumAnimationToScreenScale(grand_child)); + EXPECT_EQ(1.f, MaximumAnimationToScreenScale(child)); + EXPECT_EQ(1.f, MaximumAnimationToScreenScale(parent)); + EXPECT_EQ(1.f, MaximumAnimationToScreenScale(grand_parent)); + EXPECT_TRUE(AnimationAffectedByInvalidScale(grand_child)); + EXPECT_TRUE(AnimationAffectedByInvalidScale(child)); + EXPECT_TRUE(AnimationAffectedByInvalidScale(parent)); + EXPECT_TRUE(AnimationAffectedByInvalidScale(grand_parent)); + + gfx::Transform rotation_skew_scale; + rotation_skew_scale.Rotate(45.f); + rotation_skew_scale.Skew(45.f, 0.f); + rotation_skew_scale.Scale(2.f, 1.f); + SetTransform(grand_parent, rotation_skew_scale); UpdateActiveTreeDrawProperties(); - // |grand_parent| has a transform that's neither a translation nor a scale. - EXPECT_EQ(kNotScaled, GetMaximumAnimationScale(grand_parent)); - EXPECT_EQ(kNotScaled, GetMaximumAnimationScale(parent)); - EXPECT_EQ(kNotScaled, GetMaximumAnimationScale(child)); - EXPECT_EQ(kNotScaled, GetMaximumAnimationScale(grand_child)); + EXPECT_EQ(10.f, MaximumAnimationToScreenScale(grand_child)); + EXPECT_EQ(10.f, MaximumAnimationToScreenScale(child)); + EXPECT_EQ(10.f, MaximumAnimationToScreenScale(parent)); + EXPECT_EQ(2.f, MaximumAnimationToScreenScale(grand_parent)); - EXPECT_EQ(kNotScaled, GetStartingAnimationScale(grand_parent)); - EXPECT_EQ(kNotScaled, GetStartingAnimationScale(parent)); - EXPECT_EQ(kNotScaled, GetStartingAnimationScale(child)); - EXPECT_EQ(kNotScaled, GetStartingAnimationScale(grand_child)); + EXPECT_FALSE(AnimationAffectedByInvalidScale(grand_child)); + EXPECT_FALSE(AnimationAffectedByInvalidScale(child)); + EXPECT_FALSE(AnimationAffectedByInvalidScale(parent)); + EXPECT_FALSE(AnimationAffectedByInvalidScale(grand_parent)); } static void GatherDrawnLayers(LayerTreeImpl* tree_impl, @@ -5919,14 +5941,14 @@ TEST_F(DrawPropertiesTestWithLayerTree, DrawPropertyDeviceScale) { host()->SetRootLayer(root); host()->SetElementIdsForTesting(); - TransformOperations scale; + gfx::TransformOperations scale; scale.AppendScale(5.f, 8.f, 3.f); child2->SetTransform(scale_transform_child2); child2->SetBounds(gfx::Size(1, 1)); child2->SetIsDrawable(true); - AddAnimatedTransformToElementWithAnimation(child2->element_id(), timeline(), - 1.0, TransformOperations(), scale); + AddAnimatedTransformToElementWithAnimation( + child2->element_id(), timeline(), 1.0, gfx::TransformOperations(), scale); CommitAndActivate(); @@ -5935,9 +5957,9 @@ TEST_F(DrawPropertiesTestWithLayerTree, DrawPropertyDeviceScale) { EXPECT_FLOAT_EQ(3.f, ImplOf(mask)->GetIdealContentsScale()); EXPECT_FLOAT_EQ(5.f, ImplOf(child2)->GetIdealContentsScale()); - EXPECT_FLOAT_EQ(kNotScaled, GetMaximumAnimationScale(ImplOf(root))); - EXPECT_FLOAT_EQ(kNotScaled, GetMaximumAnimationScale(ImplOf(child1))); - EXPECT_FLOAT_EQ(8.f, GetMaximumAnimationScale(ImplOf(child2))); + EXPECT_FLOAT_EQ(8.f, MaximumAnimationToScreenScale(ImplOf(child2))); + EXPECT_FLOAT_EQ(3.f, MaximumAnimationToScreenScale(ImplOf(child1))); + EXPECT_FLOAT_EQ(1.f, MaximumAnimationToScreenScale(ImplOf(root))); // Changing device-scale would affect ideal_contents_scale and // maximum_animation_contents_scale. @@ -5950,9 +5972,9 @@ TEST_F(DrawPropertiesTestWithLayerTree, DrawPropertyDeviceScale) { EXPECT_FLOAT_EQ(12.f, ImplOf(mask)->GetIdealContentsScale()); EXPECT_FLOAT_EQ(20.f, ImplOf(child2)->GetIdealContentsScale()); - EXPECT_FLOAT_EQ(kNotScaled, GetMaximumAnimationScale(ImplOf(root))); - EXPECT_FLOAT_EQ(kNotScaled, GetMaximumAnimationScale(ImplOf(child1))); - EXPECT_FLOAT_EQ(32.f, GetMaximumAnimationScale(ImplOf(child2))); + EXPECT_FLOAT_EQ(32.f, MaximumAnimationToScreenScale(ImplOf(child2))); + EXPECT_FLOAT_EQ(12.f, MaximumAnimationToScreenScale(ImplOf(child1))); + EXPECT_FLOAT_EQ(4.f, MaximumAnimationToScreenScale(ImplOf(root))); } TEST_F(DrawPropertiesTest, DrawPropertyScales) { @@ -5991,11 +6013,11 @@ TEST_F(DrawPropertiesTest, DrawPropertyScales) { page_scale->transform_tree_index(); host()->RegisterViewportPropertyIds(viewport_property_ids); - TransformOperations scale; + gfx::TransformOperations scale; scale.AppendScale(5.f, 8.f, 3.f); - AddAnimatedTransformToElementWithAnimation(child2->element_id(), timeline(), - 1.0, TransformOperations(), scale); + AddAnimatedTransformToElementWithAnimation( + child2->element_id(), timeline(), 1.0, gfx::TransformOperations(), scale); CommitAndActivate(); @@ -6004,10 +6026,10 @@ TEST_F(DrawPropertiesTest, DrawPropertyScales) { EXPECT_FLOAT_EQ(3.f, ImplOf(child1)->GetIdealContentsScale()); EXPECT_FLOAT_EQ(5.f, ImplOf(child2)->GetIdealContentsScale()); - EXPECT_FLOAT_EQ(kNotScaled, GetMaximumAnimationScale(ImplOf(root))); - EXPECT_FLOAT_EQ(kNotScaled, GetMaximumAnimationScale(ImplOf(page_scale))); - EXPECT_FLOAT_EQ(kNotScaled, GetMaximumAnimationScale(ImplOf(child1))); - EXPECT_FLOAT_EQ(8.f, GetMaximumAnimationScale(ImplOf(child2))); + EXPECT_FLOAT_EQ(8.f, MaximumAnimationToScreenScale(ImplOf(child2))); + EXPECT_FLOAT_EQ(3.f, MaximumAnimationToScreenScale(ImplOf(child1))); + EXPECT_FLOAT_EQ(1.f, MaximumAnimationToScreenScale(ImplOf(page_scale))); + EXPECT_FLOAT_EQ(1.f, MaximumAnimationToScreenScale(ImplOf(root))); // Changing page-scale would affect ideal_contents_scale and // maximum_animation_contents_scale. @@ -6021,10 +6043,10 @@ TEST_F(DrawPropertiesTest, DrawPropertyScales) { EXPECT_FLOAT_EQ(9.f, ImplOf(child1)->GetIdealContentsScale()); EXPECT_FLOAT_EQ(15.f, ImplOf(child2)->GetIdealContentsScale()); - EXPECT_FLOAT_EQ(kNotScaled, GetMaximumAnimationScale(ImplOf(root))); - EXPECT_FLOAT_EQ(kNotScaled, GetMaximumAnimationScale(ImplOf(page_scale))); - EXPECT_FLOAT_EQ(kNotScaled, GetMaximumAnimationScale(ImplOf(child1))); - EXPECT_FLOAT_EQ(24.f, GetMaximumAnimationScale(ImplOf(child2))); + EXPECT_FLOAT_EQ(24.f, MaximumAnimationToScreenScale(ImplOf(child2))); + EXPECT_FLOAT_EQ(9.f, MaximumAnimationToScreenScale(ImplOf(child1))); + EXPECT_FLOAT_EQ(3.f, MaximumAnimationToScreenScale(ImplOf(page_scale))); + EXPECT_FLOAT_EQ(1.f, MaximumAnimationToScreenScale(ImplOf(root))); // Changing device-scale would affect ideal_contents_scale and // maximum_animation_contents_scale. @@ -6037,10 +6059,10 @@ TEST_F(DrawPropertiesTest, DrawPropertyScales) { EXPECT_FLOAT_EQ(36.f, ImplOf(child1)->GetIdealContentsScale()); EXPECT_FLOAT_EQ(60.f, ImplOf(child2)->GetIdealContentsScale()); - EXPECT_FLOAT_EQ(kNotScaled, GetMaximumAnimationScale(ImplOf(root))); - EXPECT_FLOAT_EQ(kNotScaled, GetMaximumAnimationScale(ImplOf(page_scale))); - EXPECT_FLOAT_EQ(kNotScaled, GetMaximumAnimationScale(ImplOf(child1))); - EXPECT_FLOAT_EQ(96.f, GetMaximumAnimationScale(ImplOf(child2))); + EXPECT_FLOAT_EQ(96.f, MaximumAnimationToScreenScale(ImplOf(child2))); + EXPECT_FLOAT_EQ(36.f, MaximumAnimationToScreenScale(ImplOf(child1))); + EXPECT_FLOAT_EQ(12.f, MaximumAnimationToScreenScale(ImplOf(page_scale))); + EXPECT_FLOAT_EQ(4.f, MaximumAnimationToScreenScale(ImplOf(root))); } TEST_F(DrawPropertiesTest, AnimationScales) { @@ -6062,34 +6084,83 @@ TEST_F(DrawPropertiesTest, AnimationScales) { CopyProperties(child1, child2); CreateTransformNode(child2).local = scale_transform_child2; - TransformOperations scale; + gfx::TransformOperations scale; scale.AppendScale(5.f, 8.f, 3.f); - AddAnimatedTransformToElementWithAnimation( - child2->element_id(), timeline_impl(), 1.0, TransformOperations(), scale); + AddAnimatedTransformToElementWithAnimation(child2->element_id(), + timeline_impl(), 1.0, + gfx::TransformOperations(), scale); UpdateActiveTreeDrawProperties(); - EXPECT_FLOAT_EQ(kNotScaled, GetMaximumAnimationScale(root)); - EXPECT_FLOAT_EQ(kNotScaled, GetMaximumAnimationScale(child1)); - EXPECT_FLOAT_EQ(24.f, GetMaximumAnimationScale(child2)); - - EXPECT_FLOAT_EQ(kNotScaled, GetStartingAnimationScale(root)); - EXPECT_FLOAT_EQ(kNotScaled, GetStartingAnimationScale(child1)); - EXPECT_FLOAT_EQ(3.f, GetStartingAnimationScale(child2)); + EXPECT_FLOAT_EQ(24.f, MaximumAnimationToScreenScale(child2)); + EXPECT_FLOAT_EQ(3.f, MaximumAnimationToScreenScale(child1)); + EXPECT_FLOAT_EQ(1.f, MaximumAnimationToScreenScale(root)); + EXPECT_FALSE(AnimationAffectedByInvalidScale(child2)); + EXPECT_FALSE(AnimationAffectedByInvalidScale(child1)); + EXPECT_FALSE(AnimationAffectedByInvalidScale(root)); // Correctly updates animation scale when layer property changes. SetTransform(child1, gfx::Transform()); root->layer_tree_impl()->SetTransformMutated(child1->element_id(), gfx::Transform()); UpdateActiveTreeDrawProperties(); - EXPECT_FLOAT_EQ(8.f, GetMaximumAnimationScale(child2)); - EXPECT_FLOAT_EQ(1.f, GetStartingAnimationScale(child2)); + EXPECT_FLOAT_EQ(8.f, MaximumAnimationToScreenScale(child2)); // Do not update animation scale if already updated. - host_impl()->active_tree()->property_trees()->SetAnimationScalesForTesting( - child2->transform_tree_index(), 100.f, 100.f); - EXPECT_FLOAT_EQ(100.f, GetMaximumAnimationScale(child2)); - EXPECT_FLOAT_EQ(100.f, GetStartingAnimationScale(child2)); + bool affected_by_invalid_scale = true; + host_impl() + ->active_tree() + ->property_trees() + ->SetMaximumAnimationToScreenScaleForTesting( + child2->transform_tree_index(), 100.f, affected_by_invalid_scale); + EXPECT_FLOAT_EQ(100.f, MaximumAnimationToScreenScale(child2)); + EXPECT_TRUE(AnimationAffectedByInvalidScale(child2)); +} + +TEST_F(DrawPropertiesTest, AnimationScaleFromSmallToOne) { + LayerImpl* root = root_layer(); + root->SetBounds(gfx::Size(1, 1)); + auto* parent = AddLayer<LayerImpl>(); + parent->SetBounds(gfx::Size(1, 1)); + auto* child = AddLayer<LayerImpl>(); + child->SetBounds(gfx::Size(1, 1)); + auto* grandchild = AddLayer<LayerImpl>(); + grandchild->SetBounds(gfx::Size(1, 1)); + SetElementIdsForTesting(); + + gfx::Transform parent_scale; + parent_scale.Scale(1, 2); + gfx::Transform small_scale; + small_scale.Scale(0.1, 0.2); + + CopyProperties(root, parent); + CreateTransformNode(parent).local = parent_scale; + CopyProperties(parent, child); + CreateTransformNode(child).local = small_scale; + CopyProperties(child, grandchild); + CreateTransformNode(grandchild).local = small_scale; + + gfx::TransformOperations small_scale_operations; + small_scale_operations.AppendMatrix(small_scale); + gfx::TransformOperations scale_one_operations; + + // Both child and grandchild animate scale from 0.1x0.2 to 1. + AddAnimatedTransformToElementWithAnimation( + child->element_id(), timeline_impl(), 1.0, small_scale_operations, + scale_one_operations); + AddAnimatedTransformToElementWithAnimation( + grandchild->element_id(), timeline_impl(), 1.0, small_scale_operations, + scale_one_operations); + UpdateActiveTreeDrawProperties(); + + EXPECT_FLOAT_EQ(2.f, MaximumAnimationToScreenScale(grandchild)); + EXPECT_FLOAT_EQ(2.f, MaximumAnimationToScreenScale(child)); + EXPECT_FLOAT_EQ(2.f, MaximumAnimationToScreenScale(parent)); + EXPECT_FLOAT_EQ(1.f, MaximumAnimationToScreenScale(root)); + EXPECT_FALSE(AnimationAffectedByInvalidScale(grandchild)); + EXPECT_FALSE(AnimationAffectedByInvalidScale(child)); + EXPECT_FALSE(AnimationAffectedByInvalidScale(parent)); + EXPECT_FALSE(AnimationAffectedByInvalidScale(root)); } TEST_F(DrawPropertiesTest, VisibleContentRectInChildRenderSurface) { @@ -6213,9 +6284,9 @@ TEST_F(DrawPropertiesTest, CreateEffectNode(surface).render_surface_reason = RenderSurfaceReason::kTest; CopyProperties(surface, descendant_of_keyframe_model); - TransformOperations start_transform_operations; + gfx::TransformOperations start_transform_operations; start_transform_operations.AppendMatrix(uninvertible_matrix); - TransformOperations end_transform_operations; + gfx::TransformOperations end_transform_operations; AddAnimatedTransformToElementWithAnimation( animated->element_id(), timeline_impl(), 10.0, start_transform_operations, @@ -6409,8 +6480,9 @@ TEST_F(DrawPropertiesTestWithLayerTree, SkippingSubtreeMain) { // a singular transform, the subtree should still get processed. int keyframe_model_id = 0; std::unique_ptr<KeyframeModel> keyframe_model = KeyframeModel::Create( - std::unique_ptr<AnimationCurve>(new FakeTransformTransition(1.0)), - keyframe_model_id, 1, TargetProperty::TRANSFORM); + std::unique_ptr<gfx::AnimationCurve>(new FakeTransformTransition(1.0)), + keyframe_model_id, 1, + KeyframeModel::TargetPropertyId(TargetProperty::TRANSFORM)); keyframe_model->set_fill_mode(KeyframeModel::FillMode::NONE); keyframe_model->set_time_offset(base::TimeDelta::FromMilliseconds(-1000)); AddKeyframeModelToElementWithAnimation(child->element_id(), timeline(), @@ -6436,8 +6508,10 @@ TEST_F(DrawPropertiesTestWithLayerTree, SkippingSubtreeMain) { // Add an opacity animation with a start delay. keyframe_model_id = 1; keyframe_model = KeyframeModel::Create( - std::unique_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)), - keyframe_model_id, 1, TargetProperty::OPACITY); + std::unique_ptr<gfx::AnimationCurve>( + new FakeFloatTransition(1.0, 0.f, 1.f)), + keyframe_model_id, 1, + KeyframeModel::TargetPropertyId(TargetProperty::OPACITY)); keyframe_model->set_fill_mode(KeyframeModel::FillMode::NONE); keyframe_model->set_time_offset(base::TimeDelta::FromMilliseconds(-1000)); AddKeyframeModelToElementWithExistingKeyframeEffect( @@ -6543,20 +6617,21 @@ TEST_F(DrawPropertiesTestWithLayerTree, SkippingLayerImpl) { EXPECT_EQ(gfx::Rect(10, 10), ImplOf(grandchild)->visible_layer_rect()); child->SetTransform(gfx::Transform()); - std::unique_ptr<KeyframedTransformAnimationCurve> curve( - KeyframedTransformAnimationCurve::Create()); - TransformOperations start; + std::unique_ptr<gfx::KeyframedTransformAnimationCurve> curve( + gfx::KeyframedTransformAnimationCurve::Create()); + gfx::TransformOperations start; start.AppendTranslate(1.f, 2.f, 3.f); gfx::Transform transform; transform.Scale3d(1.0, 2.0, 3.0); - TransformOperations operation; + gfx::TransformOperations operation; operation.AppendMatrix(transform); curve->AddKeyframe( - TransformKeyframe::Create(base::TimeDelta(), start, nullptr)); - curve->AddKeyframe(TransformKeyframe::Create( + gfx::TransformKeyframe::Create(base::TimeDelta(), start, nullptr)); + curve->AddKeyframe(gfx::TransformKeyframe::Create( base::TimeDelta::FromSecondsD(1.0), operation, nullptr)); - std::unique_ptr<KeyframeModel> transform_animation( - KeyframeModel::Create(std::move(curve), 3, 3, TargetProperty::TRANSFORM)); + std::unique_ptr<KeyframeModel> transform_animation(KeyframeModel::Create( + std::move(curve), 3, 3, + KeyframeModel::TargetPropertyId(TargetProperty::TRANSFORM))); scoped_refptr<Animation> animation(Animation::Create(1)); timeline()->AttachAnimation(animation); animation->AttachElement(parent->element_id()); @@ -6575,20 +6650,21 @@ TEST_F(DrawPropertiesTestWithLayerTree, SkippingLayerImpl) { // compositor without recomputing the trees. TEST_F(DrawPropertiesTest, LayerSkippingInSubtreeOfSingularTransform) { // Set up a transform animation - std::unique_ptr<KeyframedTransformAnimationCurve> curve( - KeyframedTransformAnimationCurve::Create()); - TransformOperations start; + std::unique_ptr<gfx::KeyframedTransformAnimationCurve> curve( + gfx::KeyframedTransformAnimationCurve::Create()); + gfx::TransformOperations start; start.AppendTranslate(1.f, 2.f, 3.f); gfx::Transform transform; transform.Scale3d(1.0, 2.0, 3.0); - TransformOperations operation; + gfx::TransformOperations operation; operation.AppendMatrix(transform); curve->AddKeyframe( - TransformKeyframe::Create(base::TimeDelta(), start, nullptr)); - curve->AddKeyframe(TransformKeyframe::Create( + gfx::TransformKeyframe::Create(base::TimeDelta(), start, nullptr)); + curve->AddKeyframe(gfx::TransformKeyframe::Create( base::TimeDelta::FromSecondsD(1.0), operation, nullptr)); - std::unique_ptr<KeyframeModel> transform_animation( - KeyframeModel::Create(std::move(curve), 3, 3, TargetProperty::TRANSFORM)); + std::unique_ptr<KeyframeModel> transform_animation(KeyframeModel::Create( + std::move(curve), 3, 3, + KeyframeModel::TargetPropertyId(TargetProperty::TRANSFORM))); scoped_refptr<Animation> animation(Animation::Create(1)); timeline_impl()->AttachAnimation(animation); animation->AddKeyframeModel(std::move(transform_animation)); @@ -6690,17 +6766,18 @@ TEST_F(DrawPropertiesTestWithLayerTree, SkippingPendingLayerImpl) { EXPECT_EQ(gfx::Rect(), PendingImplOf(grandchild)->visible_layer_rect()); // Check the animated case is not skipped. - std::unique_ptr<KeyframedFloatAnimationCurve> curve( - KeyframedFloatAnimationCurve::Create()); - std::unique_ptr<TimingFunction> func = - CubicBezierTimingFunction::CreatePreset( - CubicBezierTimingFunction::EaseType::EASE); - curve->AddKeyframe( - FloatKeyframe::Create(base::TimeDelta(), 0.9f, std::move(func))); + std::unique_ptr<gfx::KeyframedFloatAnimationCurve> curve( + gfx::KeyframedFloatAnimationCurve::Create()); + std::unique_ptr<gfx::TimingFunction> func = + gfx::CubicBezierTimingFunction::CreatePreset( + gfx::CubicBezierTimingFunction::EaseType::EASE); curve->AddKeyframe( - FloatKeyframe::Create(base::TimeDelta::FromSecondsD(1.0), 0.3f, nullptr)); - std::unique_ptr<KeyframeModel> keyframe_model( - KeyframeModel::Create(std::move(curve), 3, 3, TargetProperty::OPACITY)); + gfx::FloatKeyframe::Create(base::TimeDelta(), 0.9f, std::move(func))); + curve->AddKeyframe(gfx::FloatKeyframe::Create( + base::TimeDelta::FromSecondsD(1.0), 0.3f, nullptr)); + std::unique_ptr<KeyframeModel> keyframe_model(KeyframeModel::Create( + std::move(curve), 3, 3, + KeyframeModel::TargetPropertyId(TargetProperty::OPACITY))); scoped_refptr<Animation> animation(Animation::Create(1)); timeline()->AttachAnimation(animation); animation->AddKeyframeModel(std::move(keyframe_model)); @@ -7504,8 +7581,10 @@ TEST_F(DrawPropertiesTestWithLayerTree, OpacityAnimationsTrackingTest) { int keyframe_model_id = 0; std::unique_ptr<KeyframeModel> keyframe_model = KeyframeModel::Create( - std::unique_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)), - keyframe_model_id, 1, TargetProperty::OPACITY); + std::unique_ptr<gfx::AnimationCurve>( + new FakeFloatTransition(1.0, 0.f, 1.f)), + keyframe_model_id, 1, + KeyframeModel::TargetPropertyId(TargetProperty::OPACITY)); keyframe_model->set_fill_mode(KeyframeModel::FillMode::NONE); keyframe_model->set_time_offset(base::TimeDelta::FromMilliseconds(-1000)); KeyframeModel* keyframe_model_ptr = keyframe_model.get(); @@ -7549,20 +7628,21 @@ TEST_F(DrawPropertiesTestWithLayerTree, TransformAnimationsTrackingTest) { timeline()->AttachAnimation(animation); animation->AttachElement(animated->element_id()); - std::unique_ptr<KeyframedTransformAnimationCurve> curve( - KeyframedTransformAnimationCurve::Create()); - TransformOperations start; + std::unique_ptr<gfx::KeyframedTransformAnimationCurve> curve( + gfx::KeyframedTransformAnimationCurve::Create()); + gfx::TransformOperations start; start.AppendTranslate(1.f, 2.f, 3.f); gfx::Transform transform; transform.Scale3d(1.0, 2.0, 3.0); - TransformOperations operation; + gfx::TransformOperations operation; operation.AppendMatrix(transform); curve->AddKeyframe( - TransformKeyframe::Create(base::TimeDelta(), start, nullptr)); - curve->AddKeyframe(TransformKeyframe::Create( + gfx::TransformKeyframe::Create(base::TimeDelta(), start, nullptr)); + curve->AddKeyframe(gfx::TransformKeyframe::Create( base::TimeDelta::FromSecondsD(1.0), operation, nullptr)); - std::unique_ptr<KeyframeModel> keyframe_model( - KeyframeModel::Create(std::move(curve), 3, 3, TargetProperty::TRANSFORM)); + std::unique_ptr<KeyframeModel> keyframe_model(KeyframeModel::Create( + std::move(curve), 3, 3, + KeyframeModel::TargetPropertyId(TargetProperty::TRANSFORM))); keyframe_model->set_fill_mode(KeyframeModel::FillMode::NONE); keyframe_model->set_time_offset(base::TimeDelta::FromMilliseconds(-1000)); KeyframeModel* keyframe_model_ptr = keyframe_model.get(); diff --git a/chromium/cc/trees/draw_property_utils.cc b/chromium/cc/trees/draw_property_utils.cc index c2e79fc3b7e..aa4433edce3 100644 --- a/chromium/cc/trees/draw_property_utils.cc +++ b/chromium/cc/trees/draw_property_utils.cc @@ -640,19 +640,20 @@ void SetSurfaceDrawTransform(const PropertyTrees* property_trees, gfx::Rect LayerVisibleRect(PropertyTrees* property_trees, LayerImpl* layer) { const EffectNode* effect_node = property_trees->effect_tree.Node(layer->effect_tree_index()); - int effect_ancestor_with_cache_render_surface = - effect_node->closest_ancestor_with_cached_render_surface_id; - int effect_ancestor_with_copy_request = - effect_node->closest_ancestor_with_copy_request_id; int lower_effect_closest_ancestor = - std::max(effect_ancestor_with_cache_render_surface, - effect_ancestor_with_copy_request); - bool non_root_copy_request_or_cache_render_surface = + effect_node->closest_ancestor_with_cached_render_surface_id; + lower_effect_closest_ancestor = + std::max(lower_effect_closest_ancestor, + effect_node->closest_ancestor_with_copy_request_id); + lower_effect_closest_ancestor = + std::max(lower_effect_closest_ancestor, + effect_node->closest_ancestor_being_captured_id); + const bool non_root_with_render_surface = lower_effect_closest_ancestor > EffectTree::kContentsRootNodeId; gfx::Rect layer_content_rect = gfx::Rect(layer->bounds()); gfx::RectF accumulated_clip_in_root_space; - if (non_root_copy_request_or_cache_render_surface) { + if (non_root_with_render_surface) { bool include_expanding_clips = true; ConditionalClip accumulated_clip = ComputeAccumulatedClip( property_trees, include_expanding_clips, layer->clip_tree_index(), @@ -668,7 +669,7 @@ gfx::Rect LayerVisibleRect(PropertyTrees* property_trees, LayerImpl* layer) { } const EffectNode* root_effect_node = - non_root_copy_request_or_cache_render_surface + non_root_with_render_surface ? property_trees->effect_tree.Node(lower_effect_closest_ancestor) : property_trees->effect_tree.Node(EffectTree::kContentsRootNodeId); ConditionalClip accumulated_clip_in_layer_space = @@ -890,8 +891,7 @@ void AddSurfaceToRenderSurfaceList(RenderSurfaceImpl* render_surface, // TODO(senorblanco): make this smarter for the SkImageFilter case (check for // pixel-moving filters) const FilterOperations& filters = render_surface->Filters(); - bool is_occlusion_immune = render_surface->HasCopyRequest() || - render_surface->ShouldCacheRenderSurface() || + bool is_occlusion_immune = render_surface->CopyOfOutputRequired() || filters.HasReferenceFilter() || filters.HasFilterThatMovesPixels(); if (is_occlusion_immune) { @@ -1225,9 +1225,11 @@ bool CC_EXPORT LayerShouldBeSkippedForDrawPropertiesComputation( if (effect_node->HasRenderSurface() && effect_node->subtree_has_copy_request) return false; - // Skip if the node's subtree is hidden and no need to cache. - if (effect_node->subtree_hidden && !effect_node->cache_render_surface) + // Skip if the node's subtree is hidden and no need to cache, or capture. + if (effect_node->subtree_hidden && !effect_node->cache_render_surface && + !effect_node->subtree_capture_id.is_valid()) { return true; + } // If the layer transform is not invertible, it should be skipped. In case the // transform is animating and singular, we should not skip it. diff --git a/chromium/cc/trees/effect_node.cc b/chromium/cc/trees/effect_node.cc index fa07fea5b79..20b4257603f 100644 --- a/chromium/cc/trees/effect_node.cc +++ b/chromium/cc/trees/effect_node.cc @@ -42,7 +42,8 @@ EffectNode::EffectNode() clip_id(0), target_id(1), closest_ancestor_with_cached_render_surface_id(-1), - closest_ancestor_with_copy_request_id(-1) {} + closest_ancestor_with_copy_request_id(-1), + closest_ancestor_being_captured_id(-1) {} EffectNode::EffectNode(const EffectNode& other) = default; @@ -54,6 +55,7 @@ bool EffectNode::operator==(const EffectNode& other) const { stable_id == other.stable_id && opacity == other.opacity && screen_space_opacity == other.screen_space_opacity && backdrop_filter_quality == other.backdrop_filter_quality && + subtree_capture_id == other.subtree_capture_id && cache_render_surface == other.cache_render_surface && has_copy_request == other.has_copy_request && filters == other.filters && @@ -94,7 +96,9 @@ bool EffectNode::operator==(const EffectNode& other) const { closest_ancestor_with_cached_render_surface_id == other.closest_ancestor_with_cached_render_surface_id && closest_ancestor_with_copy_request_id == - other.closest_ancestor_with_copy_request_id; + other.closest_ancestor_with_copy_request_id && + closest_ancestor_being_captured_id == + other.closest_ancestor_being_captured_id; } #endif // DCHECK_IS_ON() @@ -138,6 +142,10 @@ const char* RenderSurfaceReasonToString(RenderSurfaceReason reason) { return "cache"; case RenderSurfaceReason::kCopyRequest: return "copy request"; + case RenderSurfaceReason::kMirrored: + return "mirrored"; + case RenderSurfaceReason::kSubtreeIsBeingCaptured: + return "subtree being captured"; case RenderSurfaceReason::kTest: return "test"; default: @@ -172,6 +180,7 @@ void EffectNode::AsValueInto(base::trace_event::TracedValue* value) const { } } value->SetString("blend_mode", SkBlendMode_Name(blend_mode)); + value->SetString("subtree_capture_id", subtree_capture_id.ToString()); value->SetBoolean("cache_render_surface", cache_render_surface); value->SetBoolean("has_copy_request", has_copy_request); value->SetBoolean("double_sided", double_sided); @@ -198,6 +207,8 @@ void EffectNode::AsValueInto(base::trace_event::TracedValue* value) const { closest_ancestor_with_cached_render_surface_id); value->SetInteger("closest_ancestor_with_copy_request_id", closest_ancestor_with_copy_request_id); + value->SetInteger("closest_ancestor_being_captured_id", + closest_ancestor_being_captured_id); value->SetBoolean("affected_by_backdrop_filter", affected_by_backdrop_filter); } diff --git a/chromium/cc/trees/effect_node.h b/chromium/cc/trees/effect_node.h index 2a2541d123d..65edbca03b1 100644 --- a/chromium/cc/trees/effect_node.h +++ b/chromium/cc/trees/effect_node.h @@ -8,6 +8,7 @@ #include "cc/cc_export.h" #include "cc/paint/element_id.h" #include "cc/paint/filter_operations.h" +#include "components/viz/common/surfaces/subtree_capture_id.h" #include "third_party/skia/include/core/SkBlendMode.h" #include "ui/gfx/geometry/point_f.h" #include "ui/gfx/geometry/size_f.h" @@ -44,6 +45,7 @@ enum class RenderSurfaceReason : uint8_t { kCache, kCopyRequest, kMirrored, + kSubtreeIsBeingCaptured, // This must be the last value because it's used in tracing code to know the // number of reasons. kTest, @@ -90,6 +92,8 @@ struct CC_EXPORT EffectNode { gfx::Vector2dF surface_contents_scale; + viz::SubtreeCaptureId subtree_capture_id; + bool cache_render_surface : 1; bool has_copy_request : 1; bool hidden_by_backface_visibility : 1; @@ -154,6 +158,7 @@ struct CC_EXPORT EffectNode { int target_id; int closest_ancestor_with_cached_render_surface_id; int closest_ancestor_with_copy_request_id; + int closest_ancestor_being_captured_id; bool HasRenderSurface() const { return render_surface_reason != RenderSurfaceReason::kNone; diff --git a/chromium/cc/trees/image_animation_controller.cc b/chromium/cc/trees/image_animation_controller.cc index bfb3f30cc30..9ea34f94547 100644 --- a/chromium/cc/trees/image_animation_controller.cc +++ b/chromium/cc/trees/image_animation_controller.cc @@ -10,6 +10,7 @@ #include "base/bind.h" #include "base/metrics/histogram_macros.h" #include "base/trace_event/trace_event.h" +#include "cc/base/features.h" #include "cc/paint/image_animation_count.h" namespace cc { @@ -41,7 +42,9 @@ ImageAnimationController::ImageAnimationController( Client* client, bool enable_image_animation_resync) : scheduler_(task_runner, client), - enable_image_animation_resync_(enable_image_animation_resync) {} + enable_image_animation_resync_(enable_image_animation_resync), + use_resume_behavior_( + base::FeatureList::IsEnabled(features::kAnimatedImageResume)) {} ImageAnimationController::~ImageAnimationController() = default; @@ -96,7 +99,8 @@ const PaintImageIdFlatSet& ImageAnimationController::AnimateForSyncTree( // If we were able to advance this animation, invalidate it on the sync // tree. - if (state.AdvanceFrame(args, enable_image_animation_resync_)) + if (state.AdvanceFrame(args, enable_image_animation_resync_, + use_resume_behavior_)) images_animated_on_sync_tree_.insert(id); TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"), @@ -225,7 +229,14 @@ ImageAnimationController::AnimationState::~AnimationState() { } bool ImageAnimationController::AnimationState::ShouldAnimate() const { - DCHECK(repetitions_completed_ == 0 || is_complete()); + return ShouldAnimate(current_state_.repetitions_completed, + current_state_.pending_index); +} + +bool ImageAnimationController::AnimationState::ShouldAnimate( + int repetitions_completed, + size_t pending_index) const { + DCHECK(current_state_.repetitions_completed == 0 || is_complete()); // If we have no drivers for this image, no need to animate it. if (!should_animate_from_drivers_) @@ -233,7 +244,7 @@ bool ImageAnimationController::AnimationState::ShouldAnimate() const { switch (requested_repetitions_) { case kAnimationLoopOnce: - if (repetitions_completed_ >= 1) + if (repetitions_completed >= 1) return false; break; case kAnimationNone: @@ -242,20 +253,20 @@ bool ImageAnimationController::AnimationState::ShouldAnimate() const { case kAnimationLoopInfinite: break; default: - if (requested_repetitions_ <= repetitions_completed_) + if (requested_repetitions_ <= repetitions_completed) return false; } // If we have not yet received all data for this image, we can not advance to // an incomplete frame. - if (!frames_[NextFrameIndex()].complete) + if (!frames_[NextFrameIndex(pending_index)].complete) return false; // If we don't have all data for this image, we can not trust the frame count // and loop back to the first frame. size_t last_frame_index = frames_.size() - 1; if (completion_state_ != PaintImage::CompletionState::DONE && - pending_index_ == last_frame_index) { + pending_index == last_frame_index) { return false; } @@ -274,25 +285,28 @@ bool ImageAnimationController::AnimationState::ShouldAnimate() const { // the frame should be displayed. bool ImageAnimationController::AnimationState::AdvanceFrame( const viz::BeginFrameArgs& args, - bool enable_image_animation_resync) { - DCHECK(ShouldAnimate()); + bool enable_image_animation_resync, + bool use_resume_behavior) { + DCHECK(ShouldAnimate(current_state_.repetitions_completed, + current_state_.pending_index)); const base::TimeTicks next_tick_time = args.frame_time + args.interval; // Start the animation from the first frame, if not yet started. The code // falls through to catching up if the duration for the first frame is less // than the interval. if (!animation_started_) { - DCHECK_EQ(pending_index_, 0u); + DCHECK_EQ(current_state_.pending_index, 0u); animation_started_time_ = args.frame_time; - next_desired_frame_time_ = args.frame_time + frames_[0].duration; - next_desired_tick_time_ = - SnappedTickTimeFromFrameTime(args, next_desired_frame_time_); + current_state_.next_desired_frame_time = + args.frame_time + frames_[0].duration; + current_state_.next_desired_tick_time = SnappedTickTimeFromFrameTime( + args, current_state_.next_desired_frame_time); animation_started_ = true; } // Don't advance the animation if its not time yet to move to the next frame. - if (args.frame_time < next_desired_tick_time_) + if (args.frame_time < current_state_.next_desired_tick_time) return needs_invalidation(); // If the animation is more than 5 min out of date, we don't bother catching @@ -300,35 +314,70 @@ bool ImageAnimationController::AnimationState::AdvanceFrame( // Note that we don't need to invalidate this image since the active tree // is already displaying the current frame. if (enable_image_animation_resync && - args.frame_time - next_desired_frame_time_ > kAnimationResyncCutoff) { + args.frame_time - current_state_.next_desired_frame_time > + kAnimationResyncCutoff) { TRACE_EVENT_INSTANT0("cc", "Resync - early out", TRACE_EVENT_SCOPE_THREAD); - DCHECK_EQ(pending_index_, active_index_); - next_desired_frame_time_ = - args.frame_time + frames_[pending_index_].duration; - next_desired_tick_time_ = - std::max(SnappedTickTimeFromFrameTime(args, next_desired_frame_time_), + DCHECK_EQ(current_state_.pending_index, active_index_); + current_state_.next_desired_frame_time = + args.frame_time + frames_[current_state_.pending_index].duration; + current_state_.next_desired_tick_time = + std::max(SnappedTickTimeFromFrameTime( + args, current_state_.next_desired_frame_time), next_tick_time); return needs_invalidation(); } - // Keep catching up the animation until we reach the frame we should be - // displaying now. - const size_t last_frame_index = frames_.size() - 1; - size_t num_of_frames_advanced = 0u; - while (next_desired_tick_time_ < next_tick_time && ShouldAnimate()) { - num_of_frames_advanced++; - size_t next_frame_index = NextFrameIndex(); + current_state_.num_of_frames_advanced = 0u; + if (use_resume_behavior) { + // When using the resume method, run the animation advancement starting + // at the current frame time rather than the saved tick time. + + // Advance only as many frames as would fit in the display rate. + // IE if the display refresh rate is 60 Hz and the animated image updated + // every 11 ms, we could have a display frame that spans 2 animation + // frames. + current_state_ = AdvanceAnimationState( + current_state_, args, args.frame_time, enable_image_animation_resync); + } else { + // Keep catching up the animation from the last saved tick time until we + // reach the frame we should be displaying now. + current_state_ = AdvanceAnimationState( + current_state_, args, current_state_.next_desired_tick_time, + enable_image_animation_resync); + } + DCHECK_GE(current_state_.num_of_frames_advanced, 1u); + last_num_frames_skipped_ = current_state_.num_of_frames_advanced - 1u; + + return needs_invalidation(); +} + +ImageAnimationController::AnimationState::AnimationAdvancementState +ImageAnimationController::AnimationState::AdvanceAnimationState( + AnimationAdvancementState animation_advancement_state, + const viz::BeginFrameArgs& args, + base::TimeTicks start, + bool enable_image_animation_resync) const { + const base::TimeTicks end = args.frame_time + args.interval; + base::TimeTicks elapsed_time = start; + while (elapsed_time < end && + ShouldAnimate(animation_advancement_state.repetitions_completed, + animation_advancement_state.pending_index)) { + animation_advancement_state.num_of_frames_advanced++; + size_t next_frame_index = + NextFrameIndex(animation_advancement_state.pending_index); + elapsed_time += frames_[next_frame_index].duration; TRACE_EVENT_INSTANT2(TRACE_DISABLED_BY_DEFAULT("cc.debug"), - "FrameDurationAndIndex", TRACE_EVENT_SCOPE_THREAD, + "FrameDurationIndex", TRACE_EVENT_SCOPE_THREAD, "frame_index", next_frame_index, "duration", frames_[next_frame_index].duration.InMillisecondsF()); base::TimeTicks next_desired_frame_time = - next_desired_frame_time_ + frames_[next_frame_index].duration; + animation_advancement_state.next_desired_frame_time + + frames_[next_frame_index].duration; // The image may load more slowly than it's supposed to animate, so that by // the time we reach the end of the first repetition, we're well behind. // Start the animation from the first frame in this case, so that we don't - // skip frames (or whole iterations) trying to "catch up". This is a + // skip frames (or whole iterations) trying to "catch up". This is a // tradeoff: It guarantees users see the whole animation the second time // through and don't miss any repetitions, and is closer to what other // browsers do; on the other hand, it makes animations "less accurate" for @@ -336,55 +385,62 @@ bool ImageAnimationController::AnimationState::AdvanceFrame( // especially if users switch tabs (and thus stop drawing the animation, // which will pause it) during that initial loop, then switch back later. if (enable_image_animation_resync && next_frame_index == 0u && - repetitions_completed_ == 1 && + animation_advancement_state.repetitions_completed == 1 && next_desired_frame_time <= args.frame_time) { - pending_index_ = 0u; - next_desired_frame_time_ = args.frame_time + frames_[0].duration; - next_desired_tick_time_ = - std::max(SnappedTickTimeFromFrameTime(args, next_desired_frame_time_), - next_tick_time); - repetitions_completed_ = 0; + animation_advancement_state.pending_index = 0u; + animation_advancement_state.next_desired_frame_time = + args.frame_time + frames_[0].duration; + animation_advancement_state.next_desired_tick_time = std::max( + SnappedTickTimeFromFrameTime( + args, animation_advancement_state.next_desired_frame_time), + end); + animation_advancement_state.repetitions_completed = 0; break; } - pending_index_ = next_frame_index; - next_desired_frame_time_ = next_desired_frame_time; - next_desired_tick_time_ = - SnappedTickTimeFromFrameTime(args, next_desired_frame_time_); + animation_advancement_state.pending_index = next_frame_index; + animation_advancement_state.next_desired_frame_time = + next_desired_frame_time; + animation_advancement_state.next_desired_tick_time = + SnappedTickTimeFromFrameTime( + args, animation_advancement_state.next_desired_frame_time); // If we are advancing to the last frame and the image has been completely // loaded (which means that the frame count is known to be accurate), we // just finished a loop in the animation. - if (pending_index_ == last_frame_index && is_complete()) - repetitions_completed_++; + if (animation_advancement_state.pending_index == frames_.size() - 1 && + is_complete()) + animation_advancement_state.repetitions_completed++; } // We should have advanced a single frame, anything more than that are frames // skipped trying to catch up. - DCHECK_GT(num_of_frames_advanced, 0u); - last_num_frames_skipped_ = num_of_frames_advanced - 1u; - switch (repetitions_completed_) { + DCHECK_GT(animation_advancement_state.num_of_frames_advanced, 0u); + size_t frames_skipped = + animation_advancement_state.num_of_frames_advanced - 1u; + switch (animation_advancement_state.repetitions_completed) { case 0: UMA_HISTOGRAM_COUNTS_100000( "AnimatedImage.NumOfFramesSkipped.FirstAnimationLoop", - last_num_frames_skipped_); + frames_skipped); break; case 1: UMA_HISTOGRAM_COUNTS_100000( "AnimatedImage.NumOfFramesSkipped.SecondAnimationLoop", - last_num_frames_skipped_); + frames_skipped); break; case 2: case 3: case 4: UMA_HISTOGRAM_COUNTS_100000( "AnimatedImage.NumOfFramesSkipped.ThirdToFifthAnimationLoop", - last_num_frames_skipped_); + frames_skipped); break; } UMA_HISTOGRAM_COUNTS_100000("AnimatedImage.NumOfFramesSkipped.Compositor", - last_num_frames_skipped_); - return needs_invalidation(); + frames_skipped); + + return animation_advancement_state; } void ImageAnimationController::AnimationState::UpdateMetadata( @@ -408,9 +464,9 @@ void ImageAnimationController::AnimationState::UpdateMetadata( // Update the repetition count in case we have displayed the last frame and // we now know the frame count to be accurate. size_t last_frame_index = frames_.size() - 1; - if (pending_index_ == last_frame_index && is_complete() && - repetitions_completed_ == 0) - repetitions_completed_++; + if (current_state_.pending_index == last_frame_index && is_complete() && + current_state_.repetitions_completed == 0) + current_state_.repetitions_completed++; // Reset the animation if the sequence id received in this recording was // incremented. @@ -421,7 +477,7 @@ void ImageAnimationController::AnimationState::UpdateMetadata( } void ImageAnimationController::AnimationState::PushPendingToActive() { - active_index_ = pending_index_; + active_index_ = current_state_.pending_index; } void ImageAnimationController::AnimationState::AddDriver( @@ -446,9 +502,9 @@ void ImageAnimationController::AnimationState::UpdateStateFromDrivers() { void ImageAnimationController::AnimationState::ResetAnimation() { animation_started_ = false; - next_desired_frame_time_ = base::TimeTicks(); - repetitions_completed_ = 0; - pending_index_ = 0u; + current_state_.next_desired_frame_time = base::TimeTicks(); + current_state_.repetitions_completed = 0; + current_state_.pending_index = 0u; // Don't reset the |active_index_|, tiles on the active tree still need it. } @@ -458,19 +514,22 @@ std::string ImageAnimationController::AnimationState::ToString() const { << requested_repetitions_ << "]\nrepetitions_completed[" << requested_repetitions_ << "]\ndrivers[" << drivers_.size() << "]\nactive_index[" << active_index_ << "]\npending_index[" - << pending_index_ << "]\nnext_desired_frame_time[" - << (next_desired_frame_time_ - animation_started_time_).InMillisecondsF() + << current_state_.pending_index << "]\nnext_desired_frame_time[" + << (current_state_.next_desired_frame_time - animation_started_time_) + .InMillisecondsF() << "]\nnext_desired_tick_time[" - << (next_desired_tick_time_ - animation_started_time_).InMillisecondsF() + << (current_state_.next_desired_tick_time - animation_started_time_) + .InMillisecondsF() << "]\nshould_animate_from_drivers[" << should_animate_from_drivers_ << "]\ncompletion_state[" << static_cast<int>(completion_state_) << "]"; return str.str(); } -size_t ImageAnimationController::AnimationState::NextFrameIndex() const { +size_t ImageAnimationController::AnimationState::NextFrameIndex( + size_t pending_index) const { if (!animation_started_) return 0u; - return (pending_index_ + 1) % frames_.size(); + return (pending_index + 1) % frames_.size(); } ImageAnimationController::InvalidationScheduler::InvalidationScheduler( diff --git a/chromium/cc/trees/image_animation_controller.h b/chromium/cc/trees/image_animation_controller.h index 59c1c19faa1..5dfcdb374cd 100644 --- a/chromium/cc/trees/image_animation_controller.h +++ b/chromium/cc/trees/image_animation_controller.h @@ -144,8 +144,10 @@ class CC_EXPORT ImageAnimationController { AnimationState& operator=(AnimationState&& other); bool ShouldAnimate() const; + bool ShouldAnimate(int repetitions_completed, size_t pending_index) const; bool AdvanceFrame(const viz::BeginFrameArgs& args, - bool enable_image_animation_resync); + bool enable_image_animation_resync, + bool use_resume_behavior); void UpdateMetadata(const DiscardableImageMap::AnimatedImageMetadata& data); void PushPendingToActive(); @@ -154,10 +156,10 @@ class CC_EXPORT ImageAnimationController { void UpdateStateFromDrivers(); bool has_drivers() const { return !drivers_.empty(); } - size_t pending_index() const { return pending_index_; } + size_t pending_index() const { return current_state_.pending_index; } size_t active_index() const { return active_index_; } base::TimeTicks next_desired_tick_time() const { - return next_desired_tick_time_; + return current_state_.next_desired_tick_time; } const base::flat_set<AnimationDriver*>& drivers_for_testing() const { return drivers_; @@ -168,12 +170,42 @@ class CC_EXPORT ImageAnimationController { std::string ToString() const; private: + struct AnimationAdvancementState { + // The index being displayed on the pending tree. + size_t pending_index = PaintImage::kDefaultFrameIndex; + + // The time at which we would like to display the next frame. This can be + // in the past, for instance, if we pause the animation from the image + // becoming invisible. This time is updated based on either the animation + // timeline provided by the image (when using Catch-up behavior) or the + // next displayed frame (when using Resume behavior). Here, "displayed + // frame" means an animation that updates faster than the display's + // refresh rate and might skip frames to maintain display speed. See + // kAnimatedImageResume. + base::TimeTicks next_desired_frame_time; + + // The time of the next tick at which we want to invalidate and update the + // current frame. + base::TimeTicks next_desired_tick_time; + + // The number of loops the animation has finished so far. + int repetitions_completed = 0; + size_t num_of_frames_advanced = 0u; + }; + + AnimationAdvancementState AdvanceAnimationState( + AnimationAdvancementState animation_advancement_state, + const viz::BeginFrameArgs& args, + base::TimeTicks start, + bool enable_image_animation_resync) const; void ResetAnimation(); - size_t NextFrameIndex() const; + size_t NextFrameIndex(size_t pending_index) const; bool is_complete() const { return completion_state_ == PaintImage::CompletionState::DONE; } - bool needs_invalidation() const { return pending_index_ != active_index_; } + bool needs_invalidation() const { + return current_state_.pending_index != active_index_; + } PaintImage::Id paint_image_id_ = PaintImage::kInvalidId; @@ -187,8 +219,7 @@ class CC_EXPORT ImageAnimationController { // cc/paint/image_animation_count.h int requested_repetitions_ = kAnimationNone; - // The number of loops the animation has finished so far. - int repetitions_completed_ = 0; + AnimationAdvancementState current_state_; // A set of drivers interested in animating this image. base::flat_set<AnimationDriver*> drivers_; @@ -197,19 +228,6 @@ class CC_EXPORT ImageAnimationController { // is still present. size_t active_index_ = PaintImage::kDefaultFrameIndex; - // The index being displayed on the pending tree. - size_t pending_index_ = PaintImage::kDefaultFrameIndex; - - // The time at which we would like to display the next frame. This can be in - // the past, for instance, if we pause the animation from the image becoming - // invisible. This time is updated strictly based on the animation timeline - // provided by the image. - base::TimeTicks next_desired_frame_time_; - - // The time of the next tick at which we want to invalidate and update the - // current frame. - base::TimeTicks next_desired_tick_time_; - // Set if there is at least one driver interested in animating this image, // cached from the last update. bool should_animate_from_drivers_ = false; @@ -290,6 +308,7 @@ class CC_EXPORT ImageAnimationController { InvalidationScheduler scheduler_; const bool enable_image_animation_resync_; + const bool use_resume_behavior_; bool did_navigate_ = false; }; diff --git a/chromium/cc/trees/layer_tree_host.cc b/chromium/cc/trees/layer_tree_host.cc index 03e3c41c5e5..3b598eb3144 100644 --- a/chromium/cc/trees/layer_tree_host.cc +++ b/chromium/cc/trees/layer_tree_host.cc @@ -37,6 +37,7 @@ #include "cc/base/histograms.h" #include "cc/base/math_util.h" #include "cc/debug/rendering_stats_instrumentation.h" +#include "cc/document_transition/document_transition_request.h" #include "cc/input/layer_selection_bound.h" #include "cc/input/overscroll_behavior.h" #include "cc/input/page_scale_animation.h" @@ -198,8 +199,6 @@ void LayerTreeHost::InitializeProxy(std::unique_ptr<Proxy> proxy) { proxy_->Start(); UpdateDeferMainFrameUpdateInternal(); - - mutator_host_->SetSupportsScrollAnimations(proxy_->SupportsImplScrolling()); } LayerTreeHost::~LayerTreeHost() { @@ -491,6 +490,10 @@ void LayerTreeHost::CommitComplete() { client_->DidCompletePageScaleAnimation(); did_complete_scale_animation_ = false; } + + for (auto& closure : committed_document_transition_callbacks_) + std::move(closure).Run(); + committed_document_transition_callbacks_.clear(); } void LayerTreeHost::SetLayerTreeFrameSink( @@ -535,9 +538,8 @@ std::unique_ptr<LayerTreeHostImpl> LayerTreeHost::CreateLayerTreeHostImpl( LayerTreeHostImplClient* client) { DCHECK(task_runner_provider_->IsImplThread()); - const bool supports_impl_scrolling = task_runner_provider_->HasImplThread(); std::unique_ptr<MutatorHost> mutator_host_impl = - mutator_host_->CreateImplInstance(supports_impl_scrolling); + mutator_host_->CreateImplInstance(); if (!settings_.scroll_animation_duration_for_testing.is_zero()) { mutator_host_->SetScrollAnimationDurationForTesting( @@ -653,10 +655,7 @@ void LayerTreeHost::SetNeedsCommitWithForcedRedraw() { proxy_->SetNeedsCommit(); } -void LayerTreeHost::SetDebugState(const LayerTreeDebugState& debug_state) { - LayerTreeDebugState new_debug_state = - LayerTreeDebugState::Unite(settings_.initial_debug_state, debug_state); - +void LayerTreeHost::SetDebugState(const LayerTreeDebugState& new_debug_state) { if (LayerTreeDebugState::Equal(debug_state_, new_debug_state)) return; @@ -780,11 +779,12 @@ std::string LayerTreeHost::LayersAsString() const { } bool LayerTreeHost::CaptureContent(std::vector<NodeInfo>* content) { - if (viewport_visible_rect_.IsEmpty()) + if (visual_device_viewport_intersection_rect_.IsEmpty()) return false; - gfx::Rect rect = gfx::Rect(viewport_visible_rect_.width(), - viewport_visible_rect_.height()); + gfx::Rect rect = + gfx::Rect(visual_device_viewport_intersection_rect_.width(), + visual_device_viewport_intersection_rect_.height()); for (auto* layer : *this) { // Normally, the node won't be drawn in multiple layers, even it is, such as // text strokes, the visual rect don't have too much different. @@ -800,11 +800,17 @@ void LayerTreeHost::DidObserveFirstScrollDelay( first_scroll_timestamp); } +void LayerTreeHost::AddDocumentTransitionRequest( + std::unique_ptr<DocumentTransitionRequest> request) { + document_transition_requests_.push_back(std::move(request)); + SetNeedsCommit(); +} + bool LayerTreeHost::DoUpdateLayers() { TRACE_EVENT1("cc,benchmark", "LayerTreeHost::DoUpdateLayers", "source_frame_number", SourceFrameNumber()); - UpdateHudLayer(debug_state_.ShowHudInfo()); + UpdateHudLayer(debug_state_.ShouldCreateHudLayer()); // In layer lists mode, the cc property trees are built directly and do not // need to be built here. @@ -917,29 +923,6 @@ void LayerTreeHost::ApplyViewportChanges( SetNeedsUpdateLayers(); } -void LayerTreeHost::RecordManipulationTypeCounts( - const CompositorCommitData& commit_data) { - client_->RecordManipulationTypeCounts(commit_data.manipulation_info); -} - -void LayerTreeHost::SendOverscrollAndScrollEndEventsFromImplSide( - const CompositorCommitData& commit_data) { - if (commit_data.scroll_latched_element_id == ElementId()) - return; - - if (!commit_data.overscroll_delta.IsZero()) { - client_->SendOverscrollEventFromImplSide( - commit_data.overscroll_delta, commit_data.scroll_latched_element_id); - } - // TODO(bokan): If a scroll ended and a new one began in the same Blink frame - // (e.g. during a long running main thread task), this will erroneously - // dispatch the scroll end to the latter (still-scrolling) element. - // https://crbug.com/1116780. - if (commit_data.scroll_gesture_did_end) - client_->SendScrollEndEventFromImplSide( - commit_data.scroll_latched_element_id); -} - void LayerTreeHost::UpdateScrollOffsetFromImpl( const ElementId& id, const gfx::ScrollOffset& delta, @@ -1015,14 +998,12 @@ void LayerTreeHost::ApplyCompositorChanges(CompositorCommitData* commit_data) { } } - SendOverscrollAndScrollEndEventsFromImplSide(*commit_data); + client_->UpdateCompositorScrollState(*commit_data); // This needs to happen after scroll deltas have been sent to prevent top // controls from clamping the layout viewport both on the compositor and // on the main thread. ApplyViewportChanges(*commit_data); - - RecordManipulationTypeCounts(*commit_data); } void LayerTreeHost::ApplyMutatorEvents(std::unique_ptr<MutatorEvents> events) { @@ -1245,6 +1226,13 @@ void LayerTreeHost::SetViewportRectAndScale( local_surface_id_from_parent_; SetLocalSurfaceIdFromParent(local_surface_id_from_parent); + TRACE_EVENT_NESTABLE_ASYNC_END0("cc", "LayerTreeHostSize", + TRACE_ID_LOCAL(this)); + TRACE_EVENT_NESTABLE_ASYNC_BEGIN2("cc", "LayerTreeHostSize", + TRACE_ID_LOCAL(this), "size", + device_viewport_rect.ToString(), "lsid", + local_surface_id_from_parent.ToString()); + bool device_viewport_rect_changed = false; if (device_viewport_rect_ != device_viewport_rect) { device_viewport_rect_ = device_viewport_rect; @@ -1273,11 +1261,21 @@ void LayerTreeHost::SetViewportRectAndScale( } } -void LayerTreeHost::SetViewportVisibleRect(const gfx::Rect& visible_rect) { - if (visible_rect == viewport_visible_rect_) +void LayerTreeHost::SetVisualDeviceViewportIntersectionRect( + const gfx::Rect& intersection_rect) { + if (intersection_rect == visual_device_viewport_intersection_rect_) + return; + + visual_device_viewport_intersection_rect_ = intersection_rect; +} + +void LayerTreeHost::SetVisualDeviceViewportSize( + const gfx::Size& visual_device_viewport_size) { + if (visual_device_viewport_size == visual_device_viewport_size_) return; - viewport_visible_rect_ = visible_rect; + visual_device_viewport_size_ = visual_device_viewport_size; + SetNeedsCommit(); } void LayerTreeHost::SetBrowserControlsParams( @@ -1522,6 +1520,16 @@ void LayerTreeHost::UpdateHudLayer(bool show_hud_info) { root_layer_->AddChild(hud_layer_); hud_layer_->UpdateLocationAndSize(device_viewport_rect_.size(), device_scale_factor_); + if (debug_state_.show_web_vital_metrics) { + // This WebVitalMetrics is filled by the main frame, which is equivalent + // to WebPerf's numbers. The main frame's number doesn't include any + // iframes. UMA/UKM records metrics for the entire page aggregating all + // the frames. The page metrics are in the browser process. + // TODO(weiliangc): Get the page metrics for display. + auto metrics = client_->GetWebVitalMetrics(); + if (metrics && metrics->HasValue()) + hud_layer_->UpdateWebVitalMetrics(std::move(metrics)); + } } else if (hud_layer_.get()) { hud_layer_->RemoveFromParent(); hud_layer_ = nullptr; @@ -1616,6 +1624,16 @@ void LayerTreeHost::PushLayerTreePropertiesTo(LayerTreeImpl* tree_impl) { if (delegated_ink_metadata_) tree_impl->set_delegated_ink_metadata(std::move(delegated_ink_metadata_)); + + // Transfer page transition directives. + for (auto& request : document_transition_requests_) { + // Store the commit callback on LayerTreeHost, so that we can invoke them in + // CommitComplete. + committed_document_transition_callbacks_.push_back( + request->TakeCommitCallback()); + tree_impl->AddDocumentTransitionRequest(std::move(request)); + } + document_transition_requests_.clear(); } void LayerTreeHost::PushSurfaceRangesTo(LayerTreeImpl* tree_impl) { @@ -1636,6 +1654,7 @@ void LayerTreeHost::PushLayerTreeHostPropertiesTo( RecordGpuRasterizationHistogram(host_impl); host_impl->SetDebugState(debug_state_); + host_impl->SetVisualDeviceViewportSize(visual_device_viewport_size_); } Layer* LayerTreeHost::LayerByElementId(ElementId element_id) const { @@ -1794,13 +1813,11 @@ void LayerTreeHost::ElementIsAnimatingChanged( true); } -void LayerTreeHost::AnimationScalesChanged(ElementId element_id, - ElementListType list_type, - float maximum_scale, - float starting_scale) { +void LayerTreeHost::MaximumScaleChanged(ElementId element_id, + ElementListType list_type, + float maximum_scale) { DCHECK_EQ(ElementListType::ACTIVE, list_type); - property_trees()->AnimationScalesChanged(element_id, maximum_scale, - starting_scale); + property_trees()->MaximumAnimationScaleChanged(element_id, maximum_scale); } gfx::ScrollOffset LayerTreeHost::GetScrollOffsetForAnimation( @@ -1883,4 +1900,15 @@ void LayerTreeHost::SetEnableFrameRateThrottling( proxy_->SetEnableFrameRateThrottling(enable_frame_rate_throttling); } +void LayerTreeHost::SetDelegatedInkMetadata( + std::unique_ptr<viz::DelegatedInkMetadata> metadata) { + delegated_ink_metadata_ = std::move(metadata); + SetNeedsCommit(); +} + +std::vector<std::unique_ptr<DocumentTransitionRequest>> +LayerTreeHost::TakeDocumentTransitionRequestsForTesting() { + return std::move(document_transition_requests_); +} + } // namespace cc diff --git a/chromium/cc/trees/layer_tree_host.h b/chromium/cc/trees/layer_tree_host.h index e5e66fa7cc6..3447ff7c385 100644 --- a/chromium/cc/trees/layer_tree_host.h +++ b/chromium/cc/trees/layer_tree_host.h @@ -39,6 +39,7 @@ #include "cc/metrics/begin_main_frame_metrics.h" #include "cc/metrics/events_metrics_manager.h" #include "cc/metrics/frame_sequence_tracker.h" +#include "cc/metrics/web_vital_metrics.h" #include "cc/paint/node_id.h" #include "cc/trees/browser_controls_params.h" #include "cc/trees/compositor_mode.h" @@ -64,25 +65,27 @@ struct PresentationFeedback; namespace cc { -class RasterDarkModeFilter; +class DocumentTransitionRequest; class HeadsUpDisplayLayer; class Layer; class LayerTreeHostImpl; class LayerTreeHostImplClient; class LayerTreeHostSingleThreadClient; class LayerTreeMutator; -class PaintWorkletLayerPainter; class MutatorEvents; class MutatorHost; -struct PendingPageScaleAnimation; +class PaintWorkletLayerPainter; +class RasterDarkModeFilter; class RenderFrameMetadataObserver; class RenderingStatsInstrumentation; -struct OverscrollBehavior; class TaskGraphRunner; class UIResourceManager; class UkmRecorderFactory; -struct RenderingStats; + struct CompositorCommitData; +struct OverscrollBehavior; +struct PendingPageScaleAnimation; +struct RenderingStats; // Returned from LayerTreeHost::DeferMainFrameUpdate. Automatically un-defers on // destruction. @@ -402,7 +405,22 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient { float device_scale_factor, const viz::LocalSurfaceId& local_surface_id_from_parent); - void SetViewportVisibleRect(const gfx::Rect& visible_rect); + // VisualDeviceViewportIntersectionRect is the intersection of this + // compositor's viewport with the "visible size", aka this compositor's + // viewport intersection with the global viewport (i.e. + // VisualDeviceViewportSize). It is also specified in the device viewport + // coordinate space. + void SetVisualDeviceViewportIntersectionRect( + const gfx::Rect& intersection_rect); + + // VisualDeviceViewportSize is the size of the global viewport across all + // compositors that are part of the scene that this compositor contributes to + // (i.e. the visual viewport), allowing for that scene to be broken up into + // multiple compositors that each contribute to the whole (e.g. cross-origin + // iframes are isolated from each other). This is a size instead of a rect + // because each compositor doesn't know its position relative to other + // compositors. This is specified in device viewport coordinate space. + void SetVisualDeviceViewportSize(const gfx::Size&); gfx::Rect device_viewport_rect() const { return device_viewport_rect_; } @@ -662,15 +680,13 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient { ElementListType list_type, const PropertyAnimationState& mask, const PropertyAnimationState& state) override; - void AnimationScalesChanged(ElementId element_id, - ElementListType list_type, - float maximum_scale, - float starting_scale) override; + void MaximumScaleChanged(ElementId element_id, + ElementListType list_type, + float maximum_scale) override; void OnCustomPropertyMutated( - ElementId element_id, - const std::string& custom_property_name, - PaintWorkletInput::PropertyValue custom_property_value) override {} + PaintWorkletInput::PropertyKey property_key, + PaintWorkletInput::PropertyValue property_value) override {} void ScrollOffsetAnimationFinished() override {} gfx::ScrollOffset GetScrollOffsetForAnimation( @@ -711,9 +727,7 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient { } void SetDelegatedInkMetadata( - std::unique_ptr<viz::DelegatedInkMetadata> metadata) { - delegated_ink_metadata_ = std::move(metadata); - } + std::unique_ptr<viz::DelegatedInkMetadata> metadata); viz::DelegatedInkMetadata* DelegatedInkMetadataForTesting() { return delegated_ink_metadata_.get(); } @@ -721,6 +735,12 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient { void DidObserveFirstScrollDelay(base::TimeDelta first_scroll_delay, base::TimeTicks first_scroll_timestamp); + void AddDocumentTransitionRequest( + std::unique_ptr<DocumentTransitionRequest> request); + + std::vector<std::unique_ptr<DocumentTransitionRequest>> + TakeDocumentTransitionRequestsForTesting(); + protected: LayerTreeHost(InitParams params, CompositorMode mode); @@ -764,9 +784,6 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient { enum { kNumFramesToConsiderBeforeRemovingSlowPathFlag = 60 }; void ApplyViewportChanges(const CompositorCommitData& commit_data); - void RecordManipulationTypeCounts(const CompositorCommitData& commit_data); - void SendOverscrollAndScrollEndEventsFromImplSide( - const CompositorCommitData& commit_data); void ApplyPageScaleDeltaFromImplSide(float page_scale_delta); void InitializeProxy(std::unique_ptr<Proxy> proxy); @@ -862,8 +879,8 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient { LayerSelection selection_; gfx::Rect device_viewport_rect_; - - gfx::Rect viewport_visible_rect_; + gfx::Rect visual_device_viewport_intersection_rect_; + gfx::Size visual_device_viewport_size_; bool have_scroll_event_handlers_ = false; EventListenerProperties event_listener_properties_ @@ -948,6 +965,16 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient { // sticking around and potentially being reused. std::unique_ptr<viz::DelegatedInkMetadata> delegated_ink_metadata_; + // A list of document transitions that need to be transported from Blink to + // Viz, as a CompositorFrameTransitionDirective. + std::vector<std::unique_ptr<DocumentTransitionRequest>> + document_transition_requests_; + + // A list of callbacks that need to be invoked in commit callback, + // representing document transitions that have been committed to + // LayerTreeImpl. + std::vector<base::OnceClosure> committed_document_transition_callbacks_; + // Used to vend weak pointers to LayerTreeHost to ScopedDeferMainFrameUpdate // objects. base::WeakPtrFactory<LayerTreeHost> defer_main_frame_update_weak_ptr_factory_{ diff --git a/chromium/cc/trees/layer_tree_host_client.cc b/chromium/cc/trees/layer_tree_host_client.cc new file mode 100644 index 00000000000..6baef660763 --- /dev/null +++ b/chromium/cc/trees/layer_tree_host_client.cc @@ -0,0 +1,15 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/trees/layer_tree_host_client.h" + +#include "cc/metrics/web_vital_metrics.h" + +namespace cc { + +std::unique_ptr<WebVitalMetrics> LayerTreeHostClient::GetWebVitalMetrics() { + return nullptr; +} + +} // namespace cc diff --git a/chromium/cc/trees/layer_tree_host_client.h b/chromium/cc/trees/layer_tree_host_client.h index 5a804d2f009..4a8332b88ca 100644 --- a/chromium/cc/trees/layer_tree_host_client.h +++ b/chromium/cc/trees/layer_tree_host_client.h @@ -11,6 +11,7 @@ #include "base/time/time.h" #include "cc/input/browser_controls_state.h" #include "cc/metrics/frame_sequence_tracker_collection.h" +#include "cc/trees/property_tree.h" #include "ui/gfx/geometry/scroll_offset.h" #include "ui/gfx/geometry/vector2d_f.h" @@ -24,7 +25,7 @@ struct BeginFrameArgs; namespace cc { struct BeginMainFrameMetrics; -struct ElementId; +struct WebVitalMetrics; struct ApplyViewportChangesArgs { // Scroll offset delta of the inner (visual) viewport. @@ -65,6 +66,7 @@ constexpr ManipulationInfo kManipulationInfoWheel = 1 << 0; constexpr ManipulationInfo kManipulationInfoTouch = 1 << 1; constexpr ManipulationInfo kManipulationInfoPrecisionTouchPad = 1 << 2; constexpr ManipulationInfo kManipulationInfoPinchZoom = 1 << 3; +constexpr ManipulationInfo kManipulationInfoScrollbar = 1 << 4; struct PaintBenchmarkResult { double record_time_ms = 0; @@ -136,17 +138,10 @@ class LayerTreeHostClient { // related to pinch-zoom, browser controls (aka URL bar), overscroll, etc. virtual void ApplyViewportChanges(const ApplyViewportChangesArgs& args) = 0; - // Record use counts of different methods of scrolling (e.g. wheel, touch, - // precision touchpad, etc.). - virtual void RecordManipulationTypeCounts(ManipulationInfo info) = 0; - - // Notifies the client when an overscroll has happened. - virtual void SendOverscrollEventFromImplSide( - const gfx::Vector2dF& overscroll_delta, - ElementId scroll_latched_element_id) = 0; - // Notifies the client when a gesture scroll has ended. - virtual void SendScrollEndEventFromImplSide( - ElementId scroll_latched_element_id) = 0; + // Notifies the client about scroll and input related changes that occurred in + // the LayerTreeHost since the last commit. + virtual void UpdateCompositorScrollState( + const CompositorCommitData& commit_data) = 0; // Request a LayerTreeFrameSink from the client. When the client has one it // should call LayerTreeHost::SetLayerTreeFrameSink. This will result in @@ -182,6 +177,9 @@ class LayerTreeHostClient { virtual std::unique_ptr<BeginMainFrameMetrics> GetBeginMainFrameMetrics() = 0; virtual void NotifyThroughputTrackerResults(CustomTrackerResults results) = 0; + // Should only be implemented by Blink. + virtual std::unique_ptr<WebVitalMetrics> GetWebVitalMetrics() = 0; + virtual void RunPaintBenchmark(int repeat_count, PaintBenchmarkResult& result) {} diff --git a/chromium/cc/trees/layer_tree_host_impl.cc b/chromium/cc/trees/layer_tree_host_impl.cc index 9efdf67d48c..2c3b6b3a2a8 100644 --- a/chromium/cc/trees/layer_tree_host_impl.cc +++ b/chromium/cc/trees/layer_tree_host_impl.cc @@ -38,6 +38,7 @@ #include "cc/base/switches.h" #include "cc/benchmarks/benchmark_instrumentation.h" #include "cc/debug/rendering_stats_instrumentation.h" +#include "cc/document_transition/document_transition_request.h" #include "cc/input/browser_controls_offset_manager.h" #include "cc/input/main_thread_scrolling_reason.h" #include "cc/input/page_scale_animation.h" @@ -202,7 +203,7 @@ void DidVisibilityChange(LayerTreeHostImpl* id, bool visible) { if (visible) { TRACE_EVENT_NESTABLE_ASYNC_BEGIN1( "cc,benchmark", "LayerTreeHostImpl::SetVisible", TRACE_ID_LOCAL(id), - "LayerTreeHostImpl", id); + "LayerTreeHostImpl", static_cast<void*>(id)); return; } @@ -356,6 +357,12 @@ const LayerTreeHostImpl& LayerTreeHostImpl::GetImplDeprecated() const { return *this; } +bool LayerTreeHostImpl::CanInjectJankOnMain() const { + return !!frame_trackers_.FrameSequenceTrackerActiveTypes() && + compositor_frame_reporting_controller_ + ->is_main_thread_driving_smoothness(); +} + LayerTreeHostImpl::FrameData::FrameData() = default; LayerTreeHostImpl::FrameData::~FrameData() = default; LayerTreeHostImpl::UIResourceData::UIResourceData() = default; @@ -745,16 +752,13 @@ PaintWorkletJobMap LayerTreeHostImpl::GatherDirtyPaintWorklets( PaintWorkletJob::AnimatedPropertyValues animated_property_values; for (const auto& element : input->GetPropertyKeys()) { - // We should not have multiple property ids with the same name. - DCHECK(!animated_property_values.contains(element.first)); + DCHECK(!animated_property_values.contains(element)); const PaintWorkletInput::PropertyValue& animated_property_value = paint_worklet_tracker_.GetPropertyAnimationValue(element); // No value indicates that the input property was not mutated by CC // animation. - if (animated_property_value.has_value()) { - animated_property_values.emplace(element.first, - animated_property_value); - } + if (animated_property_value.has_value()) + animated_property_values.emplace(element, animated_property_value); } job_vector->data.emplace_back(layer->id(), input, @@ -1126,7 +1130,7 @@ bool LayerTreeHostImpl::HasDamage() const { return root_surface_has_visible_damage || active_tree_->property_trees()->effect_tree.HasCopyRequests() || - hud_wants_to_draw_; + hud_wants_to_draw_ || active_tree_->HasDocumentTransitionRequests(); } DrawResult LayerTreeHostImpl::CalculateRenderPasses(FrameData* frame) { @@ -1173,8 +1177,7 @@ DrawResult LayerTreeHostImpl::CalculateRenderPasses(FrameData* frame) { render_surface->EffectTreeIndex() == EffectTree::kContentsRootNodeId; bool should_draw_into_render_pass = is_root_surface || render_surface->contributes_to_drawn_surface() || - render_surface->HasCopyRequest() || - render_surface->ShouldCacheRenderSurface(); + render_surface->CopyOfOutputRequired(); if (should_draw_into_render_pass) frame->render_passes.push_back(render_surface->CreateRenderPass()); } @@ -1227,6 +1230,8 @@ DrawResult LayerTreeHostImpl::CalculateRenderPasses(FrameData* frame) { // Advance our de-jelly state. This is a no-op if de-jelly is not active. de_jelly_state_.AdvanceFrame(active_tree_.get()); + if (settings_.enable_compositing_based_throttling) + throttle_decider_.Prepare(); for (EffectTreeLayerListIterator it(active_tree()); it.state() != EffectTreeLayerListIterator::State::END; ++it) { auto target_render_pass_id = it.target_render_surface()->render_pass_id(); @@ -1244,6 +1249,8 @@ DrawResult LayerTreeHostImpl::CalculateRenderPasses(FrameData* frame) { render_surface->EffectTreeIndex(), &target_render_pass->copy_requests); } + if (settings_.enable_compositing_based_throttling && target_render_pass) + throttle_decider_.ProcessRenderPass(*target_render_pass); } else if (it.state() == EffectTreeLayerListIterator::State::CONTRIBUTING_SURFACE) { RenderSurfaceImpl* render_surface = it.current_render_surface(); @@ -1601,7 +1608,8 @@ void LayerTreeHostImpl::RemoveRenderPasses(FrameData* frame) { } if (pass->quad_list.empty() && pass->copy_requests.empty() && - pass->filters.IsEmpty() && pass->backdrop_filters.IsEmpty()) { + !pass->subtree_capture_id.is_valid() && pass->filters.IsEmpty() && + pass->backdrop_filters.IsEmpty()) { // Remove the pass and decrement |i| to counter the for loop's increment, // so we don't skip the next pass in the loop. frame->render_passes.erase(frame->render_passes.begin() + i); @@ -2023,22 +2031,13 @@ void LayerTreeHostImpl::DidReceiveCompositorFrameAck() { void LayerTreeHostImpl::DidPresentCompositorFrame( uint32_t frame_token, const viz::FrameTimingDetails& details) { - frame_trackers_.NotifyFramePresented(frame_token, - details.presentation_feedback); - PresentationTimeCallbackBuffer::PendingCallbacks activated = + PresentationTimeCallbackBuffer::PendingCallbacks activated_callbacks = presentation_time_callbacks_.PopPendingCallbacks(frame_token); - // The callbacks in |compositor_thread_callbacks| expect to be run on the - // compositor thread so we'll run them now. - for (LayerTreeHost::PresentationTimeCallback& callback : - activated.compositor_thread_callbacks) { - std::move(callback).Run(details.presentation_feedback); - } - - // Send all the main-thread callbacks to the client in one batch. The client - // is in charge of posting them to the main thread. + // Send all tasks to the client so that it can decide which tasks + // should run on which thread. client_->DidPresentCompositorFrameOnImplThread( - frame_token, std::move(activated.main_thread_callbacks), details); + frame_token, std::move(activated_callbacks), details); // Send all pending lag events waiting on the frame pointed by |frame_token|. // It is posted as a task because LayerTreeHostImpl::DidPresentCompositorFrame @@ -2221,6 +2220,9 @@ viz::CompositorFrameMetadata LayerTreeHostImpl::MakeCompositorFrameMetadata() { metadata.delegated_ink_metadata = std::move(delegated_ink_metadata); } + for (auto& request : active_tree_->TakeDocumentTransitionRequests()) + metadata.transition_directives.push_back(request->ConstructDirective()); + return metadata; } @@ -2416,6 +2418,12 @@ bool LayerTreeHostImpl::DrawLayers(FrameData* frame) { devtools_instrumentation::DidDrawFrame(id_); benchmark_instrumentation::IssueImplThreadRenderingStatsEvent( rendering_stats_instrumentation_->TakeImplThreadRenderingStats()); + + if (settings_.enable_compositing_based_throttling && + throttle_decider_.HasThrottlingChanged()) { + client_->FrameSinksToThrottleUpdated(throttle_decider_.ids()); + } + return true; } @@ -2429,7 +2437,7 @@ viz::CompositorFrame LayerTreeHostImpl::GenerateCompositorFrame( memory_history_->SaveEntry(tile_manager_.memory_stats_from_last_assign()); - if (debug_state_.ShowHudRects()) { + if (debug_state_.ShowDebugRects()) { debug_rect_history_->SaveDebugRectsForCurrentFrame( active_tree(), active_tree_->hud_layer(), *frame->render_surface_list, debug_state_); @@ -2482,12 +2490,30 @@ viz::CompositorFrame LayerTreeHostImpl::GenerateCompositorFrame( frame->deadline_in_frames.value_or(0u), CurrentBeginFrameArgs().interval, frame->use_default_lower_bound_deadline); - frame_rate_estimator_.WillDraw(CurrentBeginFrameArgs().frame_time); - - if (settings_.force_preferred_interval_for_video || - enable_frame_rate_throttling_) { + constexpr auto kFudgeDelta = base::TimeDelta::FromMilliseconds(1); + constexpr auto kTwiceOfDefaultInterval = + viz::BeginFrameArgs::DefaultInterval() * 2; + constexpr auto kMinDelta = kTwiceOfDefaultInterval - kFudgeDelta; + if (enable_frame_rate_throttling_) { metadata.preferred_frame_interval = viz::BeginFrameArgs::MaxInterval(); + } else if (mutator_host_->MainThreadAnimationsCount() == 0 && + mutator_host_->MinimumTickInterval() > kMinDelta) { + // All animations are impl-thread animations that tick at no more than + // half the default display compositing fps. + // Here and below with FrameRateEstimator::GetPreferredInterval(), the + // meta data's preferred_frame_interval is constrainted to either 0 or + // twice the default interval. The reason is because GPU process side + // viz::FrameRateDecider is optimized for when all the preferred frame + // rates are similar. + // In general it may cause an animation to be less smooth if its fps is + // less than 30 fps and it updates at 30 fps. However, the frame rate + // reduction optimization is only applied when a webpage has two or more + // videos, i.e., very likely a video conferencing scene. It doesn't apply + // to general webpages. + metadata.preferred_frame_interval = kTwiceOfDefaultInterval; } else { + // There are main-thread or high frequency impl-thread animations. + frame_rate_estimator_.WillDraw(CurrentBeginFrameArgs().frame_time); metadata.preferred_frame_interval = frame_rate_estimator_.GetPreferredInterval(); } @@ -2499,8 +2525,11 @@ viz::CompositorFrame LayerTreeHostImpl::GenerateCompositorFrame( if (render_frame_metadata_observer_) { last_draw_render_frame_metadata_ = MakeRenderFrameMetadata(frame); - last_draw_render_frame_metadata_->has_delegated_ink_metadata = - metadata.delegated_ink_metadata.get(); + if (viz::DelegatedInkMetadata* ink_metadata = + metadata.delegated_ink_metadata.get()) { + last_draw_render_frame_metadata_->delegated_ink_metadata = + DelegatedInkBrowserMetadata(ink_metadata->is_hovering()); + } // We cache the value of any new vertical scroll direction so that we can // accurately determine when the next change in vertical scroll direction @@ -2772,6 +2801,9 @@ bool LayerTreeHostImpl::WillBeginImplFrame(const viz::BeginFrameArgs& args) { frame_trackers_.NotifyBeginImplFrame(args); total_frame_counter_.OnBeginFrame(args); + UMA_HISTOGRAM_CUSTOM_COUNTS("GPU.AcceleratedSurfaceRefreshRate", + 1 / args.interval.InSecondsF(), 0, 121, 122); + if (is_likely_to_require_a_draw_) { // Optimistically schedule a draw. This will let us expect the tile manager // to complete its work so that we can draw new tiles within the impl frame @@ -3002,7 +3034,7 @@ void LayerTreeHostImpl::DidLoseLayerTreeFrameSink() { client_->DidLoseLayerTreeFrameSinkOnImplThread(); lag_tracking_manager_.Clear(); - dropped_frame_counter_.ResetFrameSorter(); + dropped_frame_counter_.ResetPendingFrames(base::TimeTicks::Now()); } bool LayerTreeHostImpl::OnlyExpandTopControlsAtPageTop() const { @@ -3246,6 +3278,13 @@ void LayerTreeHostImpl::OnMemoryPressure( if (level != base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL) return; + // TODO(crbug.com/1189208): Unlocking decoded-image-tracker images causes + // flickering in visible trees if Out-Of-Process rasterization is enabled. +#if defined(OS_FUCHSIA) + if (use_oop_rasterization() && visible()) + return; +#endif // defined(OS_FUCHSIA) + ReleaseTileResources(); active_tree_->OnPurgeMemory(); if (pending_tree_) @@ -3260,6 +3299,7 @@ void LayerTreeHostImpl::OnMemoryPressure( } if (resource_pool_) resource_pool_->OnMemoryPressure(level); + tile_manager_.decoded_image_tracker().UnlockAllImages(); } @@ -3269,10 +3309,14 @@ void LayerTreeHostImpl::SetVisible(bool visible) { if (visible_ == visible) return; visible_ = visible; - if (visible_) - total_frame_counter_.OnShow(base::TimeTicks::Now()); - else - total_frame_counter_.OnHide(base::TimeTicks::Now()); + if (visible_) { + auto now = base::TimeTicks::Now(); + total_frame_counter_.OnShow(now); + } else { + auto now = base::TimeTicks::Now(); + total_frame_counter_.OnHide(now); + dropped_frame_counter_.ResetPendingFrames(now); + } DidVisibilityChange(this, visible_); UpdateTileManagerMemoryPolicy(ActualManagedMemoryPolicy()); @@ -3358,8 +3402,6 @@ void LayerTreeHostImpl::RecreateTileResources() { } void LayerTreeHostImpl::CreateTileManagerResources() { - raster_buffer_provider_ = CreateRasterBufferProvider(); - viz::ResourceFormat tile_format = TileRasterBufferFormat( settings_, layer_tree_frame_sink_->context_provider(), use_gpu_rasterization_); @@ -3372,6 +3414,11 @@ void LayerTreeHostImpl::CreateTileManagerResources() { tile_format), settings_.decoded_image_working_set_budget_bytes, max_texture_size_, paint_image_generator_client_id_, dark_mode_filter_); + + pending_raster_queries_ = std::make_unique<RasterQueryQueue>( + layer_tree_frame_sink_->worker_context_provider(), + can_use_oop_rasterization_); + } else { bool gpu_compositing = !!layer_tree_frame_sink_->context_provider(); image_decode_cache_ = std::make_unique<SoftwareImageDecodeCache>( @@ -3380,6 +3427,8 @@ void LayerTreeHostImpl::CreateTileManagerResources() { paint_image_generator_client_id_); } + raster_buffer_provider_ = CreateRasterBufferProvider(); + // Pass the single-threaded synchronous task graph runner to the worker pool // if we're in synchronous single-threaded mode. TaskGraphRunner* task_graph_runner = task_graph_runner_; @@ -3392,7 +3441,8 @@ void LayerTreeHostImpl::CreateTileManagerResources() { tile_manager_.SetResources(resource_pool_.get(), image_decode_cache_.get(), task_graph_runner, raster_buffer_provider_.get(), - use_gpu_rasterization_, use_oop_rasterization()); + use_gpu_rasterization_, use_oop_rasterization(), + pending_raster_queries_.get()); tile_manager_.SetCheckerImagingForceDisabled( settings_.only_checker_images_with_gpu_raster && !use_gpu_rasterization_); UpdateTileManagerMemoryPolicy(ActualManagedMemoryPolicy()); @@ -3423,7 +3473,7 @@ LayerTreeHostImpl::CreateRasterBufferProvider() { settings_.resource_settings.use_gpu_memory_buffer_resources, tile_format, settings_.max_gpu_raster_tile_size, settings_.unpremultiply_and_dither_low_bit_depth_tiles, - can_use_oop_rasterization_); + can_use_oop_rasterization_, pending_raster_queries_.get()); } bool use_zero_copy = settings_.use_zero_copy; @@ -3520,6 +3570,7 @@ void LayerTreeHostImpl::CleanUpTileManagerResources() { single_thread_synchronous_task_graph_runner_ = nullptr; image_decode_cache_ = nullptr; raster_buffer_provider_ = nullptr; + pending_raster_queries_ = nullptr; // Any resources that were allocated previously should be considered not good // for reuse, as the RasterBufferProvider will be replaced and it may choose // to allocate future resources differently. @@ -3875,6 +3926,15 @@ void LayerTreeHostImpl::BindToInputHandler( input_delegate_ = std::move(delegate); } +void LayerTreeHostImpl::SetVisualDeviceViewportSize( + const gfx::Size& visual_device_viewport_size) { + visual_device_viewport_size_ = visual_device_viewport_size; +} + +gfx::Size LayerTreeHostImpl::VisualDeviceViewportSize() const { + return visual_device_viewport_size_; +} + ScrollTree& LayerTreeHostImpl::GetScrollTree() const { return active_tree_->property_trees()->scroll_tree; } @@ -3903,6 +3963,8 @@ LayerTreeHostImpl::ProcessCompositorDeltas() { commit_data->page_scale_delta = active_tree_->page_scale_factor()->PullDeltaForMainThread(); commit_data->is_pinch_gesture_active = active_tree_->PinchGestureActive(); + commit_data->is_scroll_active = + input_delegate_ && GetInputHandler().IsCurrentlyScrolling(); // We should never process non-unit page_scale_delta for an OOPIF subframe. // TODO(wjmaclean): Remove this DCHECK as a pre-condition to closing the bug. // https://crbug.com/845097 @@ -4396,7 +4458,7 @@ void LayerTreeHostImpl::CreateUIResource(UIResourceId uid, // fully filled by drawBitmap(), so we ensure they start empty. (See // crbug.com/642011 for an example.) scaled_canvas->clear(SK_ColorTRANSPARENT); - scaled_canvas->drawBitmap(source_bitmap, 0, 0); + scaled_canvas->drawImage(source_bitmap.asImage(), 0, 0); if (layer_tree_frame_sink_->context_provider()) { SkPixmap pixmap; @@ -4641,11 +4703,10 @@ void LayerTreeHostImpl::SetElementFilterMutated( } void LayerTreeHostImpl::OnCustomPropertyMutated( - ElementId element_id, - const std::string& custom_property_name, - PaintWorkletInput::PropertyValue custom_property_value) { - paint_worklet_tracker_.OnCustomPropertyMutated( - element_id, custom_property_name, std::move(custom_property_value)); + PaintWorkletInput::PropertyKey property_key, + PaintWorkletInput::PropertyValue property_value) { + paint_worklet_tracker_.OnCustomPropertyMutated(std::move(property_key), + std::move(property_value)); } void LayerTreeHostImpl::SetElementBackdropFilterMutated( @@ -4716,15 +4777,14 @@ void LayerTreeHostImpl::ElementIsAnimatingChanged( tree->set_needs_update_draw_properties(); } -void LayerTreeHostImpl::AnimationScalesChanged(ElementId element_id, - ElementListType list_type, - float maximum_scale, - float starting_scale) { +void LayerTreeHostImpl::MaximumScaleChanged(ElementId element_id, + ElementListType list_type, + float maximum_scale) { if (LayerTreeImpl* tree = list_type == ElementListType::ACTIVE ? active_tree() : pending_tree()) { - tree->property_trees()->AnimationScalesChanged(element_id, maximum_scale, - starting_scale); + tree->property_trees()->MaximumAnimationScaleChanged(element_id, + maximum_scale); } } @@ -4758,11 +4818,6 @@ gfx::ScrollOffset LayerTreeHostImpl::GetScrollOffsetForAnimation( return gfx::ScrollOffset(); } -bool LayerTreeHostImpl::SupportsImplScrolling() const { - // Supported in threaded mode. - return task_runner_provider_->HasImplThread(); -} - bool LayerTreeHostImpl::CommitToActiveTree() const { return settings_.commit_to_active_tree; } @@ -4846,6 +4901,20 @@ void LayerTreeHostImpl::SetUkmSmoothnessDestination( ukm_smoothness_mapping_.GetMemoryAs<UkmSmoothnessDataShared>()); } +void LayerTreeHostImpl::NotifyDidPresentCompositorFrameOnImplThread( + uint32_t frame_token, + PresentationTimeCallbackBuffer::PendingCallbacks callbacks, + const viz::FrameTimingDetails& details) { + frame_trackers_.NotifyFramePresented(frame_token, + details.presentation_feedback); + // The callbacks in |compositor_thread_callbacks| expect to be run on the + // compositor thread so we'll run them now. + for (LayerTreeHost::PresentationTimeCallback& callback : + callbacks.compositor_thread_callbacks) { + std::move(callback).Run(details.presentation_feedback); + } +} + void LayerTreeHostImpl::AllocateLocalSurfaceId() { child_local_surface_id_allocator_.GenerateId(); } diff --git a/chromium/cc/trees/layer_tree_host_impl.h b/chromium/cc/trees/layer_tree_host_impl.h index eee1d46bc15..d62e352010b 100644 --- a/chromium/cc/trees/layer_tree_host_impl.h +++ b/chromium/cc/trees/layer_tree_host_impl.h @@ -41,6 +41,7 @@ #include "cc/metrics/total_frame_counter.h" #include "cc/paint/discardable_image_map.h" #include "cc/paint/paint_worklet_job.h" +#include "cc/raster/raster_query_queue.h" #include "cc/resources/ui_resource_client.h" #include "cc/scheduler/begin_frame_tracker.h" #include "cc/scheduler/commit_earlyout_reason.h" @@ -62,6 +63,7 @@ #include "cc/trees/presentation_time_callback_buffer.h" #include "cc/trees/render_frame_metadata.h" #include "cc/trees/task_runner_provider.h" +#include "cc/trees/throttle_decider.h" #include "cc/trees/ukm_manager.h" #include "components/viz/client/client_resource_provider.h" #include "components/viz/common/frame_sinks/begin_frame_args.h" @@ -161,10 +163,11 @@ class LayerTreeHostImplClient { virtual void RequestBeginMainFrameNotExpected(bool new_state) = 0; // Called when a presentation time is requested. |frame_token| identifies - // the frame that was presented. + // the frame that was presented. |callbacks| holds both impl side and main + // side callbacks to be called. virtual void DidPresentCompositorFrameOnImplThread( uint32_t frame_token, - std::vector<LayerTreeHost::PresentationTimeCallback> callbacks, + PresentationTimeCallbackBuffer::PendingCallbacks callbacks, const viz::FrameTimingDetails& details) = 0; // Returns whether the main-thread is expected to receive a BeginMainFrame. @@ -188,6 +191,9 @@ class LayerTreeHostImplClient { // code as a result. virtual bool IsInSynchronousComposite() const = 0; + virtual void FrameSinksToThrottleUpdated( + const base::flat_set<viz::FrameSinkId>& ids) = 0; + protected: virtual ~LayerTreeHostImplClient() = default; }; @@ -372,12 +378,23 @@ class CC_EXPORT LayerTreeHostImpl : public TileManagerClient, void DidScrollContent(ElementId element_id, bool animated) override; float DeviceScaleFactor() const override; float PageScaleFactor() const override; + gfx::Size VisualDeviceViewportSize() const override; const LayerTreeSettings& GetSettings() const override; LayerTreeHostImpl& GetImplDeprecated() override; const LayerTreeHostImpl& GetImplDeprecated() const override; + bool CanInjectJankOnMain() const; FrameSequenceTrackerCollection& frame_trackers() { return frame_trackers_; } + // VisualDeviceViewportSize is the size of the global viewport across all + // compositors that are part of the scene that this compositor contributes to + // (i.e. the visual viewport), allowing for that scene to be broken up into + // multiple compositors that each contribute to the whole (e.g. cross-origin + // iframes are isolated from each other). This is a size instead of a rect + // because each compositor doesn't know its position relative to other + // compositors. This is specified in device viewport coordinate space. + void SetVisualDeviceViewportSize(const gfx::Size&); + // Updates registered ElementIds present in |changed_list|. Call this after // changing the property trees for the |changed_list| trees. void UpdateElements(ElementListType changed_list); @@ -422,14 +439,12 @@ class CC_EXPORT LayerTreeHostImpl : public TileManagerClient, ElementListType list_type, const PropertyAnimationState& mask, const PropertyAnimationState& state) override; - void AnimationScalesChanged(ElementId element_id, - ElementListType list_type, - float maximum_scale, - float starting_scale) override; + void MaximumScaleChanged(ElementId element_id, + ElementListType list_type, + float maximum_scale) override; void OnCustomPropertyMutated( - ElementId element_id, - const std::string& custom_property_name, - PaintWorkletInput::PropertyValue custom_property_value) override; + PaintWorkletInput::PropertyKey property_key, + PaintWorkletInput::PropertyValue property_value) override; void ScrollOffsetAnimationFinished() override; gfx::ScrollOffset GetScrollOffsetForAnimation( @@ -730,7 +745,6 @@ class CC_EXPORT LayerTreeHostImpl : public TileManagerClient, // Only valid for synchronous (non-scheduled) single-threaded case. void SynchronouslyInitializeAllTiles(); - bool SupportsImplScrolling() const; bool CommitToActiveTree() const; // Virtual so tests can inject their own. @@ -790,6 +804,13 @@ class CC_EXPORT LayerTreeHostImpl : public TileManagerClient, void SetUkmSmoothnessDestination( base::WritableSharedMemoryMapping ukm_smoothness_data); + // Notifies FrameTrackers, impl side callbacks that the compsitor frame + // was presented. + void NotifyDidPresentCompositorFrameOnImplThread( + uint32_t frame_token, + PresentationTimeCallbackBuffer::PendingCallbacks callbacks, + const viz::FrameTimingDetails& details); + CompositorFrameReportingController* compositor_frame_reporting_controller() const { return compositor_frame_reporting_controller_.get(); @@ -818,6 +839,10 @@ class CC_EXPORT LayerTreeHostImpl : public TileManagerClient, return client_->IsInSynchronousComposite(); } + RasterQueryQueue* GetRasterQueryQueueForTesting() const { + return pending_raster_queries_.get(); + } + protected: LayerTreeHostImpl( const LayerTreeSettings& settings, @@ -1012,6 +1037,7 @@ class CC_EXPORT LayerTreeHostImpl : public TileManagerClient, GpuRasterizationStatus::OFF_DEVICE; std::unique_ptr<RasterBufferProvider> raster_buffer_provider_; std::unique_ptr<ResourcePool> resource_pool_; + std::unique_ptr<RasterQueryQueue> pending_raster_queries_; std::unique_ptr<ImageDecodeCache> image_decode_cache_; GlobalStateThatImpactsTilePriority global_tile_state_; @@ -1111,6 +1137,8 @@ class CC_EXPORT LayerTreeHostImpl : public TileManagerClient, std::unique_ptr<Viewport> viewport_; + gfx::Size visual_device_viewport_size_; + std::unique_ptr<PendingTreeRasterDurationHistogramTimer> pending_tree_raster_duration_timer_; @@ -1207,6 +1235,10 @@ class CC_EXPORT LayerTreeHostImpl : public TileManagerClient, // mutable because |contains_srgb_cache_| is accessed in a const method. mutable base::MRUCache<gfx::ColorSpace, bool> contains_srgb_cache_; + // When enabled, calculates which frame sinks can be throttled based on + // some pre-defined criteria. + ThrottleDecider throttle_decider_; + // Must be the last member to ensure this is destroyed first in the // destruction order and invalidates all weak pointers. base::WeakPtrFactory<LayerTreeHostImpl> weak_factory_{this}; diff --git a/chromium/cc/trees/layer_tree_host_impl_unittest.cc b/chromium/cc/trees/layer_tree_host_impl_unittest.cc index a9c0a3e8e39..3c78b9ea2eb 100644 --- a/chromium/cc/trees/layer_tree_host_impl_unittest.cc +++ b/chromium/cc/trees/layer_tree_host_impl_unittest.cc @@ -26,9 +26,9 @@ #include "build/build_config.h" #include "cc/animation/animation_host.h" #include "cc/animation/animation_id_provider.h" -#include "cc/animation/transform_operations.h" #include "cc/base/features.h" #include "cc/base/histograms.h" +#include "cc/document_transition/document_transition_request.h" #include "cc/input/browser_controls_offset_manager.h" #include "cc/input/main_thread_scrolling_reason.h" #include "cc/input/page_scale_animation.h" @@ -100,6 +100,7 @@ #include "ui/gfx/geometry/rect_conversions.h" #include "ui/gfx/geometry/size_conversions.h" #include "ui/gfx/geometry/vector2d_conversions.h" +#include "ui/gfx/transform_operations.h" #define EXPECT_SCOPED(statements) \ { \ @@ -260,8 +261,12 @@ class LayerTreeHostImplTest : public testing::Test, void RequestBeginMainFrameNotExpected(bool new_state) override {} void DidPresentCompositorFrameOnImplThread( uint32_t frame_token, - std::vector<LayerTreeHost::PresentationTimeCallback> callbacks, - const viz::FrameTimingDetails& details) override {} + PresentationTimeCallbackBuffer::PendingCallbacks activated, + const viz::FrameTimingDetails& details) override { + std::move(activated.main_thread_callbacks); + host_impl_->NotifyDidPresentCompositorFrameOnImplThread( + frame_token, std::move(activated), details); + } void NotifyAnimationWorkletStateChange(AnimationWorkletMutationState state, ElementListType tree_type) override {} void NotifyPaintWorkletStateChange( @@ -274,6 +279,8 @@ class LayerTreeHostImplTest : public testing::Test, first_scroll_observed++; } bool IsInSynchronousComposite() const override { return false; } + void FrameSinksToThrottleUpdated( + const base::flat_set<viz::FrameSinkId>& ids) override {} void set_reduce_memory_result(bool reduce_memory_result) { reduce_memory_result_ = reduce_memory_result; } @@ -423,6 +430,7 @@ class LayerTreeHostImplTest : public testing::Test, auto* root = SetupRootLayer<LayerImpl>(layer_tree_impl, inner_viewport_size); SetupViewport(root, outer_viewport_size, content_size); + host_impl_->SetVisualDeviceViewportSize(inner_viewport_size); UpdateDrawProperties(layer_tree_impl); layer_tree_impl->DidBecomeActive(); @@ -518,7 +526,7 @@ class LayerTreeHostImplTest : public testing::Test, ASSERT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, status.thread); ASSERT_TRUE(status.needs_main_thread_hit_test); } else { - ASSERT_EQ(ScrollThread::SCROLL_UNKNOWN, status.thread); + ASSERT_EQ(ScrollThread::SCROLL_ON_MAIN_THREAD, status.thread); ASSERT_EQ(MainThreadScrollingReason::kFailedHitTest, status.main_thread_scrolling_reasons); } @@ -533,7 +541,7 @@ class LayerTreeHostImplTest : public testing::Test, ASSERT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, status.thread); ASSERT_TRUE(status.needs_main_thread_hit_test); } else { - ASSERT_EQ(ScrollThread::SCROLL_UNKNOWN, status.thread); + ASSERT_EQ(ScrollThread::SCROLL_ON_MAIN_THREAD, status.thread); ASSERT_EQ(MainThreadScrollingReason::kFailedHitTest, status.main_thread_scrolling_reasons); } @@ -1635,7 +1643,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, status.main_thread_scrolling_reasons); EXPECT_TRUE(status.needs_main_thread_hit_test); } else { - EXPECT_EQ(ScrollThread::SCROLL_UNKNOWN, status.thread); + EXPECT_EQ(ScrollThread::SCROLL_ON_MAIN_THREAD, status.thread); EXPECT_EQ(MainThreadScrollingReason::kFailedHitTest, status.main_thread_scrolling_reasons); } @@ -1665,9 +1673,13 @@ class LayerTreeHostImplTestInvokeMainThreadCallbacks public: void DidPresentCompositorFrameOnImplThread( uint32_t frame_token, - std::vector<LayerTreeHost::PresentationTimeCallback> callbacks, + PresentationTimeCallbackBuffer::PendingCallbacks activated, const viz::FrameTimingDetails& details) override { - for (LayerTreeHost::PresentationTimeCallback& callback : callbacks) { + auto main_thread_callbacks = std::move(activated.main_thread_callbacks); + host_impl_->NotifyDidPresentCompositorFrameOnImplThread( + frame_token, std::move(activated), details); + for (LayerTreeHost::PresentationTimeCallback& callback : + main_thread_callbacks) { std::move(callback).Run(details.presentation_feedback); } } @@ -2025,7 +2037,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, status.main_thread_scrolling_reasons); ASSERT_TRUE(status.needs_main_thread_hit_test); } else { - ASSERT_EQ(ScrollThread::SCROLL_UNKNOWN, status.thread); + ASSERT_EQ(ScrollThread::SCROLL_ON_MAIN_THREAD, status.thread); ASSERT_EQ(MainThreadScrollingReason::kFailedHitTest, status.main_thread_scrolling_reasons); } @@ -2081,7 +2093,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, status.main_thread_scrolling_reasons); EXPECT_TRUE(status.needs_main_thread_hit_test); } else { - EXPECT_EQ(ScrollThread::SCROLL_UNKNOWN, status.thread); + EXPECT_EQ(ScrollThread::SCROLL_ON_MAIN_THREAD, status.thread); EXPECT_EQ(MainThreadScrollingReason::kFailedHitTest, status.main_thread_scrolling_reasons); } @@ -2290,7 +2302,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, FixedLayerOverNonFixedLayer) { status.main_thread_scrolling_reasons); EXPECT_TRUE(status.needs_main_thread_hit_test); } else { - EXPECT_EQ(ScrollThread::SCROLL_UNKNOWN, status.thread); + EXPECT_EQ(ScrollThread::SCROLL_ON_MAIN_THREAD, status.thread); EXPECT_EQ(MainThreadScrollingReason::kFailedHitTest, status.main_thread_scrolling_reasons); } @@ -2680,8 +2692,12 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, SnapAnimationTargetUpdated) { AnimatedUpdateState(gfx::Point(10, 10), gfx::Vector2dF(0, -10)).get()); EXPECT_FALSE(GetInputHandler().animating_for_snap_for_testing()); // Finish the smooth scroll animation for wheel. + const int scroll_animation_duration_ms = + base::FeatureList::IsEnabled(features::kImpulseScrollAnimations) ? 300 + : 150; BeginImplFrameAndAnimate(begin_frame_args, - start_time + base::TimeDelta::FromMilliseconds(150)); + start_time + base::TimeDelta::FromMilliseconds( + scroll_animation_duration_ms)); // At the end of the previous scroll animation, a new animation for the // snapping should have started. @@ -3260,9 +3276,9 @@ TEST_F(CommitToPendingTreeLayerTreeHostImplTest, CreateTransformNode(child); // Add a translate from 6,7 to 8,9. - TransformOperations start; + gfx::TransformOperations start; start.AppendTranslate(6, 7, 0); - TransformOperations end; + gfx::TransformOperations end; end.AppendTranslate(8, 9, 0); AddAnimatedTransformToElementWithAnimation(child->element_id(), timeline(), 4.0, start, end); @@ -3355,9 +3371,9 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, CreateTransformNode(child); // Add a translate animation. - TransformOperations start; + gfx::TransformOperations start; start.AppendTranslate(6, 7, 0); - TransformOperations end; + gfx::TransformOperations end; end.AppendTranslate(8, 9, 0); AddAnimatedTransformToElementWithAnimation(child->element_id(), timeline(), 4.0, start, end); @@ -11435,14 +11451,14 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, UIResourceManagement) { host_impl_->CreateUIResource(ui_resource_id, bitmap); EXPECT_EQ(1u, sii->shared_image_count()); viz::ResourceId id1 = host_impl_->ResourceIdForUIResource(ui_resource_id); - EXPECT_NE(0u, id1); + EXPECT_NE(viz::kInvalidResourceId, id1); // Multiple requests with the same id is allowed. The previous texture is // deleted. host_impl_->CreateUIResource(ui_resource_id, bitmap); EXPECT_EQ(1u, sii->shared_image_count()); viz::ResourceId id2 = host_impl_->ResourceIdForUIResource(ui_resource_id); - EXPECT_NE(0u, id2); + EXPECT_NE(viz::kInvalidResourceId, id2); EXPECT_NE(id1, id2); // Deleting invalid UIResourceId is allowed and does not change state. @@ -11451,11 +11467,12 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, UIResourceManagement) { // Should return zero for invalid UIResourceId. Number of textures should // not change. - EXPECT_EQ(0u, host_impl_->ResourceIdForUIResource(-1)); + EXPECT_EQ(viz::kInvalidResourceId, host_impl_->ResourceIdForUIResource(-1)); EXPECT_EQ(1u, sii->shared_image_count()); host_impl_->DeleteUIResource(ui_resource_id); - EXPECT_EQ(0u, host_impl_->ResourceIdForUIResource(ui_resource_id)); + EXPECT_EQ(viz::kInvalidResourceId, + host_impl_->ResourceIdForUIResource(ui_resource_id)); EXPECT_EQ(0u, sii->shared_image_count()); // Should not change state for multiple deletion on one UIResourceId @@ -11485,7 +11502,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, CreateETC1UIResource) { host_impl_->CreateUIResource(ui_resource_id, bitmap); EXPECT_EQ(1u, sii->shared_image_count()); viz::ResourceId id1 = host_impl_->ResourceIdForUIResource(ui_resource_id); - EXPECT_NE(0u, id1); + EXPECT_NE(viz::kInvalidResourceId, id1); } TEST_P(ScrollUnifiedLayerTreeHostImplTest, ObeyMSAACaps) { @@ -11651,7 +11668,7 @@ TEST_P(LayerTreeHostImplTestWithRenderer, ShutdownReleasesContext) { // LayerTreeHostImpl::IsInitialScrollHitTestReliable for details. TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollHitTestIsNotReliable) { // If we ray cast a scroller that is not on the first layer's ancestor chain, - // we should return ScrollThread::SCROLL_UNKNOWN. + // we should return ScrollThread::SCROLL_ON_MAIN_THREAD. gfx::Size viewport_size(50, 50); gfx::Size content_size(100, 100); SetupViewportLayersOuterScrolls(viewport_size, content_size); @@ -11677,7 +11694,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollHitTestIsNotReliable) { EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, status.thread); EXPECT_TRUE(status.needs_main_thread_hit_test); } else { - EXPECT_EQ(ScrollThread::SCROLL_UNKNOWN, status.thread); + EXPECT_EQ(ScrollThread::SCROLL_ON_MAIN_THREAD, status.thread); EXPECT_EQ(MainThreadScrollingReason::kFailedHitTest, status.main_thread_scrolling_reasons); } @@ -11688,7 +11705,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollHitTestIsNotReliable) { TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollHitTestAncestorMismatch) { // If we ray cast a scroller this is on the first layer's ancestor chain, but // is not the first scroller we encounter when walking up from the layer, we - // should also return ScrollThread::SCROLL_UNKNOWN. + // should also return ScrollThread::SCROLL_ON_MAIN_THREAD. gfx::Size viewport_size(50, 50); gfx::Size content_size(100, 100); SetupViewportLayersOuterScrolls(viewport_size, content_size); @@ -11720,7 +11737,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollHitTestAncestorMismatch) { EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, status.thread); EXPECT_TRUE(status.needs_main_thread_hit_test); } else { - EXPECT_EQ(ScrollThread::SCROLL_UNKNOWN, status.thread); + EXPECT_EQ(ScrollThread::SCROLL_ON_MAIN_THREAD, status.thread); EXPECT_EQ(MainThreadScrollingReason::kFailedHitTest, status.main_thread_scrolling_reasons); } @@ -13885,14 +13902,14 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, FadedOutPaintedScrollbarHitTest) { // MouseDown on the track of a scrollbar with opacity 0 should not produce a // scroll. - scrollbar->set_scrollbar_painted_opacity(0); + scrollbar->SetScrollbarPaintedOpacity(0); InputHandlerPointerResult result = GetInputHandler().MouseDown( gfx::PointF(350, 100), /*jump_key_modifier*/ false); EXPECT_EQ(result.scroll_offset.y(), 0u); // MouseDown on the track of a scrollbar with opacity > 0 should produce a // scroll. - scrollbar->set_scrollbar_painted_opacity(1); + scrollbar->SetScrollbarPaintedOpacity(1); result = GetInputHandler().MouseDown(gfx::PointF(350, 100), /*jump_key_modifier*/ false); EXPECT_GT(result.scroll_offset.y(), 0u); @@ -14252,7 +14269,7 @@ TEST_F(LayerTreeHostImplTest, JumpOnScrollbarClick) { InputHandlerPointerResult result = GetInputHandler().MouseDown( gfx::PointF(350, 400), /*jump_key_modifier*/ true); EXPECT_EQ(result.type, PointerResultType::kScrollbarScroll); - EXPECT_EQ(result.scroll_offset.y(), 2194); + EXPECT_FLOAT_EQ(result.scroll_offset.y(), 2194.2856f); result = GetInputHandler().MouseUp(gfx::PointF(350, 400)); EXPECT_EQ(result.type, PointerResultType::kScrollbarScroll); EXPECT_EQ(result.scroll_offset.y(), 0); @@ -14265,7 +14282,7 @@ TEST_F(LayerTreeHostImplTest, JumpOnScrollbarClick) { InputHandlerPointerResult result = GetInputHandler().MouseDown( gfx::PointF(350, 400), /*jump_key_modifier*/ false); EXPECT_EQ(result.type, PointerResultType::kScrollbarScroll); - EXPECT_EQ(result.scroll_offset.y(), 2194); + EXPECT_FLOAT_EQ(result.scroll_offset.y(), 2194.2856f); result = GetInputHandler().MouseUp(gfx::PointF(350, 400)); EXPECT_EQ(result.type, PointerResultType::kScrollbarScroll); EXPECT_EQ(result.scroll_offset.y(), 0); @@ -14342,7 +14359,8 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ThumbDragAfterJumpClick) { gfx::PointF(350, 560), /*jump_key_modifier*/ true); // This verifies that the jump click took place as expected. - EXPECT_EQ(gfx::ScrollOffset(0, 243), result.scroll_offset); + EXPECT_EQ(0, result.scroll_offset.x()); + EXPECT_FLOAT_EQ(result.scroll_offset.y(), 243.80952f); // This verifies that the drag_state_ was initialized when a jump click // occurred. @@ -14351,10 +14369,93 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ThumbDragAfterJumpClick) { ->drag_state_.has_value()); // This verifies that the jump click delta was accounted for correctly. - EXPECT_EQ(GetInputHandler() - .scrollbar_controller_for_testing() - ->drag_state_->scroll_position_at_start_, - 243); + EXPECT_FLOAT_EQ(GetInputHandler() + .scrollbar_controller_for_testing() + ->drag_state_->scroll_position_at_start_, + 243.80952f); + } + + // Tear down the LayerTreeHostImpl before the InputHandlerClient. + host_impl_->ReleaseLayerTreeFrameSink(); + host_impl_ = nullptr; +} + +// Tests that an existing scroll offset animation (for a scrollbar) is aborted +// before a new one is created. +TEST_P(ScrollUnifiedLayerTreeHostImplTest, + AbortAnimatedScrollBeforeStartingAutoscroll) { + LayerTreeSettings settings = DefaultSettings(); + settings.compositor_threaded_scrollbar_scrolling = true; + CreateHostImpl(settings, CreateLayerTreeFrameSink()); + + // Setup the viewport. + const gfx::Size viewport_size = gfx::Size(360, 600); + const gfx::Size content_size = gfx::Size(345, 3800); + SetupViewportLayersOuterScrolls(viewport_size, content_size); + LayerImpl* scroll_layer = OuterViewportScrollLayer(); + + // Set up the scrollbar and its dimensions. + LayerTreeImpl* layer_tree_impl = host_impl_->active_tree(); + auto* scrollbar = AddLayer<PaintedScrollbarLayerImpl>( + layer_tree_impl, ScrollbarOrientation::VERTICAL, + /*is_left_side_vertical_scrollbar*/ false, + /*is_overlay*/ false); + + SetupScrollbarLayer(scroll_layer, scrollbar); + const gfx::Size scrollbar_size = gfx::Size(15, 600); + scrollbar->SetBounds(scrollbar_size); + host_impl_->set_force_smooth_wheel_scrolling_for_testing(true); + + // Set up the thumb dimensions. + scrollbar->SetThumbThickness(15); + scrollbar->SetThumbLength(50); + scrollbar->SetTrackRect(gfx::Rect(0, 15, 15, 575)); + + // Set up scrollbar arrows. + scrollbar->SetBackButtonRect( + gfx::Rect(gfx::Point(345, 0), gfx::Size(15, 15))); + scrollbar->SetForwardButtonRect( + gfx::Rect(gfx::Point(345, 570), gfx::Size(15, 15))); + scrollbar->SetOffsetToTransformParent(gfx::Vector2dF(345, 0)); + + TestInputHandlerClient input_handler_client; + GetInputHandler().BindToClient(&input_handler_client); + + { + // Set up an animated scrollbar autoscroll. + GetInputHandler().scrollbar_controller_for_testing()->HandlePointerDown( + gfx::PointF(350, 560), /*jump_key_modifier*/ false); + auto begin_state = BeginState(gfx::Point(350, 560), gfx::Vector2d(0, 40), + ui::ScrollInputType::kScrollbar); + EXPECT_EQ( + ScrollThread::SCROLL_ON_IMPL_THREAD, + GetInputHandler() + .ScrollBegin(begin_state.get(), ui::ScrollInputType::kScrollbar) + .thread); + auto update_state = UpdateState(gfx::Point(350, 560), gfx::Vector2dF(0, 40), + ui::ScrollInputType::kScrollbar); + update_state->data()->delta_granularity = + ui::ScrollGranularity::kScrollByPixel; + GetInputHandler().ScrollUpdate(update_state.get()); + + // Autoscroll animations should be active. + EXPECT_TRUE(GetInputHandler() + .scrollbar_controller_for_testing() + ->ScrollbarScrollIsActive()); + EXPECT_TRUE(GetImplAnimationHost()->ImplOnlyScrollAnimatingElement()); + } + + { + // When it's time to kick off the scrollbar autoscroll animation (i.e ~250ms + // after pointerdown), the ScrollbarController should ensure that any + // existing scroll offset animations are aborted and a new autoscroll + // animation is created. Test passes if unit test doesn't hit any DCHECK + // failures. + GetInputHandler() + .scrollbar_controller_for_testing() + ->StartAutoScrollAnimation(/*scroll_velocity*/ 800, + ScrollbarPart::FORWARD_TRACK); + EXPECT_TRUE(GetImplAnimationHost()->ImplOnlyScrollAnimatingElement()); } // Tear down the LayerTreeHostImpl before the InputHandlerClient. @@ -14530,7 +14631,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ThumbDragScrollerLengthIncrease) { host_impl_->WillBeginImplFrame(begin_frame_args); result = GetInputHandler().MouseMoveAt(gfx::Point(350, 20)); - EXPECT_EQ(result.scroll_offset.y(), 12); + EXPECT_FLOAT_EQ(result.scroll_offset.y(), 12.190476f); // This is intentional. The thumb drags that follow will test the behavior // *after* the scroller length expansion. @@ -14563,7 +14664,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ThumbDragScrollerLengthIncrease) { begin_frame_args.frame_id.sequence_number++; host_impl_->WillBeginImplFrame(begin_frame_args); result = GetInputHandler().MouseMoveAt(gfx::Point(350, 26)); - EXPECT_EQ(result.scroll_offset.y(), 49); + EXPECT_FLOAT_EQ(result.scroll_offset.y(), 48.761906f); GetInputHandler().MouseUp(gfx::PointF(350, 26)); host_impl_->DidFinishImplFrame(begin_frame_args); @@ -14618,7 +14719,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, MainThreadFallback) { // InputHandlerPointerResult should return a zero offset. This will cause the // main thread to handle the scroll. GetScrollNode(scroll_layer)->main_thread_scrolling_reasons = - MainThreadScrollingReason::kHasNonLayerViewportConstrainedObjects; + MainThreadScrollingReason::kThreadedScrollingDisabled; compositor_threaded_scrolling_result = GetInputHandler().MouseDown( gfx::PointF(350, 500), /*jump_key_modifier*/ false); GetInputHandler().MouseUp(gfx::PointF(350, 500)); @@ -14761,7 +14862,10 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollAnimatedWithDelay) { begin_frame_args.frame_id.sequence_number++; host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->UpdateAnimationState(true); - EXPECT_EQ(50, CurrentScrollOffset(scrolling_layer).y()); + EXPECT_NEAR( + (base::FeatureList::IsEnabled(features::kImpulseScrollAnimations) ? 87 + : 50), + CurrentScrollOffset(scrolling_layer).y(), 1); host_impl_->DidFinishImplFrame(begin_frame_args); // Update target. @@ -16306,9 +16410,9 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, UpdatedTilingsForNonDrawingLayers) { // Now add a transform animation to this layer. While we don't drawn layers // with non-invertible transforms, we still raster them if there is a // transform animation. - TransformOperations start_transform_operations; + gfx::TransformOperations start_transform_operations; start_transform_operations.AppendMatrix(singular); - TransformOperations end_transform_operations; + gfx::TransformOperations end_transform_operations; AddAnimatedTransformToElementWithAnimation( animated_transform_layer->element_id(), timeline(), 10.0, start_transform_operations, end_transform_operations); @@ -17403,7 +17507,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, PercentBasedScrollbarDeltasDSF3) { GetInputHandler().BindToClient(&input_handler_client); // Test scrolling with device scale factor = 3. - const float expected_delta = floorf(kPercentDeltaForDirectionalScroll * 500); + const float expected_delta = kPercentDeltaForDirectionalScroll * 500; host_impl_->active_tree()->set_painted_device_scale_factor(3); @@ -18141,4 +18245,38 @@ TEST_F(LayerTreeHostImplTest, FrameElementIdHitTestOverlapSibling) { GetInputHandler().FindFrameElementIdAtPoint(gfx::PointF(30, 30))); } +TEST_F(LayerTreeHostImplTest, DocumentTransitionRequestCausesDamage) { + const gfx::Size viewport_size(100, 100); + SetupDefaultRootLayer(viewport_size); + UpdateDrawProperties(host_impl_->active_tree()); + + const gfx::Transform draw_transform; + const gfx::Rect draw_viewport(viewport_size); + bool resourceless_software_draw = false; + + // Clear any damage. + host_impl_->OnDraw(draw_transform, draw_viewport, resourceless_software_draw, + false); + last_on_draw_frame_.reset(); + did_request_redraw_ = false; + + // Ensure there is no damage. + host_impl_->OnDraw(draw_transform, draw_viewport, resourceless_software_draw, + false); + EXPECT_FALSE(did_request_redraw_); + EXPECT_TRUE(last_on_draw_frame_->has_no_damage); + last_on_draw_frame_.reset(); + did_request_redraw_ = false; + + // Adding a transition effect should cause us to redraw. + host_impl_->active_tree()->AddDocumentTransitionRequest( + DocumentTransitionRequest::CreateStart(base::OnceClosure())); + + // Ensure there is damage and we requested a redraw. + host_impl_->OnDraw(draw_transform, draw_viewport, resourceless_software_draw, + false); + EXPECT_TRUE(did_request_redraw_); + EXPECT_FALSE(last_on_draw_frame_->has_no_damage); +} + } // namespace cc diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_blending.cc b/chromium/cc/trees/layer_tree_host_pixeltest_blending.cc index 496bdbf6da9..98bb4f49eb5 100644 --- a/chromium/cc/trees/layer_tree_host_pixeltest_blending.cc +++ b/chromium/cc/trees/layer_tree_host_pixeltest_blending.cc @@ -123,7 +123,7 @@ class LayerTreeHostBlendingPixelTest gfx::Size bounds(width, height); backdrop_client_.set_bounds(bounds); backdrop_client_.add_draw_image(backing_store->makeImageSnapshot(), - gfx::Point(), PaintFlags()); + gfx::Point()); scoped_refptr<FakePictureLayer> layer = FakePictureLayer::Create(&backdrop_client_); layer->SetIsDrawable(true); @@ -146,8 +146,7 @@ class LayerTreeHostBlendingPixelTest SkRect::MakeXYWH(1, 0, bounds.width() - 1, bounds.height()), paint); mask_client_.set_bounds(bounds); - mask_client_.add_draw_image(surface->makeImageSnapshot(), gfx::Point(), - PaintFlags()); + mask_client_.add_draw_image(surface->makeImageSnapshot(), gfx::Point()); scoped_refptr<FakePictureLayer> mask = FakePictureLayer::Create(&mask_client_); diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_filters.cc b/chromium/cc/trees/layer_tree_host_pixeltest_filters.cc index 3687e59cfa0..d0a756797cf 100644 --- a/chromium/cc/trees/layer_tree_host_pixeltest_filters.cc +++ b/chromium/cc/trees/layer_tree_host_pixeltest_filters.cc @@ -59,7 +59,7 @@ class LayerTreeHostFiltersPixelTest }; FilterOperations filters; - SkImageFilter::CropRect cropRect( + PaintFilter::CropRect cropRect( SkRect::MakeXYWH(-40000, -40000, 80000, 80000)); filters.Append(FilterOperation::CreateReferenceFilter( sk_make_sp<ColorFilterPaintFilter>(SkColorFilters::Matrix(matrix), @@ -98,8 +98,7 @@ TEST_P(LayerTreeHostFiltersPixelTest, BackdropFilterBlurRect) { background->AddChild(blur); FilterOperations filters; - filters.Append(FilterOperation::CreateBlurFilter( - 2.f, SkBlurImageFilter::kClamp_TileMode)); + filters.Append(FilterOperation::CreateBlurFilter(2.f, SkTileMode::kClamp)); blur->SetBackdropFilters(filters); gfx::RRectF backdrop_filter_bounds(gfx::RectF(gfx::SizeF(blur->bounds())), 0); blur->SetBackdropFilterBounds(backdrop_filter_bounds); @@ -164,8 +163,7 @@ TEST_P(LayerTreeHostFiltersPixelTest, BackdropFilterBlurRadius) { background->AddChild(blur); FilterOperations filters; - filters.Append(FilterOperation::CreateBlurFilter( - 30.f, SkBlurImageFilter::kClamp_TileMode)); + filters.Append(FilterOperation::CreateBlurFilter(30.f, SkTileMode::kClamp)); blur->SetBackdropFilters(filters); gfx::RRectF backdrop_filter_bounds(gfx::RectF(gfx::SizeF(blur->bounds())), 0); blur->SetBackdropFilterBounds(backdrop_filter_bounds); @@ -206,8 +204,7 @@ TEST_P(LayerTreeHostFiltersPixelTest, BackdropFilterBlurRounded) { background->AddChild(blur); FilterOperations filters; - filters.Append(FilterOperation::CreateBlurFilter( - 2.f, SkBlurImageFilter::kClamp_TileMode)); + filters.Append(FilterOperation::CreateBlurFilter(2.f, SkTileMode::kClamp)); blur->SetBackdropFilters(filters); gfx::RRectF backdrop_filter_bounds(gfx::RectF(gfx::SizeF(blur->bounds())), 14, 16, 18, 20, 22, 30, 40, 50); @@ -254,8 +251,7 @@ TEST_P(LayerTreeHostFiltersPixelTest, BackdropFilterBlurOutsets) { background->AddChild(blur); FilterOperations filters; - filters.Append(FilterOperation::CreateBlurFilter( - 5.f, SkBlurImageFilter::kClamp_TileMode)); + filters.Append(FilterOperation::CreateBlurFilter(5.f, SkTileMode::kClamp)); blur->SetBackdropFilters(filters); gfx::RRectF backdrop_filter_bounds(gfx::RectF(gfx::SizeF(blur->bounds())), 0); blur->SetBackdropFilterBounds(backdrop_filter_bounds); @@ -339,8 +335,7 @@ class LayerTreeHostBlurFiltersPixelTestGPULayerList EffectNode& blur_effect_node = CreateEffectNode(blur_layers[0].get()); FilterOperations filters; - filters.Append(FilterOperation::CreateBlurFilter( - 2.f, SkBlurImageFilter::kClamp_TileMode)); + filters.Append(FilterOperation::CreateBlurFilter(2.f, SkTileMode::kClamp)); blur_effect_node.backdrop_filters = filters; blur_effect_node.render_surface_reason = RenderSurfaceReason::kBackdropFilter; @@ -497,7 +492,7 @@ TEST_P(LayerTreeHostFiltersPixelTest, CroppedFilter) { // Check that a filter with a zero-height crop rect crops out its // result completely. FilterOperations filters; - SkImageFilter::CropRect cropRect(SkRect::MakeXYWH(0, 0, 100, 0)); + PaintFilter::CropRect cropRect(SkRect::MakeXYWH(0, 0, 100, 0)); sk_sp<PaintFilter> offset( sk_make_sp<OffsetPaintFilter>(0, 0, nullptr, &cropRect)); filters.Append(FilterOperation::CreateReferenceFilter(offset)); @@ -518,7 +513,7 @@ TEST_P(LayerTreeHostFiltersPixelTest, ImageFilterClipped) { // This filter does a red-blue swap, so the foreground becomes blue. matrix[2] = matrix[6] = matrix[10] = matrix[18] = 1.0f; // We filter only the bottom 200x100 pixels of the foreground. - SkImageFilter::CropRect crop_rect(SkRect::MakeXYWH(0, 100, 200, 100)); + PaintFilter::CropRect crop_rect(SkRect::MakeXYWH(0, 100, 200, 100)); FilterOperations filters; filters.Append( FilterOperation::CreateReferenceFilter(sk_make_sp<ColorFilterPaintFilter>( @@ -610,7 +605,11 @@ TEST_P(LayerTreeHostFiltersPixelTest, ImageFilterScaled) { RunPixelTest( background, - base::FilePath(FILE_PATH_LITERAL("backdrop_filter_on_scaled_layer_.png")) + base::FilePath( + (use_swangle() || use_skia_vulkan()) + ? FILE_PATH_LITERAL("backdrop_filter_on_scaled_layer_.png") + : FILE_PATH_LITERAL( + "backdrop_filter_on_scaled_layer_legacy_swiftshader_.png")) .InsertBeforeExtensionASCII(GetRendererSuffix())); } @@ -644,8 +643,7 @@ TEST_P(LayerTreeHostFiltersPixelTest, BackdropFilterRotated) { // Add a blur filter to the blue layer. FilterOperations filters; - filters.Append(FilterOperation::CreateBlurFilter( - 5.0f, SkBlurImageFilter::kClamp_TileMode)); + filters.Append(FilterOperation::CreateBlurFilter(5.0f, SkTileMode::kClamp)); filter_layer->SetBackdropFilters(filters); gfx::RRectF backdrop_filter_bounds( gfx::RectF(gfx::SizeF(filter_layer->bounds())), 0); @@ -724,7 +722,11 @@ TEST_P(LayerTreeHostFiltersPixelTest, ImageRenderSurfaceScaled) { RunPixelTest( background, - base::FilePath(FILE_PATH_LITERAL("scaled_render_surface_layer_.png")) + base::FilePath( + (use_swangle() || use_skia_vulkan()) + ? FILE_PATH_LITERAL("scaled_render_surface_layer_.png") + : FILE_PATH_LITERAL( + "scaled_render_surface_layer_legacy_swiftshader_.png")) .InsertBeforeExtensionASCII(GetRendererSuffix())); } @@ -1019,7 +1021,7 @@ TEST_P(LayerTreeHostFiltersPixelTest, EnlargedTextureWithCropOffsetFilter) { filter_layer->AddChild(child2); FilterOperations filters; - SkImageFilter::CropRect cropRect(SkRect::MakeXYWH(10, 10, 80, 80)); + PaintFilter::CropRect cropRect(SkRect::MakeXYWH(10, 10, 80, 80)); filters.Append(FilterOperation::CreateReferenceFilter( sk_make_sp<OffsetPaintFilter>(0, 0, nullptr, &cropRect))); filter_layer->SetFilters(filters); @@ -1052,8 +1054,7 @@ TEST_P(LayerTreeHostFiltersPixelTest, BlurFilterWithClip) { filter_layer->AddChild(child4); FilterOperations filters; - filters.Append(FilterOperation::CreateBlurFilter( - 2.f, SkBlurImageFilter::kClamp_TileMode)); + filters.Append(FilterOperation::CreateBlurFilter(2.f, SkTileMode::kClamp)); filter_layer->SetFilters(filters); // Force the allocation a larger textures. diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_masks.cc b/chromium/cc/trees/layer_tree_host_pixeltest_masks.cc index 63b1f719917..f0f9fe67a4e 100644 --- a/chromium/cc/trees/layer_tree_host_pixeltest_masks.cc +++ b/chromium/cc/trees/layer_tree_host_pixeltest_masks.cc @@ -59,7 +59,7 @@ class MaskContentLayerClient : public ContentLayerClient { bool FillsBoundsCompletely() const override { return false; } - gfx::Rect PaintableRegion() override { return gfx::Rect(bounds_); } + gfx::Rect PaintableRegion() const override { return gfx::Rect(bounds_); } scoped_refptr<DisplayItemList> PaintContentsToDisplayList() override { auto display_list = base::MakeRefCounted<DisplayItemList>(); @@ -192,7 +192,7 @@ class SolidColorEmptyMaskContentLayerClient : public ContentLayerClient { bool FillsBoundsCompletely() const override { return false; } - gfx::Rect PaintableRegion() override { return gfx::Rect(bounds_); } + gfx::Rect PaintableRegion() const override { return gfx::Rect(bounds_); } scoped_refptr<DisplayItemList> PaintContentsToDisplayList() override { // Intentionally return a solid color, empty mask display list. This @@ -333,8 +333,7 @@ TEST_P(LayerTreeHostMaskPixelTestWithLayerList, ImageMaskWithEffect) { FakeContentLayerClient layer_client; layer_client.set_bounds(mask_bounds_); - layer_client.add_draw_image(surface->makeImageSnapshot(), gfx::Point(), - PaintFlags()); + layer_client.add_draw_image(surface->makeImageSnapshot(), gfx::Point()); mask_layer_ = FakePictureLayer::Create(&layer_client); pixel_comparator_ = @@ -361,8 +360,7 @@ TEST_P(LayerTreeHostMasksPixelTest, ImageMaskOfLayer) { FakeContentLayerClient mask_client; mask_client.set_bounds(mask_bounds); - mask_client.add_draw_image(surface->makeImageSnapshot(), gfx::Point(), - PaintFlags()); + mask_client.add_draw_image(surface->makeImageSnapshot(), gfx::Point()); scoped_refptr<FakePictureLayer> mask = FakePictureLayer::Create(&mask_client); mask->SetIsDrawable(true); mask->SetBounds(mask_bounds); @@ -441,7 +439,7 @@ class CheckerContentLayerClient : public ContentLayerClient { : bounds_(bounds), color_(color), vertical_(vertical) {} ~CheckerContentLayerClient() override = default; bool FillsBoundsCompletely() const override { return false; } - gfx::Rect PaintableRegion() override { return gfx::Rect(bounds_); } + gfx::Rect PaintableRegion() const override { return gfx::Rect(bounds_); } scoped_refptr<DisplayItemList> PaintContentsToDisplayList() override { auto display_list = base::MakeRefCounted<DisplayItemList>(); display_list->StartPaint(); @@ -488,7 +486,7 @@ class CircleContentLayerClient : public ContentLayerClient { : bounds_(bounds) {} ~CircleContentLayerClient() override = default; bool FillsBoundsCompletely() const override { return false; } - gfx::Rect PaintableRegion() override { return gfx::Rect(bounds_); } + gfx::Rect PaintableRegion() const override { return gfx::Rect(bounds_); } scoped_refptr<DisplayItemList> PaintContentsToDisplayList() override { auto display_list = base::MakeRefCounted<DisplayItemList>(); display_list->StartPaint(); @@ -713,7 +711,7 @@ class StaticPictureLayer : private ContentLayerClient, public PictureLayer { new StaticPictureLayer(std::move(display_list))); } - gfx::Rect PaintableRegion() override { return gfx::Rect(bounds()); } + gfx::Rect PaintableRegion() const override { return gfx::Rect(bounds()); } scoped_refptr<DisplayItemList> PaintContentsToDisplayList() override { return display_list_; } diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_scrollbars.cc b/chromium/cc/trees/layer_tree_host_pixeltest_scrollbars.cc index d8c4420ce21..b531ed8694d 100644 --- a/chromium/cc/trees/layer_tree_host_pixeltest_scrollbars.cc +++ b/chromium/cc/trees/layer_tree_host_pixeltest_scrollbars.cc @@ -173,14 +173,22 @@ TEST_P(LayerTreeHostScrollbarsPixelTest, MAYBE_HugeTransformScale) { scale_transform.Scale(scale, scale); layer->SetTransform(scale_transform); - if (renderer_type_ == viz::RendererType::kSkiaGL || + if (use_swangle() || use_skia_vulkan() || + renderer_type_ == viz::RendererType::kSkiaGL || renderer_type_ == viz::RendererType::kSkiaDawn) pixel_comparator_ = std::make_unique<FuzzyPixelOffByOneComparator>(true); - RunPixelTest(background, - use_skia_vulkan() - ? base::FilePath(FILE_PATH_LITERAL("spiral_64_scale_vk.png")) - : base::FilePath(FILE_PATH_LITERAL("spiral_64_scale.png"))); + RunPixelTest( + background, + base::FilePath( + use_swangle() + ? (use_skia_vulkan() ? FILE_PATH_LITERAL("spiral_64_scale_vk.png") + : FILE_PATH_LITERAL("spiral_64_scale.png")) + : (use_skia_vulkan() + ? FILE_PATH_LITERAL( + "spiral_64_scale_legacy_swiftshader_vk.png") + : FILE_PATH_LITERAL( + "spiral_64_scale_legacy_swiftshader.png")))); } class LayerTreeHostOverlayScrollbarsPixelTest diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_tiles.cc b/chromium/cc/trees/layer_tree_host_pixeltest_tiles.cc index d837df92f79..a3f7a12b671 100644 --- a/chromium/cc/trees/layer_tree_host_pixeltest_tiles.cc +++ b/chromium/cc/trees/layer_tree_host_pixeltest_tiles.cc @@ -71,7 +71,7 @@ class BlueYellowClient : public ContentLayerClient { explicit BlueYellowClient(const gfx::Size& size) : size_(size), blue_top_(true) {} - gfx::Rect PaintableRegion() override { return gfx::Rect(size_); } + gfx::Rect PaintableRegion() const override { return gfx::Rect(size_); } scoped_refptr<DisplayItemList> PaintContentsToDisplayList() override { auto display_list = base::MakeRefCounted<DisplayItemList>(); @@ -151,7 +151,7 @@ class PrimaryColorClient : public ContentLayerClient { public: explicit PrimaryColorClient(const gfx::Size& size) : size_(size) {} - gfx::Rect PaintableRegion() override { return gfx::Rect(size_); } + gfx::Rect PaintableRegion() const override { return gfx::Rect(size_); } scoped_refptr<DisplayItemList> PaintContentsToDisplayList() override { // When painted, the DisplayItemList should produce blocks of red, green, // and blue to test primary color reproduction. @@ -353,7 +353,8 @@ TEST_P(LayerTreeHostTilesTestRasterColorSpace, CustomColorSpace) { // This test doesn't work on Vulkan because on our hardware we can't render to // RGBA4444 format using either SwiftShader or native Vulkan. See -// crbug.com/987278 for details +// crbug.com/987278 for details. +// TODO(crbug.com/1151490) : Re-enable after this is supported for OOPR. #if BUILDFLAG(ENABLE_GL_BACKEND_TESTS) class LayerTreeHostTilesTestPartialInvalidationLowBitDepth : public LayerTreeHostTilesTestPartialInvalidation { @@ -373,14 +374,16 @@ INSTANTIATE_TEST_SUITE_P( RasterTestConfig{viz::RendererType::kGL, TestRasterType::kGpu}), ::testing::PrintToStringParamName()); -TEST_P(LayerTreeHostTilesTestPartialInvalidationLowBitDepth, PartialRaster) { +TEST_P(LayerTreeHostTilesTestPartialInvalidationLowBitDepth, + DISABLED_PartialRaster) { use_partial_raster_ = true; RunSingleThreadedPixelTest(picture_layer_, base::FilePath(FILE_PATH_LITERAL( "blue_yellow_partial_flipped_dither.png"))); } -TEST_P(LayerTreeHostTilesTestPartialInvalidationLowBitDepth, FullRaster) { +TEST_P(LayerTreeHostTilesTestPartialInvalidationLowBitDepth, + DISABLED_FullRaster) { RunSingleThreadedPixelTest( picture_layer_, base::FilePath(FILE_PATH_LITERAL("blue_yellow_flipped_dither.png"))); diff --git a/chromium/cc/trees/layer_tree_host_single_thread_client.h b/chromium/cc/trees/layer_tree_host_single_thread_client.h index f2ce8e18597..f35d3cfd6b2 100644 --- a/chromium/cc/trees/layer_tree_host_single_thread_client.h +++ b/chromium/cc/trees/layer_tree_host_single_thread_client.h @@ -5,7 +5,9 @@ #ifndef CC_TREES_LAYER_TREE_HOST_SINGLE_THREAD_CLIENT_H_ #define CC_TREES_LAYER_TREE_HOST_SINGLE_THREAD_CLIENT_H_ +#include "base/containers/flat_set.h" #include "base/time/time.h" +#include "components/viz/common/surfaces/frame_sink_id.h" namespace cc { @@ -31,6 +33,12 @@ class LayerTreeHostSingleThreadClient { // run the machinery to acquire a new LayerTreeFrameSink. virtual void DidLoseLayerTreeFrameSink() = 0; + // When compositing-based throttling is enabled, this function is called every + // time when a frame composition change has updated the frame sinks to + // throttle. + virtual void FrameSinksToThrottleUpdated( + const base::flat_set<viz::FrameSinkId>& ids) {} + protected: virtual ~LayerTreeHostSingleThreadClient() {} }; diff --git a/chromium/cc/trees/layer_tree_host_unittest.cc b/chromium/cc/trees/layer_tree_host_unittest.cc index 163146ca91e..79ceaf87f18 100644 --- a/chromium/cc/trees/layer_tree_host_unittest.cc +++ b/chromium/cc/trees/layer_tree_host_unittest.cc @@ -12,16 +12,18 @@ #include "base/auto_reset.h" #include "base/bind.h" #include "base/callback_helpers.h" +#include "base/containers/contains.h" #include "base/location.h" #include "base/single_thread_task_runner.h" -#include "base/stl_util.h" #include "base/strings/stringprintf.h" #include "base/synchronization/lock.h" +#include "base/test/bind.h" +#include "base/test/simple_test_tick_clock.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" #include "build/build_config.h" #include "cc/animation/animation_host.h" -#include "cc/animation/timing_function.h" +#include "cc/document_transition/document_transition_request.h" #include "cc/input/scroll_elasticity_helper.h" #include "cc/layers/content_layer_client.h" #include "cc/layers/heads_up_display_layer.h" @@ -66,6 +68,7 @@ #include "components/viz/common/frame_sinks/begin_frame_args.h" #include "components/viz/common/frame_sinks/copy_output_request.h" #include "components/viz/common/frame_sinks/copy_output_result.h" +#include "components/viz/common/quads/compositor_frame_transition_directive.h" #include "components/viz/common/quads/compositor_render_pass_draw_quad.h" #include "components/viz/common/quads/draw_quad.h" #include "components/viz/common/quads/tile_draw_quad.h" @@ -79,6 +82,7 @@ #include "third_party/khronos/GLES2/gl2ext.h" #include "third_party/skia/include/core/SkPicture.h" #include "third_party/skia/include/gpu/GrDirectContext.h" +#include "ui/gfx/animation/keyframe/timing_function.h" #include "ui/gfx/geometry/point_conversions.h" #include "ui/gfx/geometry/size_conversions.h" #include "ui/gfx/geometry/vector2d_conversions.h" @@ -913,8 +917,7 @@ class LayerTreeHostTestInvisibleLayersSkipRenderPass void AddBackgroundBlurFilter(Layer* layer) { FilterOperations filters; - filters.Append(FilterOperation::CreateBlurFilter( - 30, SkBlurImageFilter::kClamp_TileMode)); + filters.Append(FilterOperation::CreateBlurFilter(30, SkTileMode::kClamp)); layer->SetBackdropFilters(filters); } @@ -8267,7 +8270,7 @@ class LayerTreeHostTestImageAnimationDrawImage private: void AddImageOp(const PaintImage& image) override { - content_layer_client_.add_draw_image(image, gfx::Point(0, 0), PaintFlags()); + content_layer_client_.add_draw_image(image, gfx::Point(0, 0)); } }; @@ -8289,7 +8292,7 @@ class LayerTreeHostTestImageAnimationDrawRecordShader : public LayerTreeHostTestImageAnimation { void AddImageOp(const PaintImage& image) override { auto record = sk_make_sp<PaintOpBuffer>(); - record->push<DrawImageOp>(image, 0.f, 0.f, nullptr); + record->push<DrawImageOp>(image, 0.f, 0.f); PaintFlags flags; flags.setShader(PaintShader::MakePaintRecord( record, SkRect::MakeWH(500, 500), SkTileMode::kClamp, @@ -8304,7 +8307,7 @@ class LayerTreeHostTestImageAnimationPaintFilter : public LayerTreeHostTestImageAnimation { void AddImageOp(const PaintImage& image) override { auto record = sk_make_sp<PaintOpBuffer>(); - record->push<DrawImageOp>(image, 0.f, 0.f, nullptr); + record->push<DrawImageOp>(image, 0.f, 0.f); PaintFlags flags; flags.setImageFilter( sk_make_sp<RecordPaintFilter>(record, SkRect::MakeWH(500, 500))); @@ -8380,12 +8383,9 @@ class LayerTreeHostTestImageDecodingHints : public LayerTreeHostTest { .set_id(3) .set_decoding_mode(PaintImage::DecodingMode::kUnspecified) .TakePaintImage(); - content_layer_client_.add_draw_image(async_image, gfx::Point(0, 0), - PaintFlags()); - content_layer_client_.add_draw_image(sync_image, gfx::Point(1, 2), - PaintFlags()); - content_layer_client_.add_draw_image(unspecified_image, gfx::Point(3, 4), - PaintFlags()); + content_layer_client_.add_draw_image(async_image, gfx::Point(0, 0)); + content_layer_client_.add_draw_image(sync_image, gfx::Point(1, 2)); + content_layer_client_.add_draw_image(unspecified_image, gfx::Point(3, 4)); layer_tree_host()->SetRootLayer( FakePictureLayer::Create(&content_layer_client_)); @@ -8817,7 +8817,7 @@ MULTI_THREAD_TEST_F(LayerTreeHostTopControlsDeltaTriggersViewportUpdate); // Tests that custom sequence throughput tracking result is reported to // LayerTreeHostClient. constexpr MutatorHost::TrackedAnimationSequenceId kSequenceId = 1u; -class LayerTreeHostCustomThrougputTrackerTest : public LayerTreeHostTest { +class LayerTreeHostCustomThroughputTrackerTest : public LayerTreeHostTest { public: void BeginTest() override { PostSetNeedsCommitToMainThread(); } @@ -8850,7 +8850,7 @@ class LayerTreeHostCustomThrougputTrackerTest : public LayerTreeHostTest { } }; -SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostCustomThrougputTrackerTest); +SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostCustomThroughputTrackerTest); // Confirm that DelegatedInkMetadata set on the LTH propagates to the // CompositorFrameMetadata and RenderFrameMetadata, and then both are correctly @@ -8901,9 +8901,10 @@ class LayerTreeHostTestDelegatedInkMetadataOnAndOff gfx::PointF point = gfx::PointF(135, 45); gfx::RectF area = gfx::RectF(173, 438); base::TimeTicks timestamp = base::TimeTicks::Now(); + bool is_hovering = true; - expected_metadata_ = - viz::DelegatedInkMetadata(point, diameter, color, timestamp, area); + expected_metadata_ = viz::DelegatedInkMetadata( + point, diameter, color, timestamp, area, is_hovering); layer_tree_host()->SetDelegatedInkMetadata( std::make_unique<viz::DelegatedInkMetadata>( expected_metadata_.value())); @@ -8925,24 +8926,29 @@ class LayerTreeHostTestDelegatedInkMetadataOnAndOff } } - void ExpectMetadata(bool had_delegated_ink_metadata, + void ExpectMetadata(base::Optional<DelegatedInkBrowserMetadata> + browser_delegated_ink_metadata, viz::DelegatedInkMetadata* actual_metadata) { if (expected_metadata_.has_value()) { - EXPECT_TRUE(had_delegated_ink_metadata); + EXPECT_TRUE(browser_delegated_ink_metadata.has_value()); EXPECT_TRUE(actual_metadata); + EXPECT_TRUE( + browser_delegated_ink_metadata.value().delegated_ink_is_hovering); EXPECT_EQ(expected_metadata_->point(), actual_metadata->point()); EXPECT_EQ(expected_metadata_->color(), actual_metadata->color()); EXPECT_EQ(expected_metadata_->diameter(), actual_metadata->diameter()); EXPECT_EQ(expected_metadata_->presentation_area(), actual_metadata->presentation_area()); EXPECT_EQ(expected_metadata_->timestamp(), actual_metadata->timestamp()); + EXPECT_EQ(expected_metadata_->is_hovering(), + actual_metadata->is_hovering()); // Record the frame time from the metadata so we can confirm that it // matches the LayerTreeHostImpl's frame time in DrawLayersOnThread. EXPECT_GT(actual_metadata->frame_time(), base::TimeTicks::Min()); metadata_frame_time_ = actual_metadata->frame_time(); } else { - EXPECT_FALSE(had_delegated_ink_metadata); + EXPECT_FALSE(browser_delegated_ink_metadata.has_value()); EXPECT_FALSE(actual_metadata); EndTest(); } @@ -8954,7 +8960,7 @@ class LayerTreeHostTestDelegatedInkMetadataOnAndOff const RenderFrameMetadata& render_frame_metadata, viz::CompositorFrameMetadata* compositor_frame_metadata, bool force_send) override { - ExpectMetadata(render_frame_metadata.has_delegated_ink_metadata, + ExpectMetadata(render_frame_metadata.delegated_ink_metadata, compositor_frame_metadata->delegated_ink_metadata.get()); } @@ -8999,18 +9005,30 @@ class LayerTreeHostTestEventsMetrics : public LayerTreeHostTest { private: void SimulateEventOnMain() { - std::unique_ptr<EventMetrics> metrics = EventMetrics::Create( + base::SimpleTestTickClock tick_clock; + tick_clock.Advance(base::TimeDelta::FromMicroseconds(10)); + base::TimeTicks event_time = tick_clock.NowTicks(); + tick_clock.Advance(base::TimeDelta::FromMicroseconds(10)); + std::unique_ptr<EventMetrics> metrics = EventMetrics::CreateForTesting( ui::ET_GESTURE_SCROLL_UPDATE, - EventMetrics::ScrollUpdateType::kContinued, base::TimeTicks::Now(), - ui::ScrollInputType::kWheel); + EventMetrics::ScrollUpdateType::kContinued, ui::ScrollInputType::kWheel, + event_time, &tick_clock); + DCHECK_NE(metrics, nullptr); { + tick_clock.Advance(base::TimeDelta::FromMicroseconds(10)); + metrics->SetDispatchStageTimestamp( + EventMetrics::DispatchStage::kRendererCompositorStarted); auto done_callback = base::BindOnce( - [](std::unique_ptr<EventMetrics> metrics, bool handled) { + [](std::unique_ptr<EventMetrics> metrics, + base::SimpleTestTickClock* tick_clock, bool handled) { + tick_clock->Advance(base::TimeDelta::FromMicroseconds(10)); + metrics->SetDispatchStageTimestamp( + EventMetrics::DispatchStage::kRendererCompositorFinished); std::unique_ptr<EventMetrics> result = handled ? std::move(metrics) : nullptr; return result; }, - std::move(metrics)); + std::move(metrics), &tick_clock); auto scoped_event_monitor = layer_tree_host()->GetScopedEventMetricsMonitor( std::move(done_callback)); @@ -9434,5 +9452,71 @@ class LayerTreeHostUkmSmoothnessMemoryOwnership : public LayerTreeTest { MULTI_THREAD_TEST_F(LayerTreeHostUkmSmoothnessMemoryOwnership); +class LayerTreeHostTestDocumentTransitionsPropagatedToMetadata + : public LayerTreeHostTest { + protected: + void SetupTree() override { + SetInitialRootBounds(gfx::Size(10, 10)); + LayerTreeHostTest::SetupTree(); + } + + void BeginTest() override { + layer_tree_host()->AddDocumentTransitionRequest( + DocumentTransitionRequest::CreatePrepare( + DocumentTransitionRequest::Effect::kExplode, + base::TimeDelta::FromMilliseconds(123), + base::BindLambdaForTesting([this]() { CommitLambdaCalled(); }))); + layer_tree_host()->AddDocumentTransitionRequest( + DocumentTransitionRequest::CreateStart( + base::BindLambdaForTesting([this]() { CommitLambdaCalled(); }))); + } + + void CommitLambdaCalled() { ++num_lambda_calls_; } + + void DisplayReceivedCompositorFrameOnThread( + const viz::CompositorFrame& frame) override { + ASSERT_EQ(2u, frame.metadata.transition_directives.size()); + const auto& save = frame.metadata.transition_directives[0]; + + EXPECT_EQ(save.type(), + viz::CompositorFrameTransitionDirective::Type::kSave); + EXPECT_EQ(save.effect(), + viz::CompositorFrameTransitionDirective::Effect::kExplode); + EXPECT_EQ(save.duration(), base::TimeDelta::FromMilliseconds(123)); + + const auto& animate = frame.metadata.transition_directives[1]; + EXPECT_GT(animate.sequence_id(), save.sequence_id()); + EXPECT_EQ(animate.type(), + viz::CompositorFrameTransitionDirective::Type::kAnimate); + + EndTest(); + } + + void AfterTest() override { EXPECT_EQ(2, num_lambda_calls_); } + + int num_lambda_calls_ = 0; +}; + +SINGLE_AND_MULTI_THREAD_TEST_F( + LayerTreeHostTestDocumentTransitionsPropagatedToMetadata); + +class LayerTreeHostTestDebugStateDowngrade : public LayerTreeHostTest { + void InitializeSettings(LayerTreeSettings* settings) override { + settings->initial_debug_state.show_fps_counter = true; + } + + void BeginTest() override { + LayerTreeHost* host = layer_tree_host(); + LayerTreeDebugState state = host->GetDebugState(); + EXPECT_TRUE(state.show_fps_counter); + state.show_fps_counter = false; + host->SetDebugState(state); + EXPECT_FALSE(host->GetDebugState().show_fps_counter); + EndTest(); + } +}; + +SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestDebugStateDowngrade); + } // namespace } // namespace cc diff --git a/chromium/cc/trees/layer_tree_host_unittest_animation.cc b/chromium/cc/trees/layer_tree_host_unittest_animation.cc index a3a4c36d76a..4d80dbe7f76 100644 --- a/chromium/cc/trees/layer_tree_host_unittest_animation.cc +++ b/chromium/cc/trees/layer_tree_host_unittest_animation.cc @@ -10,7 +10,6 @@ #include "base/bind.h" #include "base/metrics/statistics_recorder.h" #include "cc/animation/animation.h" -#include "cc/animation/animation_curve.h" #include "cc/animation/animation_host.h" #include "cc/animation/animation_id_provider.h" #include "cc/animation/animation_timeline.h" @@ -19,8 +18,6 @@ #include "cc/animation/scroll_offset_animation_curve.h" #include "cc/animation/scroll_offset_animation_curve_factory.h" #include "cc/animation/scroll_offset_animations.h" -#include "cc/animation/timing_function.h" -#include "cc/animation/transform_operations.h" #include "cc/base/completion_event.h" #include "cc/layers/layer.h" #include "cc/layers/layer_impl.h" @@ -33,6 +30,9 @@ #include "cc/trees/target_property.h" #include "cc/trees/transform_node.h" #include "components/viz/common/quads/compositor_frame.h" +#include "ui/gfx/animation/keyframe/animation_curve.h" +#include "ui/gfx/animation/keyframe/timing_function.h" +#include "ui/gfx/transform_operations.h" namespace cc { namespace { @@ -376,8 +376,9 @@ class LayerTreeHostAnimationTestAddKeyframeModelWithTimingFunction KeyframeModel* keyframe_model = animation_child_impl->GetKeyframeModel(TargetProperty::OPACITY); - const FloatAnimationCurve* curve = - keyframe_model->curve()->ToFloatAnimationCurve(); + const gfx::FloatAnimationCurve* curve = + gfx::FloatAnimationCurve::ToFloatAnimationCurve( + keyframe_model->curve()); float start_opacity = curve->GetValue(base::TimeDelta()); float end_opacity = curve->GetValue(curve->Duration()); float linearly_interpolated_opacity = @@ -554,9 +555,10 @@ class LayerTreeHostAnimationTestLayerAddedWithAnimation animation_->set_animation_delegate(this); // Any valid AnimationCurve will do here. - std::unique_ptr<AnimationCurve> curve(new FakeFloatAnimationCurve()); + std::unique_ptr<gfx::AnimationCurve> curve(new FakeFloatAnimationCurve()); std::unique_ptr<KeyframeModel> keyframe_model(KeyframeModel::Create( - std::move(curve), 1, 1, TargetProperty::OPACITY)); + std::move(curve), 1, 1, + KeyframeModel::TargetPropertyId(TargetProperty::OPACITY))); animation_->AddKeyframeModel(std::move(keyframe_model)); // We add the animation *before* attaching the layer to the tree. @@ -764,9 +766,8 @@ class LayerTreeHostAnimationTestCheckerboardDoesntStartAnimations MULTI_THREAD_TEST_F( LayerTreeHostAnimationTestCheckerboardDoesntStartAnimations); -// Verifies that scroll offset animations are only accepted when impl-scrolling -// is supported, and that when scroll offset animations are accepted, -// scroll offset updates are sent back to the main thread. +// Verifies that a scroll offset animation sends scroll offset updates back to +// the main thread. class LayerTreeHostAnimationTestScrollOffsetChangesArePropagated : public LayerTreeHostAnimationTest { public: @@ -794,13 +795,10 @@ class LayerTreeHostAnimationTestScrollOffsetChangesArePropagated CreateEaseInOutAnimationForTesting( gfx::ScrollOffset(500.f, 550.f))); std::unique_ptr<KeyframeModel> keyframe_model(KeyframeModel::Create( - std::move(curve), 1, 0, TargetProperty::SCROLL_OFFSET)); + std::move(curve), 1, 0, + KeyframeModel::TargetPropertyId(TargetProperty::SCROLL_OFFSET))); keyframe_model->set_needs_synchronized_start_time(true); - bool impl_scrolling_supported = proxy()->SupportsImplScrolling(); - if (impl_scrolling_supported) - animation_child_->AddKeyframeModel(std::move(keyframe_model)); - else - EndTest(); + animation_child_->AddKeyframeModel(std::move(keyframe_model)); break; } default: @@ -864,10 +862,11 @@ class LayerTreeHostAnimationTestScrollOffsetAnimationTakeover } } - void NotifyAnimationTakeover(base::TimeTicks monotonic_time, - int target_property, - base::TimeTicks animation_start_time, - std::unique_ptr<AnimationCurve> curve) override { + void NotifyAnimationTakeover( + base::TimeTicks monotonic_time, + int target_property, + base::TimeTicks animation_start_time, + std::unique_ptr<gfx::AnimationCurve> curve) override { EndTest(); } @@ -941,8 +940,9 @@ class LayerTreeHostAnimationTestScrollOffsetAnimationAdjusted ScrollOffsetKeyframeEffect(*host_impl, scroll_layer_) .GetKeyframeModel(TargetProperty::SCROLL_OFFSET); DCHECK(keyframe_model); - ScrollOffsetAnimationCurve* curve = - keyframe_model->curve()->ToScrollOffsetAnimationCurve(); + const ScrollOffsetAnimationCurve* curve = + ScrollOffsetAnimationCurve::ToScrollOffsetAnimationCurve( + keyframe_model->curve()); // Verifiy the initial and target position before the scroll offset // update from MT. @@ -966,8 +966,9 @@ class LayerTreeHostAnimationTestScrollOffsetAnimationAdjusted ScrollOffsetKeyframeEffect(*host_impl, scroll_layer_) .GetKeyframeModel(TargetProperty::SCROLL_OFFSET); DCHECK(keyframe_model); - ScrollOffsetAnimationCurve* curve = - keyframe_model->curve()->ToScrollOffsetAnimationCurve(); + const ScrollOffsetAnimationCurve* curve = + ScrollOffsetAnimationCurve::ToScrollOffsetAnimationCurve( + keyframe_model->curve()); // Verifiy the initial and target position after the scroll offset // update from MT EXPECT_EQ(KeyframeModel::RunState::STARTING, keyframe_model->run_state()); @@ -1006,7 +1007,8 @@ class LayerTreeHostPresentationDuringAnimation ScrollOffsetAnimationCurveFactory::CreateEaseInOutAnimationForTesting( gfx::ScrollOffset(6500.f, 7500.f))); std::unique_ptr<KeyframeModel> keyframe_model(KeyframeModel::Create( - std::move(curve), 1, 0, TargetProperty::SCROLL_OFFSET)); + std::move(curve), 1, 0, + KeyframeModel::TargetPropertyId(TargetProperty::SCROLL_OFFSET))); keyframe_model->set_needs_synchronized_start_time(true); AttachAnimationsToTimeline(); @@ -1086,7 +1088,8 @@ class LayerTreeHostAnimationTestScrollOffsetAnimationRemoval ScrollOffsetAnimationCurveFactory::CreateEaseInOutAnimationForTesting( gfx::ScrollOffset(6500.f, 7500.f))); std::unique_ptr<KeyframeModel> keyframe_model(KeyframeModel::Create( - std::move(curve), 1, 0, TargetProperty::SCROLL_OFFSET)); + std::move(curve), 1, 0, + KeyframeModel::TargetPropertyId(TargetProperty::SCROLL_OFFSET))); keyframe_model->set_needs_synchronized_start_time(true); AttachAnimationsToTimeline(); @@ -1211,7 +1214,8 @@ class LayerTreeHostAnimationTestScrollOffsetAnimationCompletion ScrollOffsetAnimationCurveFactory::CreateEaseInOutAnimationForTesting( final_position_)); std::unique_ptr<KeyframeModel> keyframe_model(KeyframeModel::Create( - std::move(curve), 1, 0, TargetProperty::SCROLL_OFFSET)); + std::move(curve), 1, 0, + KeyframeModel::TargetPropertyId(TargetProperty::SCROLL_OFFSET))); keyframe_model->set_needs_synchronized_start_time(true); AttachAnimationsToTimeline(); @@ -1421,9 +1425,9 @@ class LayerTreeHostAnimationTestPendingTreeAnimatesFirstCommit void BeginTest() override { // Add a translate from 6,7 to 8,9. - TransformOperations start; + gfx::TransformOperations start; start.AppendTranslate(6.f, 7.f, 0.f); - TransformOperations end; + gfx::TransformOperations end; end.AppendTranslate(8.f, 9.f, 0.f); AddAnimatedTransformToAnimation(animation_.get(), 4.0, start, end); @@ -2167,9 +2171,9 @@ class ImplSideInvalidationWithoutCommitTestScroll ScrollOffsetAnimationCurveFactory::CreateEaseInOutAnimationForTesting( gfx::ScrollOffset(500.f, 550.f))); std::unique_ptr<KeyframeModel> keyframe_model(KeyframeModel::Create( - std::move(curve), 1, 0, TargetProperty::SCROLL_OFFSET)); + std::move(curve), 1, 0, + KeyframeModel::TargetPropertyId(TargetProperty::SCROLL_OFFSET))); keyframe_model->set_needs_synchronized_start_time(true); - ASSERT_TRUE(proxy()->SupportsImplScrolling()); animation_child_->AddKeyframeModel(std::move(keyframe_model)); PostSetNeedsCommitToMainThread(); } @@ -2262,9 +2266,9 @@ class LayerTreeHostAnimationTestChangeAnimation timeline_->DetachAnimation(animation_child_.get()); animation_->AttachElement(layer_->element_id()); - TransformOperations start; + gfx::TransformOperations start; start.AppendTranslate(5.f, 5.f, 0.f); - TransformOperations end; + gfx::TransformOperations end; end.AppendTranslate(5.f, 5.f, 0.f); AddAnimatedTransformToAnimation(animation_.get(), 1.0, start, end); } diff --git a/chromium/cc/trees/layer_tree_host_unittest_capture.cc b/chromium/cc/trees/layer_tree_host_unittest_capture.cc new file mode 100644 index 00000000000..0336419a2bb --- /dev/null +++ b/chromium/cc/trees/layer_tree_host_unittest_capture.cc @@ -0,0 +1,143 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/test/fake_content_layer_client.h" +#include "cc/test/fake_picture_layer.h" +#include "cc/test/layer_tree_test.h" +#include "cc/test/property_tree_test_utils.h" +#include "cc/trees/layer_tree_impl.h" +#include "components/viz/common/quads/compositor_frame.h" +#include "components/viz/common/surfaces/subtree_capture_id.h" + +namespace cc { +namespace { + +constexpr viz::SubtreeCaptureId kCaptureId{22}; + +// A base class for tests that verifies the bahvior of the layer tree when a +// sub layer has a valid viz::SubtreeCaptureId. +class LayerTreeHostCaptureTest : public LayerTreeTest { + public: + void SetupTree() override { + scoped_refptr<Layer> root = FakePictureLayer::Create(&client_); + root->SetBounds(gfx::Size(100, 100)); + + child_ = FakePictureLayer::Create(&client_); + child_->SetBounds(gfx::Size(50, 60)); + child_->SetPosition(gfx::PointF(10.f, 5.5f)); + root->AddChild(child_); + + grand_child_ = FakePictureLayer::Create(&client_); + grand_child_->SetBounds(gfx::Size(70, 30)); + grand_child_->SetPosition(gfx::PointF(50.f, 50.f)); + child_->AddChild(grand_child_); + + layer_tree_host()->SetRootLayer(root); + LayerTreeTest::SetupTree(); + client_.set_bounds(root->bounds()); + } + + void DrawLayersOnThread(LayerTreeHostImpl* impl) override { + LayerImpl* root = impl->active_tree()->root_layer(); + LayerImpl* child = impl->active_tree()->LayerById(child_->id()); + LayerImpl* grand_child = impl->active_tree()->LayerById(grand_child_->id()); + + VerifyLayerImpls(root, child, grand_child); + } + + protected: + // Lets test subclasses to verify the LayerImpls of the layers in the tree. + virtual void VerifyLayerImpls(LayerImpl* root, + LayerImpl* child, + LayerImpl* grand_child) = 0; + + FakeContentLayerClient client_; + scoped_refptr<Layer> child_; + scoped_refptr<Layer> grand_child_; +}; + +// ----------------------------------------------------------------------------- +// LayerTreeHostCaptureTestNoExtraRenderPassWhenNotCapturing: +// +// Tests that a layer tree that doesn't have a viz::SubtreeCaptureId on any of +// its layers, draw in a single root render surface, and generates a single +// compositor render pass. +class LayerTreeHostCaptureTestNoExtraRenderPassWhenNotCapturing + : public LayerTreeHostCaptureTest { + public: + void BeginTest() override { PostSetNeedsCommitToMainThread(); } + + void VerifyLayerImpls(LayerImpl* root, + LayerImpl* child, + LayerImpl* grand_child) override { + // All layers in the tree draw in the same root render surface. + auto* root_surface = GetRenderSurface(root); + auto* child_surface = GetRenderSurface(child); + auto* grand_child_surface = GetRenderSurface(grand_child); + EXPECT_EQ(root_surface, child_surface); + EXPECT_EQ(root_surface, grand_child_surface); + EXPECT_FALSE(root_surface->CopyOfOutputRequired()); + EXPECT_FALSE(root_surface->SubtreeCaptureId().is_valid()); + + EndTest(); + } + + void DisplayReceivedCompositorFrameOnThread( + const viz::CompositorFrame& frame) override { + // There should be a single compositor render pass, which has no valid + // SubtreeCaptureId. + ASSERT_EQ(frame.render_pass_list.size(), 1u); + EXPECT_FALSE(frame.render_pass_list.back()->subtree_capture_id.is_valid()); + } +}; + +SINGLE_AND_MULTI_THREAD_TEST_F( + LayerTreeHostCaptureTestNoExtraRenderPassWhenNotCapturing); + +// ----------------------------------------------------------------------------- +// LayerTreeHostCaptureTestLayerWithCaptureIdElevatesToSurface +// +// Tests that a layer sub tree whose root has a valid viz::SubtreeCaptureId will +// draw into a separate render surface and a separate render pass. +class LayerTreeHostCaptureTestLayerWithCaptureIdElevatesToSurface + : public LayerTreeHostCaptureTest { + public: + void BeginTest() override { child_->SetSubtreeCaptureId(kCaptureId); } + + void VerifyLayerImpls(LayerImpl* root, + LayerImpl* child, + LayerImpl* grand_child) override { + // |child| should draw into a separate render surface from that of the root, + // and the |grand_child| should draw into the render surface of its parent + // (which is |child|'s). + // The |chils|'s surface should have the expected capture ID. + auto* root_surface = GetRenderSurface(root); + auto* child_surface = GetRenderSurface(child); + auto* grand_child_surface = GetRenderSurface(grand_child); + EXPECT_NE(root_surface, child_surface); + EXPECT_NE(root_surface, grand_child_surface); + EXPECT_EQ(child_surface, grand_child_surface); + EXPECT_EQ(kCaptureId, child_surface->SubtreeCaptureId()); + EXPECT_TRUE(child_surface->CopyOfOutputRequired()); + + EndTest(); + } + + void DisplayReceivedCompositorFrameOnThread( + const viz::CompositorFrame& frame) override { + // There should be 2 render passes. The non-root render pass is associated + // with the layer subtree rooted at |child| and should have the expected + // capture ID. + ASSERT_EQ(frame.render_pass_list.size(), 2u); + EXPECT_TRUE(frame.render_pass_list.front()->subtree_capture_id.is_valid()); + EXPECT_EQ(kCaptureId, frame.render_pass_list.front()->subtree_capture_id); + EXPECT_FALSE(frame.render_pass_list.back()->subtree_capture_id.is_valid()); + } +}; + +SINGLE_AND_MULTI_THREAD_TEST_F( + LayerTreeHostCaptureTestLayerWithCaptureIdElevatesToSurface); + +} // namespace +} // namespace cc diff --git a/chromium/cc/trees/layer_tree_host_unittest_capture_content.cc b/chromium/cc/trees/layer_tree_host_unittest_capture_content.cc index c7bf7b5b3cf..2423cef7446 100644 --- a/chromium/cc/trees/layer_tree_host_unittest_capture_content.cc +++ b/chromium/cc/trees/layer_tree_host_unittest_capture_content.cc @@ -71,7 +71,8 @@ class LayerTreeHostCaptureContentTest : public LayerTreeTest { root->AddChild(root_picture_layer_); layer_tree_host()->SetRootLayer(root); - layer_tree_host()->SetViewportVisibleRect(gfx::Rect(device_bounds_)); + layer_tree_host()->SetVisualDeviceViewportIntersectionRect( + gfx::Rect(device_bounds_)); } void VerifyCapturedContent(std::vector<FakeTextHolder>* expected_result) { diff --git a/chromium/cc/trees/layer_tree_host_unittest_checkerimaging.cc b/chromium/cc/trees/layer_tree_host_unittest_checkerimaging.cc index f841b4c7826..9801b1718cb 100644 --- a/chromium/cc/trees/layer_tree_host_unittest_checkerimaging.cc +++ b/chromium/cc/trees/layer_tree_host_unittest_checkerimaging.cc @@ -69,7 +69,7 @@ class LayerTreeHostCheckerImagingTest : public LayerTreeTest { .set_decoding_mode(PaintImage::DecodingMode::kAsync) .TakePaintImage(); content_layer_client_.add_draw_image(checkerable_image, gfx::Point(0, 0), - PaintFlags()); + SkSamplingOptions(), PaintFlags()); layer_tree_host()->SetRootLayer( FakePictureLayer::Create(&content_layer_client_)); diff --git a/chromium/cc/trees/layer_tree_host_unittest_context.cc b/chromium/cc/trees/layer_tree_host_unittest_context.cc index b679607a722..648e80e7ea1 100644 --- a/chromium/cc/trees/layer_tree_host_unittest_context.cc +++ b/chromium/cc/trees/layer_tree_host_unittest_context.cc @@ -1216,7 +1216,8 @@ class UIResourceLostAfterCommit : public UIResourceLostTestSimple { switch (time_step_) { case 1: // The resource should have been created on LTHI after the commit. - EXPECT_NE(0u, impl->ResourceIdForUIResource(ui_resource_->id())); + EXPECT_NE(viz::kInvalidResourceId, + impl->ResourceIdForUIResource(ui_resource_->id())); PostSetNeedsCommitToMainThread(); break; case 2: @@ -1228,7 +1229,8 @@ class UIResourceLostAfterCommit : public UIResourceLostTestSimple { EXPECT_EQ(1, ui_resource_->lost_resource_count); // Resource Id on the impl-side have been recreated as well. Note // that the same UIResourceId persists after the context lost. - EXPECT_NE(0u, impl->ResourceIdForUIResource(ui_resource_->id())); + EXPECT_NE(viz::kInvalidResourceId, + impl->ResourceIdForUIResource(ui_resource_->id())); PostSetNeedsCommitToMainThread(); break; } @@ -1319,9 +1321,11 @@ class UIResourceLostBeforeCommit : public UIResourceLostTestSimple { case 3: // Sequence 2 (continued): // The previous resource should have been deleted. - EXPECT_EQ(0u, impl->ResourceIdForUIResource(test_id0_)); + EXPECT_EQ(viz::kInvalidResourceId, + impl->ResourceIdForUIResource(test_id0_)); // The second resource should have been created. - EXPECT_NE(0u, impl->ResourceIdForUIResource(test_id1_)); + EXPECT_NE(viz::kInvalidResourceId, + impl->ResourceIdForUIResource(test_id1_)); // The second resource was not actually uploaded before the context // was lost, so it only got created once. EXPECT_EQ(1, ui_resource_->resource_create_count); @@ -1334,7 +1338,8 @@ class UIResourceLostBeforeCommit : public UIResourceLostTestSimple { // No "resource lost" callbacks. EXPECT_EQ(0, ui_resource_->lost_resource_count); // The UI resource id should not be valid - EXPECT_EQ(0u, impl->ResourceIdForUIResource(test_id0_)); + EXPECT_EQ(viz::kInvalidResourceId, + impl->ResourceIdForUIResource(test_id0_)); break; } } @@ -1394,7 +1399,8 @@ class UIResourceLostBeforeActivateTree : public UIResourceLostTest { // The resource is not yet lost (sanity check). EXPECT_EQ(0, ui_resource_->lost_resource_count); // The resource should not have been created yet on the impl-side. - EXPECT_EQ(0u, impl->ResourceIdForUIResource(ui_resource_->id())); + EXPECT_EQ(viz::kInvalidResourceId, + impl->ResourceIdForUIResource(ui_resource_->id())); LoseContext(); break; case 3: @@ -1410,7 +1416,8 @@ class UIResourceLostBeforeActivateTree : public UIResourceLostTest { // The pending requests on the impl-side should not have been processed // since the context was lost. But we should have marked the resource as // evicted instead. - EXPECT_EQ(0u, impl->ResourceIdForUIResource(ui_resource_->id())); + EXPECT_EQ(viz::kInvalidResourceId, + impl->ResourceIdForUIResource(ui_resource_->id())); EXPECT_TRUE(impl->EvictedUIResourcesExist()); break; case 2: @@ -1418,13 +1425,15 @@ class UIResourceLostBeforeActivateTree : public UIResourceLostTest { // should have gotten recreated now and shouldn't be marked as evicted // anymore. EXPECT_EQ(1, ui_resource_->lost_resource_count); - EXPECT_NE(0u, impl->ResourceIdForUIResource(ui_resource_->id())); + EXPECT_NE(viz::kInvalidResourceId, + impl->ResourceIdForUIResource(ui_resource_->id())); EXPECT_FALSE(impl->EvictedUIResourcesExist()); break; case 4: // The resource is deleted and should not be in the manager. Use // test_id_ since ui_resource_ has been deleted. - EXPECT_EQ(0u, impl->ResourceIdForUIResource(test_id_)); + EXPECT_EQ(viz::kInvalidResourceId, + impl->ResourceIdForUIResource(test_id_)); break; } @@ -1476,9 +1485,12 @@ class UIResourceLostEviction : public UIResourceLostTestSimple { if (!visible) { // All resources should have been evicted. ASSERT_EQ(0u, sii_->shared_image_count()); - EXPECT_EQ(0u, impl->ResourceIdForUIResource(ui_resource_->id())); - EXPECT_EQ(0u, impl->ResourceIdForUIResource(ui_resource2_->id())); - EXPECT_EQ(0u, impl->ResourceIdForUIResource(ui_resource3_->id())); + EXPECT_EQ(viz::kInvalidResourceId, + impl->ResourceIdForUIResource(ui_resource_->id())); + EXPECT_EQ(viz::kInvalidResourceId, + impl->ResourceIdForUIResource(ui_resource2_->id())); + EXPECT_EQ(viz::kInvalidResourceId, + impl->ResourceIdForUIResource(ui_resource3_->id())); EXPECT_EQ(2, ui_resource_->resource_create_count); EXPECT_EQ(1, ui_resource_->lost_resource_count); // Drawing is disabled both because of the evicted resources and @@ -1496,16 +1508,20 @@ class UIResourceLostEviction : public UIResourceLostTestSimple { // The first two resources should have been created on LTHI after the // commit. ASSERT_EQ(2u, sii_->shared_image_count()); - EXPECT_NE(0u, impl->ResourceIdForUIResource(ui_resource_->id())); - EXPECT_NE(0u, impl->ResourceIdForUIResource(ui_resource2_->id())); + EXPECT_NE(viz::kInvalidResourceId, + impl->ResourceIdForUIResource(ui_resource_->id())); + EXPECT_NE(viz::kInvalidResourceId, + impl->ResourceIdForUIResource(ui_resource2_->id())); EXPECT_EQ(1, ui_resource_->resource_create_count); EXPECT_EQ(0, ui_resource_->lost_resource_count); EXPECT_TRUE(impl->CanDraw()); // Evict all UI resources. This will trigger a commit. impl->EvictAllUIResources(); ASSERT_EQ(0u, sii_->shared_image_count()); - EXPECT_EQ(0u, impl->ResourceIdForUIResource(ui_resource_->id())); - EXPECT_EQ(0u, impl->ResourceIdForUIResource(ui_resource2_->id())); + EXPECT_EQ(viz::kInvalidResourceId, + impl->ResourceIdForUIResource(ui_resource_->id())); + EXPECT_EQ(viz::kInvalidResourceId, + impl->ResourceIdForUIResource(ui_resource2_->id())); EXPECT_EQ(1, ui_resource_->resource_create_count); EXPECT_EQ(0, ui_resource_->lost_resource_count); EXPECT_FALSE(impl->CanDraw()); @@ -1513,10 +1529,12 @@ class UIResourceLostEviction : public UIResourceLostTestSimple { case 2: // The first two resources should have been recreated. ASSERT_EQ(2u, sii_->shared_image_count()); - EXPECT_NE(0u, impl->ResourceIdForUIResource(ui_resource_->id())); + EXPECT_NE(viz::kInvalidResourceId, + impl->ResourceIdForUIResource(ui_resource_->id())); EXPECT_EQ(2, ui_resource_->resource_create_count); EXPECT_EQ(1, ui_resource_->lost_resource_count); - EXPECT_NE(0u, impl->ResourceIdForUIResource(ui_resource2_->id())); + EXPECT_NE(viz::kInvalidResourceId, + impl->ResourceIdForUIResource(ui_resource2_->id())); EXPECT_EQ(2, ui_resource2_->resource_create_count); EXPECT_EQ(1, ui_resource2_->lost_resource_count); EXPECT_TRUE(impl->CanDraw()); @@ -1525,17 +1543,20 @@ class UIResourceLostEviction : public UIResourceLostTestSimple { // The first resource should have been recreated after visibility was // restored. ASSERT_EQ(2u, sii_->shared_image_count()); - EXPECT_NE(0u, impl->ResourceIdForUIResource(ui_resource_->id())); + EXPECT_NE(viz::kInvalidResourceId, + impl->ResourceIdForUIResource(ui_resource_->id())); EXPECT_EQ(3, ui_resource_->resource_create_count); EXPECT_EQ(2, ui_resource_->lost_resource_count); // This resource was deleted. - EXPECT_EQ(0u, impl->ResourceIdForUIResource(ui_resource2_->id())); + EXPECT_EQ(viz::kInvalidResourceId, + impl->ResourceIdForUIResource(ui_resource2_->id())); EXPECT_EQ(2, ui_resource2_->resource_create_count); EXPECT_EQ(1, ui_resource2_->lost_resource_count); // This resource should have been created now. - EXPECT_NE(0u, impl->ResourceIdForUIResource(ui_resource3_->id())); + EXPECT_NE(viz::kInvalidResourceId, + impl->ResourceIdForUIResource(ui_resource3_->id())); EXPECT_EQ(1, ui_resource3_->resource_create_count); EXPECT_EQ(0, ui_resource3_->lost_resource_count); EXPECT_TRUE(impl->CanDraw()); @@ -1565,7 +1586,8 @@ class UIResourceFreedIfLostWhileExported : public LayerTreeHostContextTest { switch (impl->active_tree()->source_frame_number()) { case 0: // The UIResource has been created and a gpu resource made for it. - EXPECT_NE(0u, impl->ResourceIdForUIResource(ui_resource_->id())); + EXPECT_NE(viz::kInvalidResourceId, + impl->ResourceIdForUIResource(ui_resource_->id())); EXPECT_EQ(1u, sii_->shared_image_count()); // Lose the LayerTreeFrameSink connection. The UI resource should // be replaced and the old texture should be destroyed. @@ -1574,7 +1596,8 @@ class UIResourceFreedIfLostWhileExported : public LayerTreeHostContextTest { case 1: // The UIResource has been recreated, the old texture is not kept // around. - EXPECT_NE(0u, impl->ResourceIdForUIResource(ui_resource_->id())); + EXPECT_NE(viz::kInvalidResourceId, + impl->ResourceIdForUIResource(ui_resource_->id())); EXPECT_EQ(1u, sii_->shared_image_count()); MainThreadTaskRunner()->PostTask( FROM_HERE, @@ -1714,7 +1737,7 @@ class SoftwareTileResourceFreedIfLostWhileExported : public LayerTreeTest { } FakeContentLayerClient client_; - viz::ResourceId exported_resource_id_ = 0; + viz::ResourceId exported_resource_id_ = viz::kInvalidResourceId; }; SINGLE_AND_MULTI_THREAD_TEST_F(SoftwareTileResourceFreedIfLostWhileExported); diff --git a/chromium/cc/trees/layer_tree_host_unittest_scroll.cc b/chromium/cc/trees/layer_tree_host_unittest_scroll.cc index 26b01cbd5ab..1aa61b98e70 100644 --- a/chromium/cc/trees/layer_tree_host_unittest_scroll.cc +++ b/chromium/cc/trees/layer_tree_host_unittest_scroll.cc @@ -5,10 +5,10 @@ #include "cc/trees/layer_tree_host.h" #include "base/bind.h" +#include "base/containers/contains.h" #include "base/location.h" #include "base/memory/weak_ptr.h" #include "base/single_thread_task_runner.h" -#include "base/stl_util.h" #include "base/threading/thread_task_runner_handle.h" #include "build/build_config.h" #include "cc/animation/animation_host.h" @@ -1729,8 +1729,17 @@ class LayerTreeHostScrollTestImplScrollUnderMainThreadScrollingParent scoped_refptr<Layer> scroller_; }; +// This test is flaky in the single threaded configuration, only on the +// chromeos-amd64-generic-rel bot. https://crbug.com/1093078. +#if defined(OS_CHROMEOS) +// SINGLE_THREAD_TEST_F( +// LayerTreeHostScrollTestImplScrollUnderMainThreadScrollingParent); +MULTI_THREAD_TEST_F( + LayerTreeHostScrollTestImplScrollUnderMainThreadScrollingParent); +#else SINGLE_AND_MULTI_THREAD_TEST_F( LayerTreeHostScrollTestImplScrollUnderMainThreadScrollingParent); +#endif class ThreadCheckingInputHandlerClient : public InputHandlerClient { public: diff --git a/chromium/cc/trees/layer_tree_impl.cc b/chromium/cc/trees/layer_tree_impl.cc index cb9d1d174d5..1ac475f2afe 100644 --- a/chromium/cc/trees/layer_tree_impl.cc +++ b/chromium/cc/trees/layer_tree_impl.cc @@ -16,12 +16,12 @@ #include <utility> #include "base/containers/adapters.h" +#include "base/containers/contains.h" #include "base/debug/crash_logging.h" #include "base/debug/dump_without_crashing.h" #include "base/json/json_writer.h" #include "base/metrics/histogram_macros.h" #include "base/numerics/ranges.h" -#include "base/stl_util.h" #include "base/strings/stringprintf.h" #include "base/timer/elapsed_timer.h" #include "base/trace_event/trace_event.h" @@ -31,6 +31,7 @@ #include "cc/base/histograms.h" #include "cc/base/math_util.h" #include "cc/base/synced_property.h" +#include "cc/document_transition/document_transition_request.h" #include "cc/input/page_scale_animation.h" #include "cc/input/scrollbar_animation_controller.h" #include "cc/layers/effect_tree_layer_list_iterator.h" @@ -660,6 +661,9 @@ void LayerTreeImpl::PushPropertiesTo(LayerTreeImpl* target_tree) { delegated_ink_metadata_->point().ToString()); target_tree->set_delegated_ink_metadata(std::move(delegated_ink_metadata_)); } + + for (auto& request : TakeDocumentTransitionRequests()) + target_tree->AddDocumentTransitionRequest(std::move(request)); } void LayerTreeImpl::HandleTickmarksVisibilityChange() { @@ -964,9 +968,8 @@ void LayerTreeImpl::UpdateTransformAnimation(ElementId element_id, list_type); if (node->has_potential_animation != has_potential_animation) { node->has_potential_animation = has_potential_animation; - mutator_host()->GetAnimationScales(element_id, list_type, - &node->maximum_animation_scale, - &node->starting_animation_scale); + node->maximum_animation_scale = + mutator_host()->MaximumScale(element_id, list_type); transform_tree.set_needs_update(true); set_needs_update_draw_properties(); } @@ -1194,6 +1197,7 @@ void LayerTreeImpl::SetDeviceViewportRect( if (device_viewport_rect == device_viewport_rect_) return; device_viewport_rect_ = device_viewport_rect; + device_viewport_rect_changed_ = true; set_needs_update_draw_properties(); if (!IsActiveTree()) @@ -1450,6 +1454,8 @@ bool LayerTreeImpl::UpdateDrawProperties( image_animation_controller()->UpdateStateFromDrivers(); } + device_viewport_rect_changed_ = false; + DCHECK(!needs_update_draw_properties_) << "CalcDrawProperties should not set_needs_update_draw_properties()"; return true; @@ -2143,7 +2149,6 @@ static void FindClosestMatchingLayer(const gfx::PointF& screen_space_point, LayerImpl* root_layer, const Functor& func, FindClosestMatchingLayerState* state) { - base::ElapsedTimer timer; // We want to iterate from front to back when hit testing. for (auto* layer : base::Reversed(*root_layer->layer_tree_impl())) { if (!func(layer)) @@ -2173,12 +2178,6 @@ static void FindClosestMatchingLayer(const gfx::PointF& screen_space_point, state->closest_match = layer; } } - if (const char* client_name = GetClientNameForMetrics()) { - UMA_HISTOGRAM_COUNTS_1M( - base::StringPrintf("Compositing.%s.HitTestTimeToFindClosestLayer", - client_name), - timer.Elapsed().InMicroseconds()); - } } LayerImpl* LayerTreeImpl::FindFirstScrollingLayerOrScrollbarThatIsHitByPoint( @@ -2670,4 +2669,20 @@ std::string LayerTreeImpl::LayerListAsJson() const { return value.ToFormattedJSON(); } +void LayerTreeImpl::AddDocumentTransitionRequest( + std::unique_ptr<DocumentTransitionRequest> request) { + document_transition_requests_.push_back(std::move(request)); + // We need to send the request to viz. + SetNeedsRedraw(); +} + +std::vector<std::unique_ptr<DocumentTransitionRequest>> +LayerTreeImpl::TakeDocumentTransitionRequests() { + return std::move(document_transition_requests_); +} + +bool LayerTreeImpl::HasDocumentTransitionRequests() const { + return !document_transition_requests_.empty(); +} + } // namespace cc diff --git a/chromium/cc/trees/layer_tree_impl.h b/chromium/cc/trees/layer_tree_impl.h index b1638ab1490..ec13bafda99 100644 --- a/chromium/cc/trees/layer_tree_impl.h +++ b/chromium/cc/trees/layer_tree_impl.h @@ -48,6 +48,7 @@ class ContextProvider; namespace cc { class DebugRectHistory; +class DocumentTransitionRequest; class DroppedFrameCounter; class HeadsUpDisplayLayerImpl; class ImageDecodeCache; @@ -745,6 +746,21 @@ class CC_EXPORT LayerTreeImpl { return events_metrics_from_main_thread_.size(); } + bool device_viewport_rect_changed() const { + return device_viewport_rect_changed_; + } + + // Add a document transition request from the embedder. + void AddDocumentTransitionRequest( + std::unique_ptr<DocumentTransitionRequest> request); + + // Returns all of the document transition requests stored so far, and empties + // the internal list. + std::vector<std::unique_ptr<DocumentTransitionRequest>> + TakeDocumentTransitionRequests(); + + bool HasDocumentTransitionRequests() const; + protected: float ClampPageScaleFactorToLimits(float page_scale_factor) const; void PushPageScaleFactorAndLimits(const float* page_scale_factor, @@ -796,6 +812,7 @@ class CC_EXPORT LayerTreeImpl { bool new_local_surface_id_request_ = false; // Contains the physical rect of the device viewport, to be used in // determining what needs to be drawn. + bool device_viewport_rect_changed_ = false; gfx::Rect device_viewport_rect_; scoped_refptr<SyncedElasticOverscroll> elastic_overscroll_; @@ -898,6 +915,10 @@ class CC_EXPORT LayerTreeImpl { EventMetrics::List events_metrics_from_main_thread_; std::unique_ptr<viz::DelegatedInkMetadata> delegated_ink_metadata_; + + // Document transition requests to be transferred to Viz. + std::vector<std::unique_ptr<DocumentTransitionRequest>> + document_transition_requests_; }; } // namespace cc diff --git a/chromium/cc/trees/layer_tree_impl_unittest.cc b/chromium/cc/trees/layer_tree_impl_unittest.cc index 0e1e993131c..a132530bfaa 100644 --- a/chromium/cc/trees/layer_tree_impl_unittest.cc +++ b/chromium/cc/trees/layer_tree_impl_unittest.cc @@ -1697,7 +1697,7 @@ TEST_F(LayerTreeImplTest, HitTestingTouchHandlerRegionsForLayerThatIsNotDrawn) { // Hit testing for a point outside the test layer should return null pointer. // We also implicitly check that the updated screen space transform of a layer // that is not in drawn render surface layer list (test_layer) is used during - // hit testing (becuase the point is inside test_layer with respect to the old + // hit testing (because the point is inside test_layer with respect to the old // screen space transform). gfx::PointF test_point(24.f, 24.f); test_layer->SetOffsetToTransformParent(gfx::Vector2dF(25.f, 25.f)); diff --git a/chromium/cc/trees/layer_tree_settings.h b/chromium/cc/trees/layer_tree_settings.h index 3d411412c72..c02eb016dc3 100644 --- a/chromium/cc/trees/layer_tree_settings.h +++ b/chromium/cc/trees/layer_tree_settings.h @@ -197,10 +197,11 @@ class CC_EXPORT LayerTreeSettings { // See crbug.com/1008483. bool enable_transform_interop = false; - // When enabled, the compositor specifies a frame rate preference that would - // allow the display to run at a low refresh rate matching the playback rate - // for videos updating onscreen. - bool force_preferred_interval_for_video = false; + // Enables ThrottleDecider which produces a list of FrameSinkIds that are + // candidates for throttling. + // LayerTreeHostSingleThreadClient::FrameSinksToThrottleUpdated() will be + // called with candidates. + bool enable_compositing_based_throttling = false; }; class CC_EXPORT LayerListSettings : public LayerTreeSettings { diff --git a/chromium/cc/trees/mutator_host.h b/chromium/cc/trees/mutator_host.h index d0865d0002d..6e4773a91a5 100644 --- a/chromium/cc/trees/mutator_host.h +++ b/chromium/cc/trees/mutator_host.h @@ -28,8 +28,8 @@ class LayerTreeMutator; class ScrollTree; // Used as the return value of GetAnimationScales() to indicate that there is -// no active scale animation or the scale cannot be computed. -const float kNotScaled = 0; +// no active transform animation or the scale cannot be computed. +constexpr float kInvalidScale = 0.f; // A MutatorHost owns all the animation and mutation effects. // There is just one MutatorHost for LayerTreeHost on main renderer thread @@ -42,8 +42,7 @@ class MutatorHost { public: virtual ~MutatorHost() = default; - virtual std::unique_ptr<MutatorHost> CreateImplInstance( - bool supports_impl_scrolling) const = 0; + virtual std::unique_ptr<MutatorHost> CreateImplInstance() const = 0; virtual void ClearMutators() = 0; @@ -62,7 +61,6 @@ class MutatorHost { virtual void PushPropertiesTo(MutatorHost* host_impl) = 0; - virtual void SetSupportsScrollAnimations(bool supports_scroll_animations) = 0; virtual void SetScrollAnimationDurationForTesting( base::TimeDelta duration) = 0; virtual bool NeedsTickAnimations() const = 0; @@ -120,16 +118,11 @@ class MutatorHost { virtual bool AnimationsPreserveAxisAlignment(ElementId element_id) const = 0; - // Gets scales transform animations. On return, |maximum_scale| is the maximum - // scale along any dimension at any destination in active scale animations, - // and |starting_scale| is the maximum of starting animation scale along any - // dimension at any destination in active scale animations. They are set to - // kNotScaled if there is no active scale animation or the scales cannot be - // computed. - virtual void GetAnimationScales(ElementId element_id, - ElementListType list_type, - float* maximum_scale, - float* starting_scale) const = 0; + // Returns the maximum scale along any dimension at any destination in active + // scale animations, or kInvalidScale if there is no active transform + // animation or the scale cannot be computed. + virtual float MaximumScale(ElementId element_id, + ElementListType list_type) const = 0; virtual bool IsElementAnimating(ElementId element_id) const = 0; virtual bool HasTickingKeyframeModelForTesting( @@ -167,6 +160,11 @@ class MutatorHost { virtual bool HasCanvasInvalidation() const = 0; virtual bool HasJSAnimation() const = 0; + // Iterates through all animations and returns the minimum tick interval. + // Returns 0 if there is a continuous animation which should be ticked + // as fast as possible. + virtual base::TimeDelta MinimumTickInterval() const = 0; + using TrackedAnimationSequenceId = size_t; struct PendingThroughputTrackerInfo { // Id of a tracked animation sequence. diff --git a/chromium/cc/trees/mutator_host_client.h b/chromium/cc/trees/mutator_host_client.h index dd08f0afc1f..4a794d76bfb 100644 --- a/chromium/cc/trees/mutator_host_client.h +++ b/chromium/cc/trees/mutator_host_client.h @@ -61,10 +61,9 @@ class MutatorHostClient { const PropertyAnimationState& mask, const PropertyAnimationState& state) = 0; - virtual void AnimationScalesChanged(ElementId element_id, - ElementListType list_type, - float maximum_scale, - float starting_scale) = 0; + virtual void MaximumScaleChanged(ElementId element_id, + ElementListType list_type, + float maximum_scale) = 0; virtual void ScrollOffsetAnimationFinished() = 0; virtual gfx::ScrollOffset GetScrollOffsetForAnimation( @@ -75,9 +74,8 @@ class MutatorHostClient { ElementListType tree_type) = 0; virtual void OnCustomPropertyMutated( - ElementId element_id, - const std::string& custom_property_name, - PaintWorkletInput::PropertyValue custom_property_value) = 0; + PaintWorkletInput::PropertyKey property_key, + PaintWorkletInput::PropertyValue property_value) = 0; }; } // namespace cc diff --git a/chromium/cc/trees/occlusion_tracker.cc b/chromium/cc/trees/occlusion_tracker.cc index b3ed41e48f5..2a88893df7a 100644 --- a/chromium/cc/trees/occlusion_tracker.cc +++ b/chromium/cc/trees/occlusion_tracker.cc @@ -196,9 +196,7 @@ void OcclusionTracker::FinishedRenderTarget( // Readbacks always happen on render targets so we only need to check // for readbacks here. bool target_is_only_for_copy_request_or_force_render_surface = - (finished_target_surface->HasCopyRequest() || - finished_target_surface->ShouldCacheRenderSurface()) && - is_hidden; + is_hidden && finished_target_surface->CopyOfOutputRequired(); // If the occlusion within the surface can not be applied to things outside of // the surface's subtree, then clear the occlusion here so it won't be used. diff --git a/chromium/cc/trees/property_animation_state.cc b/chromium/cc/trees/property_animation_state.cc index 6c785231ac0..48a2f6282b7 100644 --- a/chromium/cc/trees/property_animation_state.cc +++ b/chromium/cc/trees/property_animation_state.cc @@ -59,7 +59,7 @@ PropertyAnimationState operator^(const PropertyAnimationState& lhs, bool PropertyAnimationState::IsValid() const { // currently_running must be a subset for potentially_animating. // currently <= potentially i.e. potentially || !currently. - TargetProperties result = potentially_animating | ~currently_running; + gfx::TargetProperties result = potentially_animating | ~currently_running; return result.all(); } diff --git a/chromium/cc/trees/property_animation_state.h b/chromium/cc/trees/property_animation_state.h index 1b7c07a7ed7..f37b64d1bd8 100644 --- a/chromium/cc/trees/property_animation_state.h +++ b/chromium/cc/trees/property_animation_state.h @@ -7,6 +7,7 @@ #include "cc/cc_export.h" #include "cc/trees/target_property.h" +#include "ui/gfx/animation/keyframe/target_property.h" namespace cc { @@ -15,8 +16,8 @@ struct CC_EXPORT PropertyAnimationState { PropertyAnimationState(const PropertyAnimationState& rhs); ~PropertyAnimationState(); - TargetProperties currently_running; - TargetProperties potentially_animating; + gfx::TargetProperties currently_running; + gfx::TargetProperties potentially_animating; bool operator==(const PropertyAnimationState& other) const; bool operator!=(const PropertyAnimationState& other) const; diff --git a/chromium/cc/trees/property_tree.cc b/chromium/cc/trees/property_tree.cc index d9da2f91bcc..8bc3982468a 100644 --- a/chromium/cc/trees/property_tree.cc +++ b/chromium/cc/trees/property_tree.cc @@ -24,6 +24,7 @@ #include "cc/trees/transform_node.h" #include "components/viz/common/frame_sinks/copy_output_request.h" #include "ui/gfx/geometry/vector2d_conversions.h" +#include "ui/gfx/transform_util.h" namespace cc { @@ -574,7 +575,7 @@ void TransformTree::SetRootScaleAndTransform( const gfx::Transform& device_transform) { device_scale_factor_ = device_scale_factor; gfx::Vector2dF device_transform_scale_components = - MathUtil::ComputeTransform2dScaleComponents(device_transform, 1.f); + gfx::ComputeTransform2dScaleComponents(device_transform, 1.f); // Not handling the rare case of different x and y device scale. device_transform_scale_factor_ = @@ -589,8 +590,7 @@ void TransformTree::SetRootScaleAndTransform( gfx::Transform transform = device_transform; transform.Scale(device_scale_factor, device_scale_factor); gfx::Vector2dF screen_space_scale = - MathUtil::ComputeTransform2dScaleComponents(transform, - device_scale_factor); + gfx::ComputeTransform2dScaleComponents(transform, device_scale_factor); DCHECK_NE(screen_space_scale.x(), 0.f); DCHECK_NE(screen_space_scale.y(), 0.f); @@ -729,20 +729,24 @@ void EffectTree::UpdateIsDrawn(EffectNode* node, EffectNode* parent_node) { // Exceptions: // 1) Nodes that contribute to copy requests, whether hidden or not, must be // drawn. - // 2) Nodes that have a backdrop filter. - // 3) Nodes with animating screen space opacity on main thread or pending tree + // 2) Nodes that have a valid SubtreeCaptureId, must be drawn so that they can + // be captured by the FrameSinkVideoCapturer. + // 3) Nodes that have a backdrop filter. + // 4) Nodes with animating screen space opacity on main thread or pending tree // are drawn if their parent is drawn irrespective of their opacity. - if (node->has_copy_request || node->cache_render_surface) + if (node->has_copy_request || node->cache_render_surface || + node->subtree_capture_id.is_valid()) { node->is_drawn = true; - else if (EffectiveOpacity(node) == 0.f && - (!node->has_potential_opacity_animation || - property_trees()->is_active) && - node->backdrop_filters.IsEmpty()) + } else if (EffectiveOpacity(node) == 0.f && + (!node->has_potential_opacity_animation || + property_trees()->is_active) && + node->backdrop_filters.IsEmpty()) { node->is_drawn = false; - else if (parent_node) + } else if (parent_node) { node->is_drawn = parent_node->is_drawn; - else + } else { node->is_drawn = true; + } } void EffectTree::UpdateEffectChanged(EffectNode* node, @@ -789,7 +793,8 @@ void EffectTree::UpdateHasMaskingChild(EffectNode* node, void EffectTree::UpdateOnlyDrawsVisibleContent(EffectNode* node, EffectNode* parent_node) { - node->only_draws_visible_content = !node->has_copy_request; + node->only_draws_visible_content = + !node->has_copy_request && !node->subtree_capture_id.is_valid(); if (parent_node) node->only_draws_visible_content &= parent_node->only_draws_visible_content; if (!node->backdrop_filters.IsEmpty()) { @@ -813,9 +818,8 @@ void EffectTree::UpdateSurfaceContentsScale(EffectNode* effect_node) { layer_scale_factor *= transform_tree.page_scale_factor(); const gfx::Vector2dF old_scale = effect_node->surface_contents_scale; - effect_node->surface_contents_scale = - MathUtil::ComputeTransform2dScaleComponents( - transform_tree.ToScreen(transform_node->id), layer_scale_factor); + effect_node->surface_contents_scale = gfx::ComputeTransform2dScaleComponents( + transform_tree.ToScreen(transform_node->id), layer_scale_factor); // If surface contents scale changes, draw transforms are no longer valid. // Invalidates the draw transform cache and updates the clip for the surface. @@ -1949,13 +1953,11 @@ bool PropertyTrees::ElementIsAnimatingChanged( return updated_transform; } -void PropertyTrees::AnimationScalesChanged(ElementId element_id, - float maximum_scale, - float starting_scale) { +void PropertyTrees::MaximumAnimationScaleChanged(ElementId element_id, + float maximum_scale) { if (TransformNode* transform_node = transform_tree.FindNodeFromElementId(element_id)) { transform_node->maximum_animation_scale = maximum_scale; - transform_node->starting_animation_scale = starting_scale; UpdateTransformTreeUpdateNumber(); } } @@ -2038,105 +2040,102 @@ std::string PropertyTrees::ToString() const { return value.ToFormattedJSON(); } -CombinedAnimationScale PropertyTrees::GetAnimationScales( - int transform_node_id, - LayerTreeImpl* layer_tree_impl) { - AnimationScaleData* animation_scales = - &cached_data_.animation_scales[transform_node_id]; - if (animation_scales->update_number != +bool PropertyTrees::AnimationScaleCacheIsInvalid(int transform_id) const { + DCHECK(!is_main_thread); + // This doesn't check if |update_number| equals to + // |transform_tree_update_number| because the the latter is changed by the + // animation itself while we want to treat the scale as valid during the + // animation. |update_number| is reset to kInvalidUpdateNumber when a new + // property tree is pushed. + return cached_data_.animation_scales[transform_id].update_number == + kInvalidUpdateNumber; +} + +float PropertyTrees::MaximumAnimationToScreenScale(int transform_id) { + return GetAnimationScaleData(transform_id).maximum_to_screen_scale; +} + +bool PropertyTrees::AnimationAffectedByInvalidScale(int transform_id) { + return GetAnimationScaleData(transform_id).affected_by_invalid_scale; +} + +const AnimationScaleData& PropertyTrees::GetAnimationScaleData( + int transform_id) { + DCHECK(!is_main_thread); + + auto& animation_scale = cached_data_.animation_scales[transform_id]; + if (animation_scale.update_number == cached_data_.transform_tree_update_number) { - TransformNode* node = transform_tree.Node(transform_node_id); - TransformNode* parent_node = transform_tree.parent(node); - bool ancestor_is_animating_scale = false; - float ancestor_maximum_target_scale = kNotScaled; - float ancestor_starting_animation_scale = kNotScaled; - if (parent_node) { - CombinedAnimationScale combined_animation_scale = - GetAnimationScales(parent_node->id, layer_tree_impl); - ancestor_maximum_target_scale = - combined_animation_scale.maximum_animation_scale; - ancestor_starting_animation_scale = - combined_animation_scale.starting_animation_scale; - ancestor_is_animating_scale = - cached_data_.animation_scales[parent_node->id] - .to_screen_has_scale_animation; - } + return animation_scale; + } + + animation_scale.update_number = cached_data_.transform_tree_update_number; + + TransformNode* node = transform_tree.Node(transform_id); + TransformNode* parent_node = transform_tree.parent(node); + const auto* parent_animation_scale = + parent_node ? &GetAnimationScaleData(parent_node->id) : nullptr; + + bool ancestor_affected_by_animation_scale = + parent_node && parent_animation_scale->affected_by_animation_scale; + bool node_affected_by_animation_scale = + node->has_potential_animation && node->maximum_animation_scale != 1.0f; + + animation_scale.affected_by_animation_scale = + node_affected_by_animation_scale || ancestor_affected_by_animation_scale; + animation_scale.affected_by_invalid_scale = + (parent_node && parent_animation_scale->affected_by_invalid_scale) || + // Computing maximum animated scale in the presence of perspective isn't + // supported. + node->to_parent.HasPerspective() || + (node->has_potential_animation && + node->maximum_animation_scale == kInvalidScale); + + // We don't attempt to accumulate animation scale from multiple nodes with + // scale animations, because of the risk of significant overestimation. For + // example, one node might be increasing scale from 1 to 10 at the same time + // as another node is decreasing scale from 10 to 1. Naively combining these + // scales would produce a scale of 100. + bool failed_for_multiple_scale_animations = + ancestor_affected_by_animation_scale && node_affected_by_animation_scale; + + float local_maximum_scale = 1.0f; + if (animation_scale.affected_by_invalid_scale || + failed_for_multiple_scale_animations) { + // Will use the parent's maximum_to_screen_scale. + } else if (!node->to_screen_is_potentially_animated) { + // No transform animations. Calculate the current to_screen scale. + gfx::Vector2dF to_screen_scales = gfx::ComputeTransform2dScaleComponents( + transform_tree.ToScreen(transform_id), kInvalidScale); + animation_scale.maximum_to_screen_scale = + std::max(to_screen_scales.x(), to_screen_scales.y()); + return animation_scale; + } else if (!node->has_potential_animation) { + gfx::Vector2dF local_scales = + gfx::ComputeTransform2dScaleComponents(node->local, 1.0f); + local_maximum_scale = std::max(local_scales.x(), local_scales.y()); + } else { + DCHECK_NE(node->maximum_animation_scale, kInvalidScale); + local_maximum_scale = node->maximum_animation_scale; + } - bool node_is_animating_scale = - node->maximum_animation_scale != kNotScaled && - node->starting_animation_scale != kNotScaled; - - animation_scales->to_screen_has_scale_animation = - node_is_animating_scale || ancestor_is_animating_scale; - - // Once we've failed to compute a maximum animated scale at an ancestor, we - // continue to fail. - bool failed_at_ancestor = ancestor_is_animating_scale && - ancestor_maximum_target_scale == kNotScaled; - - // Computing maximum animated scale in the presence of non-scale/translation - // transforms isn't supported. - bool failed_for_non_scale_or_translation = - !node->to_parent.IsScaleOrTranslation(); - - // We don't attempt to accumulate animation scale from multiple nodes with - // scale animations, because of the risk of significant overestimation. For - // example, one node might be increasing scale from 1 to 10 at the same time - // as another node is decreasing scale from 10 to 1. Naively combining these - // scales would produce a scale of 100. - bool failed_for_multiple_scale_animations = - ancestor_is_animating_scale && node_is_animating_scale; - - if (failed_at_ancestor || failed_for_non_scale_or_translation || - failed_for_multiple_scale_animations) { - // This ensures that descendants know we've failed to compute a maximum - // animated scale. - animation_scales->to_screen_has_scale_animation = true; - animation_scales->combined_maximum_animation_target_scale = kNotScaled; - animation_scales->combined_starting_animation_scale = kNotScaled; - } else if (!animation_scales->to_screen_has_scale_animation) { - animation_scales->combined_maximum_animation_target_scale = kNotScaled; - animation_scales->combined_starting_animation_scale = kNotScaled; - } else if (!node_is_animating_scale) { - // An ancestor is animating scale. - gfx::Vector2dF local_scales = - MathUtil::ComputeTransform2dScaleComponents(node->local, kNotScaled); - float max_local_scale = std::max(local_scales.x(), local_scales.y()); - animation_scales->combined_maximum_animation_target_scale = - max_local_scale * ancestor_maximum_target_scale; - animation_scales->combined_starting_animation_scale = - max_local_scale * ancestor_starting_animation_scale; - } else { - gfx::Vector2dF ancestor_scales = - parent_node - ? MathUtil::ComputeTransform2dScaleComponents( - transform_tree.ToScreen(parent_node->id), kNotScaled) - : gfx::Vector2dF(1.f, 1.f); - - float max_ancestor_scale = - std::max(ancestor_scales.x(), ancestor_scales.y()); - animation_scales->combined_maximum_animation_target_scale = - max_ancestor_scale * node->maximum_animation_scale; - animation_scales->combined_starting_animation_scale = - max_ancestor_scale * node->starting_animation_scale; - } - animation_scales->update_number = cached_data_.transform_tree_update_number; + animation_scale.maximum_to_screen_scale = local_maximum_scale; + if (parent_node) { + animation_scale.maximum_to_screen_scale *= + parent_animation_scale->maximum_to_screen_scale; } - return CombinedAnimationScale( - animation_scales->combined_maximum_animation_target_scale, - animation_scales->combined_starting_animation_scale); + + return animation_scale; } -void PropertyTrees::SetAnimationScalesForTesting( +void PropertyTrees::SetMaximumAnimationToScreenScaleForTesting( int transform_id, - float maximum_animation_scale, - float starting_animation_scale) { - cached_data_.animation_scales[transform_id] - .combined_maximum_animation_target_scale = maximum_animation_scale; - cached_data_.animation_scales[transform_id] - .combined_starting_animation_scale = starting_animation_scale; - cached_data_.animation_scales[transform_id].update_number = - cached_data_.transform_tree_update_number; + float maximum_scale, + bool affected_by_invalid_scale) { + auto& animation_scale = cached_data_.animation_scales[transform_id]; + animation_scale.maximum_to_screen_scale = maximum_scale; + animation_scale.affected_by_invalid_scale = affected_by_invalid_scale; + animation_scale.update_number = cached_data_.transform_tree_update_number; } bool PropertyTrees::GetToTarget(int transform_id, @@ -2200,7 +2199,7 @@ DrawTransformData& PropertyTrees::FetchDrawTransformsDataFromCache( // Add an entry to the cache. cached_data_.draw_transforms[transform_id].push_back(DrawTransformData()); DrawTransformData& data = cached_data_.draw_transforms[transform_id].back(); - data.update_number = -1; + data.update_number = kInvalidUpdateNumber; data.target_id = dest_id; return data; } @@ -2210,7 +2209,8 @@ ClipRectData* PropertyTrees::FetchClipRectFromCache(int clip_id, ClipNode* clip_node = clip_tree.Node(clip_id); for (size_t i = 0; i < clip_node->cached_clip_rects->size(); ++i) { auto& data = clip_node->cached_clip_rects[i]; - if (data.target_id == target_id || data.target_id == -1) + if (data.target_id == target_id || + data.target_id == EffectTree::kInvalidNodeId) return &data; } clip_node->cached_clip_rects->emplace_back(); @@ -2286,13 +2286,13 @@ void PropertyTrees::ResetCachedData() { const auto transform_count = transform_tree.nodes().size(); cached_data_.animation_scales.resize(transform_count); for (auto& animation_scale : cached_data_.animation_scales) - animation_scale.update_number = -1; + animation_scale.update_number = kInvalidUpdateNumber; cached_data_.draw_transforms.resize(transform_count, std::vector<DrawTransformData>(1)); for (auto& draw_transforms_for_id : cached_data_.draw_transforms) { draw_transforms_for_id.resize(1); - draw_transforms_for_id[0].update_number = -1; + draw_transforms_for_id[0].update_number = kInvalidUpdateNumber; draw_transforms_for_id[0].target_id = EffectTree::kInvalidNodeId; } } diff --git a/chromium/cc/trees/property_tree.h b/chromium/cc/trees/property_tree.h index 8b60fde7c87..2bb36062737 100644 --- a/chromium/cc/trees/property_tree.h +++ b/chromium/cc/trees/property_tree.h @@ -21,7 +21,7 @@ #include "cc/input/scroll_snap_data.h" #include "cc/paint/element_id.h" #include "cc/paint/filter_operations.h" -#include "cc/trees/mutator_host_client.h" +#include "cc/trees/mutator_host.h" #include "cc/trees/sticky_position_constraint.h" #include "ui/gfx/geometry/rect_f.h" #include "ui/gfx/geometry/scroll_offset.h" @@ -539,40 +539,26 @@ class CC_EXPORT ScrollTree final : public PropertyTree<ScrollNode> { bool use_fractional_deltas); }; +constexpr int kInvalidUpdateNumber = -1; + struct AnimationScaleData { - // Variable used to invalidate cached animation scale data when transform tree + // Variable used to invalidate cached maximum scale data when transform tree // updates. - int update_number; - - // The maximum scale that this node's |to_target| transform will have during - // current animations, considering only scales at keyframes not incuding the - // starting keyframe of each animation. - float combined_maximum_animation_target_scale; - - // The maximum scale that this node's |to_target| transform will have during - // current animations, considering only the starting scale of each animation. - float combined_starting_animation_scale; + int update_number = kInvalidUpdateNumber; - bool to_screen_has_scale_animation; - - AnimationScaleData() { - update_number = -1; - combined_maximum_animation_target_scale = 0.f; - combined_starting_animation_scale = 0.f; - to_screen_has_scale_animation = false; - } -}; + // The maximum scale that this node's |to_screen| transform will have during + // current animations of this node and its ancestors, or the current scale of + // this node's |to_screen| transform if there are no animations. + float maximum_to_screen_scale = kInvalidScale; -struct CombinedAnimationScale { - float maximum_animation_scale; - float starting_animation_scale; + // Whether |maximum_to_screen_scale| is affected by any animation of this + // node or its ancestors. A scale animation having maximum scale of 1 is + // treated as not affecting |maximum_to_screen_scale|. + bool affected_by_animation_scale = false; - CombinedAnimationScale(float maximum, float starting) - : maximum_animation_scale(maximum), starting_animation_scale(starting) {} - bool operator==(const CombinedAnimationScale& other) const { - return maximum_animation_scale == other.maximum_animation_scale && - starting_animation_scale == other.starting_animation_scale; - } + // Whether |maximum_to_screen_scale| is affected by any non-calculatable + // scale. + bool affected_by_invalid_scale = false; }; struct DrawTransforms { @@ -601,17 +587,12 @@ struct DrawTransforms { }; struct DrawTransformData { - int update_number; - int target_id; - - DrawTransforms transforms; + int update_number = kInvalidUpdateNumber; + int target_id = EffectTree::kInvalidNodeId; // TODO(sunxd): Move screen space transforms here if it can improve // performance. - DrawTransformData() - : update_number(-1), - target_id(EffectTree::kInvalidNodeId), - transforms(gfx::Transform(), gfx::Transform()) {} + DrawTransforms transforms{gfx::Transform(), gfx::Transform()}; }; struct ConditionalClip { @@ -620,10 +601,8 @@ struct ConditionalClip { }; struct ClipRectData { - int target_id; + int target_id = ClipTree::kInvalidNodeId; ConditionalClip clip; - - ClipRectData() : target_id(-1) {} }; struct PropertyTreesCachedData { @@ -686,9 +665,7 @@ class CC_EXPORT PropertyTrees final { const PropertyAnimationState& mask, const PropertyAnimationState& state, bool check_node_existence); - void AnimationScalesChanged(ElementId element_id, - float maximum_scale, - float starting_scale); + void MaximumAnimationScaleChanged(ElementId element_id, float maximum_scale); void SetInnerViewportContainerBoundsDelta(gfx::Vector2dF bounds_delta); void SetOuterViewportContainerBoundsDelta(gfx::Vector2dF bounds_delta); void UpdateChangeTracking(); @@ -711,11 +688,14 @@ class CC_EXPORT PropertyTrees final { void AsValueInto(base::trace_event::TracedValue* value) const; std::string ToString() const; - CombinedAnimationScale GetAnimationScales(int transform_node_id, - LayerTreeImpl* layer_tree_impl); - void SetAnimationScalesForTesting(int transform_id, - float maximum_animation_scale, - float starting_animation_scale); + bool AnimationScaleCacheIsInvalid(int transform_id) const; + float MaximumAnimationToScreenScale(int transform_id); + bool AnimationAffectedByInvalidScale(int transform_id); + + void SetMaximumAnimationToScreenScaleForTesting( + int transform_id, + float maximum_scale, + bool affected_by_invalid_scale); bool GetToTarget(int transform_id, int effect_id, @@ -738,6 +718,8 @@ class CC_EXPORT PropertyTrees final { gfx::Vector2dF inner_viewport_container_bounds_delta_; gfx::Vector2dF outer_viewport_container_bounds_delta_; + const AnimationScaleData& GetAnimationScaleData(int transform_id); + // GetDrawTransforms may change the value of cached_data_. DrawTransforms& GetDrawTransforms(int transform_id, int effect_id) const; DrawTransformData& FetchDrawTransformsDataFromCache(int transform_id, diff --git a/chromium/cc/trees/property_tree_builder.cc b/chromium/cc/trees/property_tree_builder.cc index d0f5851130c..5bd4eeeae24 100644 --- a/chromium/cc/trees/property_tree_builder.cc +++ b/chromium/cc/trees/property_tree_builder.cc @@ -39,6 +39,7 @@ struct DataForRecursion { int scroll_tree_parent; int closest_ancestor_with_cached_render_surface; int closest_ancestor_with_copy_request; + int closest_ancestor_being_captured; SkColor safe_opaque_background_color; bool animation_axis_aligned_since_render_target; bool not_axis_aligned_since_last_clip; @@ -137,13 +138,9 @@ bool HasPotentiallyRunningTransformAnimation(const MutatorHost& host, layer->element_id(), layer->GetElementTypeForAnimation()); } -void GetAnimationScales(const MutatorHost& host, - Layer* layer, - float* maximum_scale, - float* starting_scale) { - return host.GetAnimationScales(layer->element_id(), - layer->GetElementTypeForAnimation(), - maximum_scale, starting_scale); +float MaximumAnimationScale(const MutatorHost& host, Layer* layer) { + return host.MaximumScale(layer->element_id(), + layer->GetElementTypeForAnimation()); } bool AnimationsPreserveAxisAlignment(const MutatorHost& host, Layer* layer) { @@ -287,8 +284,7 @@ bool PropertyTreeBuilderContext::AddTransformNodeIfNeeded( node->has_potential_animation = has_potentially_animated_transform; node->is_currently_animating = TransformIsAnimating(mutator_host_, layer); - GetAnimationScales(mutator_host_, layer, &node->maximum_animation_scale, - &node->starting_animation_scale); + node->maximum_animation_scale = MaximumAnimationScale(mutator_host_, layer); node->scroll_offset = layer->scroll_offset(); @@ -389,6 +385,9 @@ RenderSurfaceReason ComputeRenderSurfaceReason(const MutatorHost& mutator_host, if (layer->mirror_count()) return RenderSurfaceReason::kMirrored; + if (layer->subtree_capture_id().is_valid()) + return RenderSurfaceReason::kSubtreeIsBeingCaptured; + return RenderSurfaceReason::kNone; } @@ -455,6 +454,7 @@ bool PropertyTreeBuilderContext::AddEffectNodeIfNeeded( node->stable_id = layer->id(); node->opacity = layer->opacity(); node->blend_mode = layer->blend_mode(); + node->subtree_capture_id = layer->subtree_capture_id(); node->cache_render_surface = layer->cache_render_surface(); node->has_copy_request = layer->HasCopyRequest(); node->filters = layer->filters(); @@ -484,6 +484,10 @@ bool PropertyTreeBuilderContext::AddEffectNodeIfNeeded( layer->HasCopyRequest() ? node_id : data_from_ancestor.closest_ancestor_with_copy_request; + node->closest_ancestor_being_captured_id = + layer->subtree_capture_id().is_valid() + ? node_id + : data_from_ancestor.closest_ancestor_being_captured; if (layer->HasRoundedCorner()) { // This is currently in the local space of the layer and hence in an invalid @@ -518,6 +522,8 @@ bool PropertyTreeBuilderContext::AddEffectNodeIfNeeded( node->closest_ancestor_with_cached_render_surface_id; data_for_children->closest_ancestor_with_copy_request = node->closest_ancestor_with_copy_request_id; + data_for_children->closest_ancestor_being_captured = + node->closest_ancestor_being_captured_id; data_for_children->effect_tree_parent = node_id; layer->SetEffectTreeIndex(node_id); @@ -717,6 +723,8 @@ void PropertyTreeBuilderContext::BuildPropertyTrees() { EffectTree::kInvalidNodeId; data_for_recursion.closest_ancestor_with_copy_request = EffectTree::kInvalidNodeId; + data_for_recursion.closest_ancestor_being_captured = + EffectTree::kInvalidNodeId; data_for_recursion.compound_transform_since_render_target = gfx::Transform(); data_for_recursion.animation_axis_aligned_since_render_target = true; data_for_recursion.not_axis_aligned_since_last_clip = false; diff --git a/chromium/cc/trees/property_tree_builder_unittest.cc b/chromium/cc/trees/property_tree_builder_unittest.cc index c824607608b..2576916ed38 100644 --- a/chromium/cc/trees/property_tree_builder_unittest.cc +++ b/chromium/cc/trees/property_tree_builder_unittest.cc @@ -7,7 +7,7 @@ #include <memory> #include <utility> -#include "cc/animation/keyframed_animation_curve.h" +#include "cc/animation/filter_animation_curve.h" #include "cc/layers/layer.h" #include "cc/layers/layer_impl.h" #include "cc/layers/picture_layer.h" @@ -17,6 +17,7 @@ #include "cc/test/fake_content_layer_client.h" #include "cc/test/layer_tree_impl_test_base.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/gfx/animation/keyframe/keyframed_animation_curve.h" #include "ui/gfx/geometry/size_conversions.h" #include "ui/gfx/geometry/vector2d_conversions.h" #include "ui/gfx/transform.h" @@ -552,8 +553,9 @@ TEST_F(PropertyTreeBuilderTest, DelayedFilterAnimationCreatesRenderSurface) { FilterKeyframe::Create(base::TimeDelta(), start_filters, nullptr)); curve->AddKeyframe(FilterKeyframe::Create( base::TimeDelta::FromMilliseconds(100), end_filters, nullptr)); - std::unique_ptr<KeyframeModel> keyframe_model = - KeyframeModel::Create(std::move(curve), 0, 1, TargetProperty::FILTER); + std::unique_ptr<KeyframeModel> keyframe_model = KeyframeModel::Create( + std::move(curve), 0, 1, + KeyframeModel::TargetPropertyId(TargetProperty::FILTER)); keyframe_model->set_fill_mode(KeyframeModel::FillMode::NONE); keyframe_model->set_time_offset(base::TimeDelta::FromMilliseconds(-1000)); diff --git a/chromium/cc/trees/proxy.h b/chromium/cc/trees/proxy.h index 5c886bb152f..44c40243afc 100644 --- a/chromium/cc/trees/proxy.h +++ b/chromium/cc/trees/proxy.h @@ -79,8 +79,6 @@ class CC_EXPORT Proxy { virtual void SetPaintWorkletLayerPainter( std::unique_ptr<PaintWorkletLayerPainter> painter) = 0; - virtual bool SupportsImplScrolling() const = 0; - virtual void UpdateBrowserControlsState(BrowserControlsState constraints, BrowserControlsState current, bool animate) = 0; diff --git a/chromium/cc/trees/proxy_impl.cc b/chromium/cc/trees/proxy_impl.cc index 13cccd11026..a67c14b6b64 100644 --- a/chromium/cc/trees/proxy_impl.cc +++ b/chromium/cc/trees/proxy_impl.cc @@ -20,6 +20,7 @@ #include "cc/benchmarks/benchmark_instrumentation.h" #include "cc/input/browser_controls_offset_manager.h" #include "cc/metrics/compositor_timing_history.h" +#include "cc/metrics/jank_injector.h" #include "cc/paint/paint_worklet_layer_painter.h" #include "cc/trees/compositor_commit_data.h" #include "cc/trees/layer_tree_frame_sink.h" @@ -250,6 +251,11 @@ bool ProxyImpl::IsInSynchronousComposite() const { return false; } +void ProxyImpl::FrameSinksToThrottleUpdated( + const base::flat_set<viz::FrameSinkId>& ids) { + NOTREACHED(); +} + void ProxyImpl::NotifyReadyToCommitOnImpl( CompletionEvent* completion, LayerTreeHost* layer_tree_host, @@ -516,13 +522,17 @@ void ProxyImpl::NotifyImageDecodeRequestFinished() { void ProxyImpl::DidPresentCompositorFrameOnImplThread( uint32_t frame_token, - std::vector<LayerTreeHost::PresentationTimeCallback> callbacks, + PresentationTimeCallbackBuffer::PendingCallbacks activated, const viz::FrameTimingDetails& details) { + auto main_thread_callbacks = std::move(activated.main_thread_callbacks); + host_impl_->NotifyDidPresentCompositorFrameOnImplThread( + frame_token, std::move(activated), details); + MainThreadTaskRunner()->PostTask( - FROM_HERE, - base::BindOnce(&ProxyMain::DidPresentCompositorFrame, - proxy_main_weak_ptr_, frame_token, std::move(callbacks), - details.presentation_feedback)); + FROM_HERE, base::BindOnce(&ProxyMain::DidPresentCompositorFrame, + proxy_main_weak_ptr_, frame_token, + std::move(main_thread_callbacks), + details.presentation_feedback)); if (scheduler_) scheduler_->DidPresentCompositorFrame(frame_token, details); } @@ -590,6 +600,11 @@ void ProxyImpl::WillNotReceiveBeginFrame() { void ProxyImpl::ScheduledActionSendBeginMainFrame( const viz::BeginFrameArgs& args) { DCHECK(IsImplThread()); + + if (is_jank_injection_enabled_ && host_impl_->CanInjectJankOnMain()) { + jank_injector_.ScheduleJankIfNeeded(args, MainThreadTaskRunner()); + } + benchmark_instrumentation::ScopedBeginFrameTask begin_frame_task( benchmark_instrumentation::kSendBeginFrame, args.frame_id.sequence_number); @@ -805,6 +820,7 @@ base::SingleThreadTaskRunner* ProxyImpl::MainThreadTaskRunner() { void ProxyImpl::SetSourceURL(ukm::SourceId source_id, const GURL& url) { DCHECK(IsImplThread()); + is_jank_injection_enabled_ = JankInjector::IsEnabled(url); host_impl_->SetActiveURL(url, source_id); } diff --git a/chromium/cc/trees/proxy_impl.h b/chromium/cc/trees/proxy_impl.h index 815935a5d41..05eb84ed3eb 100644 --- a/chromium/cc/trees/proxy_impl.h +++ b/chromium/cc/trees/proxy_impl.h @@ -12,6 +12,7 @@ #include "cc/base/completion_event.h" #include "cc/base/delayed_unique_notifier.h" #include "cc/input/browser_controls_state.h" +#include "cc/metrics/jank_injector.h" #include "cc/scheduler/scheduler.h" #include "cc/trees/layer_tree_host_impl.h" @@ -24,6 +25,7 @@ class LayerTreeHost; class ProxyMain; class RenderFrameMetadataObserver; +class JankInjector; class ScopedCompletionEvent; // This class aggregates all the interactions that the main side of the @@ -115,7 +117,7 @@ class CC_EXPORT ProxyImpl : public LayerTreeHostImplClient, void NotifyImageDecodeRequestFinished() override; void DidPresentCompositorFrameOnImplThread( uint32_t frame_token, - std::vector<LayerTreeHost::PresentationTimeCallback> callbacks, + PresentationTimeCallbackBuffer::PendingCallbacks activated, const viz::FrameTimingDetails& details) override; void NotifyAnimationWorkletStateChange( AnimationWorkletMutationState state, @@ -126,6 +128,9 @@ class CC_EXPORT ProxyImpl : public LayerTreeHostImplClient, void DidObserveFirstScrollDelay( base::TimeDelta first_scroll_delay, base::TimeTicks first_scroll_timestamp) override; + bool IsInSynchronousComposite() const override; + void FrameSinksToThrottleUpdated( + const base::flat_set<viz::FrameSinkId>& id) override; // SchedulerClient implementation bool WillBeginImplFrame(const viz::BeginFrameArgs& args) override; @@ -149,7 +154,6 @@ class CC_EXPORT ProxyImpl : public LayerTreeHostImplClient, base::TimeTicks time) override; void FrameIntervalUpdated(base::TimeDelta interval) override {} bool HasCustomPropertyAnimations() const override; - bool IsInSynchronousComposite() const override; DrawResult DrawInternal(bool forced_draw); @@ -178,6 +182,8 @@ class CC_EXPORT ProxyImpl : public LayerTreeHostImplClient, bool send_compositor_frame_ack_; + JankInjector jank_injector_; + TreePriority last_raster_priority_; TaskRunnerProvider* task_runner_provider_; @@ -190,6 +196,8 @@ class CC_EXPORT ProxyImpl : public LayerTreeHostImplClient, BlockedMainCommitOnly main_thread_blocked_commit_vars_unsafe_; BlockedMainCommitOnly& blocked_main_commit(); + bool is_jank_injection_enabled_ = false; + // Used to post tasks to ProxyMain on the main thread. base::WeakPtr<ProxyMain> proxy_main_weak_ptr_; diff --git a/chromium/cc/trees/proxy_main.cc b/chromium/cc/trees/proxy_main.cc index 0fa28a69c06..cfafe9c81b5 100644 --- a/chromium/cc/trees/proxy_main.cc +++ b/chromium/cc/trees/proxy_main.cc @@ -606,10 +606,6 @@ void ProxyMain::SetPaintWorkletLayerPainter( base::Unretained(proxy_impl_.get()), std::move(painter))); } -bool ProxyMain::SupportsImplScrolling() const { - return true; -} - bool ProxyMain::MainFrameWillHappenForTesting() { DCHECK(IsMainThread()); bool main_frame_will_happen = false; diff --git a/chromium/cc/trees/proxy_main.h b/chromium/cc/trees/proxy_main.h index d5bc7291989..186de9d07b6 100644 --- a/chromium/cc/trees/proxy_main.h +++ b/chromium/cc/trees/proxy_main.h @@ -5,6 +5,9 @@ #ifndef CC_TREES_PROXY_MAIN_H_ #define CC_TREES_PROXY_MAIN_H_ +#include <memory> +#include <vector> + #include "cc/cc_export.h" #include "cc/input/browser_controls_state.h" #include "cc/trees/layer_tree_host.h" @@ -89,7 +92,6 @@ class CC_EXPORT ProxyMain : public Proxy { bool CommitRequested() const override; void Start() override; void Stop() override; - bool SupportsImplScrolling() const override; void SetMutator(std::unique_ptr<LayerTreeMutator> mutator) override; void SetPaintWorkletLayerPainter( std::unique_ptr<PaintWorkletLayerPainter> painter) override; diff --git a/chromium/cc/trees/render_frame_metadata.cc b/chromium/cc/trees/render_frame_metadata.cc index 268b1bf8877..0efa3496688 100644 --- a/chromium/cc/trees/render_frame_metadata.cc +++ b/chromium/cc/trees/render_frame_metadata.cc @@ -29,6 +29,7 @@ bool RenderFrameMetadata::operator==(const RenderFrameMetadata& other) const { is_scroll_offset_at_top == other.is_scroll_offset_at_top && selection == other.selection && is_mobile_optimized == other.is_mobile_optimized && + delegated_ink_metadata == other.delegated_ink_metadata && device_scale_factor == other.device_scale_factor && viewport_size_in_pixels == other.viewport_size_in_pixels && page_scale_factor == other.page_scale_factor && diff --git a/chromium/cc/trees/render_frame_metadata.h b/chromium/cc/trees/render_frame_metadata.h index b1d8efd6413..589a022fe40 100644 --- a/chromium/cc/trees/render_frame_metadata.h +++ b/chromium/cc/trees/render_frame_metadata.h @@ -20,6 +20,31 @@ namespace cc { +// Contains information to assist in making a decision about forwarding +// pointerevents to viz for use in a delegated ink trail. +struct DelegatedInkBrowserMetadata { + public: + DelegatedInkBrowserMetadata() = default; + explicit DelegatedInkBrowserMetadata(bool hovering) + : delegated_ink_is_hovering(hovering) {} + + bool operator==(const DelegatedInkBrowserMetadata& other) const { + return delegated_ink_is_hovering == other.delegated_ink_is_hovering; + } + + bool operator!=(const DelegatedInkBrowserMetadata& other) const { + return !operator==(other); + } + + // Flag used to indicate the state of the hovering on the pointerevent that + // the delegated ink metadata was created from. If this state does not match + // the point under consideration to send to viz, it won't be sent. As soon + // as it matches again the point will be sent, regardless of if the renderer + // has processed the point that didn't match yet or not. It is true when + // hovering, false otherwise. + bool delegated_ink_is_hovering; +}; + class CC_EXPORT RenderFrameMetadata { public: RenderFrameMetadata(); @@ -55,10 +80,12 @@ class CC_EXPORT RenderFrameMetadata { // are the same). bool is_mobile_optimized = false; - // Flag used to notify the browser process to start or stop forwarding points - // to viz for use in a delegated ink trail. True the entire time points should - // be forwarded, and forwarding stops as soon as it is false again. - bool has_delegated_ink_metadata = false; + // Existence of this flag informs the browser process to start forwarding + // points to viz for use in a delegated ink trail. It contains more + // information to be used in making the forwarding decision. It exists the + // entire time points could be forwarded, and forwarding must stop as soon as + // it is null. + base::Optional<DelegatedInkBrowserMetadata> delegated_ink_metadata; // The device scale factor used to generate a CompositorFrame. float device_scale_factor = 1.f; diff --git a/chromium/cc/trees/single_thread_proxy.cc b/chromium/cc/trees/single_thread_proxy.cc index f2050b82334..628454e3b90 100644 --- a/chromium/cc/trees/single_thread_proxy.cc +++ b/chromium/cc/trees/single_thread_proxy.cc @@ -523,11 +523,19 @@ void SingleThreadProxy::NotifyImageDecodeRequestFinished() { void SingleThreadProxy::DidPresentCompositorFrameOnImplThread( uint32_t frame_token, - std::vector<LayerTreeHost::PresentationTimeCallback> callbacks, + PresentationTimeCallbackBuffer::PendingCallbacks callbacks, const viz::FrameTimingDetails& details) { - layer_tree_host_->DidPresentCompositorFrame(frame_token, std::move(callbacks), - details.presentation_feedback); - + std::vector<LayerTreeHost::PresentationTimeCallback> main_thread_callbacks = + std::move(callbacks.main_thread_callbacks); + DebugScopedSetImplThread impl(task_runner_provider_); + host_impl_->NotifyDidPresentCompositorFrameOnImplThread( + frame_token, std::move(callbacks), details); + { + DebugScopedSetMainThread main(task_runner_provider_); + layer_tree_host_->DidPresentCompositorFrame( + frame_token, std::move(main_thread_callbacks), + details.presentation_feedback); + } if (scheduler_on_impl_thread_) { scheduler_on_impl_thread_->DidPresentCompositorFrame(frame_token, details); } @@ -547,7 +555,26 @@ void SingleThreadProxy::NotifyPaintWorkletStateChange( void SingleThreadProxy::NotifyThroughputTrackerResults( CustomTrackerResults results) { - layer_tree_host_->NotifyThroughputTrackerResults(std::move(results)); + DCHECK(task_runner_provider_->IsImplThread()); + // This method is called from ImplThread side so post a task to + // MainThread. This is necessary because the throughput tracker callbacks are + // supposed to be executed on MainThread side, which may invok compositor's + // method that expected to be executed on MainThread. + task_runner_provider_->MainThreadTaskRunner()->PostTask( + FROM_HERE, + base::BindOnce( + &SingleThreadProxy::NotifyThroughputTrackerResultsOnMainThread, + weak_factory_.GetWeakPtr(), std::move(results))); +} + +bool SingleThreadProxy::IsInSynchronousComposite() const { + return inside_synchronous_composite_; +} + +void SingleThreadProxy::FrameSinksToThrottleUpdated( + const base::flat_set<viz::FrameSinkId>& ids) { + DebugScopedSetMainThread main(task_runner_provider_); + single_thread_client_->FrameSinksToThrottleUpdated(ids); } void SingleThreadProxy::RequestBeginMainFrameNotExpected(bool new_state) { @@ -557,10 +584,6 @@ void SingleThreadProxy::RequestBeginMainFrameNotExpected(bool new_state) { } } -bool SingleThreadProxy::IsInSynchronousComposite() const { - return inside_synchronous_composite_; -} - void SingleThreadProxy::CompositeImmediatelyForTest( base::TimeTicks frame_begin_time, bool raster) { @@ -640,10 +663,6 @@ void SingleThreadProxy::CompositeImmediatelyForTest( } } -bool SingleThreadProxy::SupportsImplScrolling() const { - return false; -} - bool SingleThreadProxy::ShouldComposite() const { DCHECK(task_runner_provider_->IsImplThread()); return host_impl_->visible() && host_impl_->CanDraw(); @@ -963,6 +982,7 @@ void SingleThreadProxy::ScheduledActionPerformImplSideInvalidation() { void SingleThreadProxy::DidFinishImplFrame( const viz::BeginFrameArgs& last_activated_args) { + DebugScopedSetImplThread impl(task_runner_provider_); host_impl_->DidFinishImplFrame(last_activated_args); #if DCHECK_IS_ON() DCHECK(inside_impl_frame_) @@ -985,4 +1005,9 @@ void SingleThreadProxy::DidReceiveCompositorFrameAck() { layer_tree_host_->DidReceiveCompositorFrameAck(); } +void SingleThreadProxy::NotifyThroughputTrackerResultsOnMainThread( + CustomTrackerResults results) { + layer_tree_host_->NotifyThroughputTrackerResults(std::move(results)); +} + } // namespace cc diff --git a/chromium/cc/trees/single_thread_proxy.h b/chromium/cc/trees/single_thread_proxy.h index 8b0960dbd4f..246df6800cb 100644 --- a/chromium/cc/trees/single_thread_proxy.h +++ b/chromium/cc/trees/single_thread_proxy.h @@ -10,6 +10,7 @@ #include <vector> #include "base/cancelable_callback.h" +#include "base/containers/flat_set.h" #include "base/time/time.h" #include "cc/scheduler/scheduler.h" #include "cc/trees/layer_tree_host_impl.h" @@ -62,7 +63,6 @@ class CC_EXPORT SingleThreadProxy : public Proxy, void SetMutator(std::unique_ptr<LayerTreeMutator> mutator) override; void SetPaintWorkletLayerPainter( std::unique_ptr<PaintWorkletLayerPainter> painter) override; - bool SupportsImplScrolling() const override; bool MainFrameWillHappenForTesting() override; void SetSourceURL(ukm::SourceId source_id, const GURL& url) override { // Single-threaded mode is only for browser compositing and for renderers in @@ -132,7 +132,7 @@ class CC_EXPORT SingleThreadProxy : public Proxy, void NotifyImageDecodeRequestFinished() override; void DidPresentCompositorFrameOnImplThread( uint32_t frame_token, - std::vector<LayerTreeHost::PresentationTimeCallback> callbacks, + PresentationTimeCallbackBuffer::PendingCallbacks callbacks, const viz::FrameTimingDetails& details) override; void NotifyAnimationWorkletStateChange( AnimationWorkletMutationState state, @@ -141,6 +141,8 @@ class CC_EXPORT SingleThreadProxy : public Proxy, Scheduler::PaintWorkletState state) override; void NotifyThroughputTrackerResults(CustomTrackerResults results) override; bool IsInSynchronousComposite() const override; + void FrameSinksToThrottleUpdated( + const base::flat_set<viz::FrameSinkId>& ids) override; void RequestNewLayerTreeFrameSink(); @@ -178,6 +180,7 @@ class CC_EXPORT SingleThreadProxy : public Proxy, void IssueImageDecodeFinishedCallbacks(); void DidReceiveCompositorFrameAck(); + void NotifyThroughputTrackerResultsOnMainThread(CustomTrackerResults results); // Accessed on main thread only. LayerTreeHost* layer_tree_host_; diff --git a/chromium/cc/trees/target_property.cc b/chromium/cc/trees/target_property.cc index ed206255730..96f8c3bd7cd 100644 --- a/chromium/cc/trees/target_property.cc +++ b/chromium/cc/trees/target_property.cc @@ -4,16 +4,19 @@ #include "cc/trees/target_property.h" +#include "ui/gfx/animation/keyframe/target_property.h" + namespace cc { -static_assert(TargetProperty::LAST_TARGET_PROPERTY < kMaxTargetPropertyIndex, +static_assert(TargetProperty::LAST_TARGET_PROPERTY < + gfx::kMaxTargetPropertyIndex, "The number of cc target properties has exceeded the capacity of" " TargetProperties"); // bitset will use a multiple of the architecture int size, which is at least 32 // bits so make it explicit to have as many properties as fit into the memory // used. -static_assert(kMaxTargetPropertyIndex % (8 * sizeof(uint32_t)) == 0, +static_assert(gfx::kMaxTargetPropertyIndex % (8 * sizeof(uint32_t)) == 0, "The maximum number of target properties should be a multiple of " "sizeof(uint32_t)"); diff --git a/chromium/cc/trees/target_property.h b/chromium/cc/trees/target_property.h index 407090f27c4..a60b371119c 100644 --- a/chromium/cc/trees/target_property.h +++ b/chromium/cc/trees/target_property.h @@ -5,14 +5,10 @@ #ifndef CC_TREES_TARGET_PROPERTY_H_ #define CC_TREES_TARGET_PROPERTY_H_ -#include <bitset> - #include "base/containers/flat_map.h" namespace cc { -static constexpr size_t kMaxTargetPropertyIndex = 32u; - namespace TargetProperty { // Must be zero-based as this will be stored in a bitset. @@ -24,6 +20,9 @@ enum Type { BACKGROUND_COLOR, BOUNDS, CSS_CUSTOM_PROPERTY, + // This is used for the set of properties whose animation use paint worklet + // infra. The value of the animation represents its progress. + NATIVE_PROPERTY, BACKDROP_FILTER, // These sentinels must be last FIRST_TARGET_PROPERTY = TRANSFORM, @@ -32,9 +31,6 @@ enum Type { } // namespace TargetProperty -// A set of target properties. -using TargetProperties = std::bitset<kMaxTargetPropertyIndex>; - // A map of target property to ElementId. // flat_map was chosen because there are expected to be relatively few entries // in the map. For low number of entries, flat_map is known to perform better diff --git a/chromium/cc/trees/throttle_decider.cc b/chromium/cc/trees/throttle_decider.cc new file mode 100644 index 00000000000..305d0dd3881 --- /dev/null +++ b/chromium/cc/trees/throttle_decider.cc @@ -0,0 +1,92 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/trees/throttle_decider.h" + +#include <vector> + +#include "components/viz/common/quads/compositor_render_pass_draw_quad.h" +#include "components/viz/common/quads/surface_draw_quad.h" +#include "components/viz/common/surfaces/surface_range.h" + +namespace cc { + +ThrottleDecider::ThrottleDecider() = default; + +ThrottleDecider::~ThrottleDecider() = default; + +void ThrottleDecider::Prepare() { + last_ids_.swap(ids_); + id_to_pass_map_.clear(); + ids_.clear(); +} + +void ThrottleDecider::ProcessRenderPass( + const viz::CompositorRenderPass& render_pass) { + bool foreground_blurred = + render_pass.filters.HasFilterOfType(FilterOperation::BLUR); + std::vector<gfx::RectF> blur_backdrop_filter_bounds; + for (viz::QuadList::ConstIterator it = render_pass.quad_list.begin(); + it != render_pass.quad_list.end(); ++it) { + const viz::DrawQuad* quad = *it; + if (quad->material == viz::DrawQuad::Material::kCompositorRenderPass) { + // If the quad render pass has a blur backdrop filter without a mask, add + // the filter bounds to the bounds list. + const auto* render_pass_quad = + viz::CompositorRenderPassDrawQuad::MaterialCast(quad); + auto found = id_to_pass_map_.find(render_pass_quad->render_pass_id); + if (found == id_to_pass_map_.end()) { + // It is possible that this function is called when the render passes in + // a frame haven't been cleaned up yet. A RPDQ can possibly refer to an + // invalid render pass. + continue; + } + const auto& child_rp = *found->second; + if (child_rp.backdrop_filters.HasFilterOfType(FilterOperation::BLUR) && + render_pass_quad->resources + .ids[viz::RenderPassDrawQuadInternal::kMaskResourceIdIndex] == + viz::kInvalidResourceId) { + gfx::RectF blur_bounds(child_rp.output_rect); + if (child_rp.backdrop_filter_bounds) + blur_bounds.Intersect(child_rp.backdrop_filter_bounds->rect()); + quad->shared_quad_state->quad_to_target_transform.TransformRect( + &blur_bounds); + if (quad->shared_quad_state->is_clipped) { + blur_bounds.Intersect(gfx::RectF(quad->shared_quad_state->clip_rect)); + } + blur_backdrop_filter_bounds.push_back(blur_bounds); + } + } else if (quad->material == viz::DrawQuad::Material::kSurfaceContent) { + bool inside_backdrop_filter_bounds = false; + if (!foreground_blurred && !blur_backdrop_filter_bounds.empty()) { + gfx::RectF rect_in_target_space(quad->visible_rect); + quad->shared_quad_state->quad_to_target_transform.TransformRect( + &rect_in_target_space); + if (quad->shared_quad_state->is_clipped) { + rect_in_target_space.Intersect( + gfx::RectF(quad->shared_quad_state->clip_rect)); + } + + for (const gfx::RectF& blur_bounds : blur_backdrop_filter_bounds) { + if (blur_bounds.Contains(rect_in_target_space)) { + inside_backdrop_filter_bounds = true; + break; + } + } + } + const auto* surface_quad = viz::SurfaceDrawQuad::MaterialCast(quad); + const viz::SurfaceRange& range = surface_quad->surface_range; + DCHECK(range.IsValid()); + if (foreground_blurred || inside_backdrop_filter_bounds) + ids_.insert(range.end().frame_sink_id()); + } + } + id_to_pass_map_.emplace(render_pass.id, &render_pass); +} + +bool ThrottleDecider::HasThrottlingChanged() const { + return ids_ != last_ids_; +} + +} // namespace cc diff --git a/chromium/cc/trees/throttle_decider.h b/chromium/cc/trees/throttle_decider.h new file mode 100644 index 00000000000..d41479a423c --- /dev/null +++ b/chromium/cc/trees/throttle_decider.h @@ -0,0 +1,48 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CC_TREES_THROTTLE_DECIDER_H_ +#define CC_TREES_THROTTLE_DECIDER_H_ + +#include "base/containers/flat_map.h" +#include "base/containers/flat_set.h" +#include "cc/cc_export.h" +#include "components/viz/common/quads/compositor_render_pass.h" +#include "components/viz/common/surfaces/frame_sink_id.h" + +namespace cc { + +// This class is used to decide if any frame sinks in a render pass list +// satisfies the compositing-based criteria to be throttled. +class CC_EXPORT ThrottleDecider { + public: + ThrottleDecider(); + ~ThrottleDecider(); + ThrottleDecider(const ThrottleDecider&) = delete; + ThrottleDecider& operator=(const ThrottleDecider&) = delete; + + // This function should be called at the beginning of each time when a render + // pass list is about to be processed. + void Prepare(); + // Go through the quads in |render_pass| and decide for each embedded surface + // in SurfaceDrawQuad if it can be throttled. This is a simple version where + // intersection calculation of surface/quad rects are confined to the render + // pass's constituent quads. + void ProcessRenderPass(const viz::CompositorRenderPass& render_pass); + bool HasThrottlingChanged() const; + const base::flat_set<viz::FrameSinkId>& ids() const { return ids_; } + + private: + base::flat_map<viz::CompositorRenderPassId, const viz::CompositorRenderPass*> + id_to_pass_map_; + // Ids of frame sinks that are qualified for throttling. + base::flat_set<viz::FrameSinkId> ids_; + // Ids of frame sinks that were qualified for throttling from last + // compositing. + base::flat_set<viz::FrameSinkId> last_ids_; +}; + +} // namespace cc + +#endif // CC_TREES_THROTTLE_DECIDER_H_ diff --git a/chromium/cc/trees/throttle_decider_unittest.cc b/chromium/cc/trees/throttle_decider_unittest.cc new file mode 100644 index 00000000000..ebb1102d1d5 --- /dev/null +++ b/chromium/cc/trees/throttle_decider_unittest.cc @@ -0,0 +1,111 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/trees/throttle_decider.h" +#include "components/viz/common/quads/compositor_render_pass_draw_quad.h" +#include "components/viz/common/quads/surface_draw_quad.h" +#include "components/viz/common/surfaces/local_surface_id.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace cc { + +class ThrottleDeciderTest : public ::testing::Test { + protected: + void RunThrottleDecider(const viz::CompositorRenderPassList& render_passes) { + throttle_decider_.Prepare(); + for (auto& render_pass : render_passes) { + throttle_decider_.ProcessRenderPass(*render_pass.get()); + } + } + const base::flat_set<viz::FrameSinkId>& GetFrameSinksToThrottle() const { + return throttle_decider_.ids(); + } + ThrottleDecider throttle_decider_; +}; + +TEST_F(ThrottleDeciderTest, BackdropFilter) { + // Create two render passes. The first render pass has a blur backdrop filter. + // The second render pass has two quads: a RPDQ referencing the first render + // pass and a surface quad. + viz::CompositorRenderPassList render_passes; + render_passes.push_back(viz::CompositorRenderPass::Create()); + render_passes.push_back(viz::CompositorRenderPass::Create()); + + gfx::Rect render_pass_rect(0, 0, 100, 100); + gfx::Rect quad_rect(0, 0, 100, 100); + viz::CompositorRenderPassId id1{1u}; + viz::CompositorRenderPassId id2{2u}; + + render_passes[0]->SetNew(id1, render_pass_rect, gfx::Rect(), + gfx::Transform()); + render_passes[0]->backdrop_filters.Append( + FilterOperation::CreateBlurFilter(5.0)); + render_passes[1]->SetNew(id2, render_pass_rect, gfx::Rect(), + gfx::Transform()); + + auto* rpdq = + render_passes[1] + ->CreateAndAppendDrawQuad<viz::CompositorRenderPassDrawQuad>(); + rpdq->material = viz::DrawQuad::Material::kCompositorRenderPass; + rpdq->render_pass_id = id1; + viz::SharedQuadState sqs1; + rpdq->shared_quad_state = &sqs1; + rpdq->rect = quad_rect; + + viz::FrameSinkId frame_sink_id{10, 10}; + auto* surface_quad = + render_passes[1]->CreateAndAppendDrawQuad<viz::SurfaceDrawQuad>(); + viz::SharedQuadState sqs2; + surface_quad->shared_quad_state = &sqs2; + surface_quad->material = viz::DrawQuad::Material::kSurfaceContent; + surface_quad->surface_range = viz::SurfaceRange( + base::nullopt, + viz::SurfaceId(frame_sink_id, viz::LocalSurfaceId( + 1u, base::UnguessableToken::Create()))); + surface_quad->rect = quad_rect; + surface_quad->visible_rect = quad_rect; + + base::flat_set<viz::FrameSinkId> expected_frame_sinks{frame_sink_id}; + // The surface quad (0,0 100x100) is entirely behind the backdrop filter on + // the rpdq (0,0 100x100) so it can be throttled. + RunThrottleDecider(render_passes); + EXPECT_EQ(GetFrameSinksToThrottle(), expected_frame_sinks); + + // Put the backdrop filter within bounds (0,10 50x50). + render_passes[0]->backdrop_filter_bounds = + base::Optional<gfx::RRectF>(gfx::RRectF(0.0f, 10.0f, 50.0f, 50.0f, 1.0f)); + // The surface quad (0,0 100x100) is partially behind the backdrop filter on + // the rpdq (0,10 50x50) so it should not be throttled. + RunThrottleDecider(render_passes); + EXPECT_TRUE(GetFrameSinksToThrottle().empty()); + + // Transform the surface quad to (0,10 50x50). + gfx::Transform transform; + transform.Translate(0, 10); + transform.Scale(0.5f, 0.5f); + sqs2.quad_to_target_transform = transform; + // The surface quad (0,10 50x50) is entirely behind the backdrop filter on the + // rpdq (0,10 50x50) so it can be throttled. + RunThrottleDecider(render_passes); + EXPECT_EQ(GetFrameSinksToThrottle(), expected_frame_sinks); + + // Add a mask to the backdrop filter. + rpdq->resources.ids[viz::RenderPassDrawQuadInternal::kMaskResourceIdIndex] = + viz::ResourceId::FromUnsafeValue(1u); + + // As the mask would make the backdrop filter to be ignored, the surface + // should not be throttled. + RunThrottleDecider(render_passes); + EXPECT_TRUE(GetFrameSinksToThrottle().empty()); + + // Add a foreground filter to the second render pass. + render_passes[1]->filters.Append(FilterOperation::CreateBlurFilter(5.0f)); + // The surface quad is being blurred by the foreground filter so it can be + // throttled. + RunThrottleDecider(render_passes); + EXPECT_EQ(GetFrameSinksToThrottle(), expected_frame_sinks); +} + +} // namespace cc diff --git a/chromium/cc/trees/transform_node.cc b/chromium/cc/trees/transform_node.cc index 4d58a3c71fb..1c91fcae2fc 100644 --- a/chromium/cc/trees/transform_node.cc +++ b/chromium/cc/trees/transform_node.cc @@ -35,8 +35,7 @@ TransformNode::TransformNode() delegates_to_parent_for_backface(false), will_change_transform(false), node_or_ancestors_will_change_transform(false), - maximum_animation_scale(kNotScaled), - starting_animation_scale(kNotScaled) {} + maximum_animation_scale(kInvalidScale) {} TransformNode::TransformNode(const TransformNode&) = default; @@ -74,7 +73,6 @@ bool TransformNode::operator==(const TransformNode& other) const { scroll_offset == other.scroll_offset && snap_amount == other.snap_amount && maximum_animation_scale == other.maximum_animation_scale && - starting_animation_scale == other.starting_animation_scale && visible_frame_element_id == other.visible_frame_element_id; } #endif // DCHECK_IS_ON() diff --git a/chromium/cc/trees/transform_node.h b/chromium/cc/trees/transform_node.h index 577a79dec15..71d8e98f459 100644 --- a/chromium/cc/trees/transform_node.h +++ b/chromium/cc/trees/transform_node.h @@ -122,10 +122,10 @@ struct CC_EXPORT TransformNode { // otherwise we may need it to undo the snapping next frame. gfx::Vector2dF snap_amount; - // See MutatorHost::GetAnimationScales() for their meanings. Updated by - // PropertyTrees::AnimationScalesChanged(). + // From MutatorHost::GetMaximuimAnimationScale(). Updated by + // PropertyTrees::MaximumAnimationScaleChanged() and + // LayerTreeImpl::UpdateTransformAnimation(). float maximum_animation_scale; - float starting_animation_scale; // Set to the element ID of containing document if this transform node is the // root of a visible frame subtree. diff --git a/chromium/cc/trees/tree_synchronizer.cc b/chromium/cc/trees/tree_synchronizer.cc index 5c5a70d2020..26f56297145 100644 --- a/chromium/cc/trees/tree_synchronizer.cc +++ b/chromium/cc/trees/tree_synchronizer.cc @@ -9,10 +9,10 @@ #include <set> #include "base/check_op.h" +#include "base/containers/contains.h" #include "base/containers/flat_set.h" #include "base/debug/crash_logging.h" #include "base/debug/dump_without_crashing.h" -#include "base/stl_util.h" #include "base/trace_event/trace_event.h" #include "cc/layers/layer.h" #include "cc/layers/layer_collections.h" diff --git a/chromium/cc/trees/tree_synchronizer_unittest.cc b/chromium/cc/trees/tree_synchronizer_unittest.cc index 88f747398b9..5981ff8f314 100644 --- a/chromium/cc/trees/tree_synchronizer_unittest.cc +++ b/chromium/cc/trees/tree_synchronizer_unittest.cc @@ -11,9 +11,9 @@ #include <utility> #include <vector> +#include "base/containers/contains.h" #include "base/format_macros.h" #include "base/memory/ptr_util.h" -#include "base/stl_util.h" #include "base/strings/stringprintf.h" #include "base/threading/thread_task_runner_handle.h" #include "cc/animation/animation_host.h" @@ -728,22 +728,34 @@ TEST_F(TreeSynchronizerTest, RefreshPropertyTreesCachedData) { host_impl->ActivateSyncTree(); // This arbitrarily set the animation scale for transform_layer and see if it - // is - // refreshed when pushing layer trees. - host_impl->active_tree()->property_trees()->SetAnimationScalesForTesting( - transform_layer->transform_tree_index(), 10.f, 10.f); + // is refreshed when pushing layer trees. + float maximum_animation_scale = 123.f; + bool animation_affected_by_invalid_scale = true; + host_impl->active_tree() + ->property_trees() + ->SetMaximumAnimationToScreenScaleForTesting( + transform_layer->transform_tree_index(), maximum_animation_scale, + animation_affected_by_invalid_scale); EXPECT_EQ( - CombinedAnimationScale(10.f, 10.f), - host_impl->active_tree()->property_trees()->GetAnimationScales( - transform_layer->transform_tree_index(), host_impl->active_tree())); + maximum_animation_scale, + host_impl->active_tree()->property_trees()->MaximumAnimationToScreenScale( + transform_layer->transform_tree_index())); + EXPECT_TRUE(host_impl->active_tree() + ->property_trees() + ->AnimationAffectedByInvalidScale( + transform_layer->transform_tree_index())); host_impl->CreatePendingTree(); host_->CommitAndCreatePendingTree(); host_impl->ActivateSyncTree(); EXPECT_EQ( - CombinedAnimationScale(kNotScaled, kNotScaled), - host_impl->active_tree()->property_trees()->GetAnimationScales( - transform_layer->transform_tree_index(), host_impl->active_tree())); + 2.0f, + host_impl->active_tree()->property_trees()->MaximumAnimationToScreenScale( + transform_layer->transform_tree_index())); + EXPECT_FALSE(host_impl->active_tree() + ->property_trees() + ->AnimationAffectedByInvalidScale( + transform_layer->transform_tree_index())); } TEST_F(TreeSynchronizerTest, RoundedScrollDeltasOnCommit) { diff --git a/chromium/cc/trees/ukm_manager.cc b/chromium/cc/trees/ukm_manager.cc index b3761e46d3d..f75f67c74c4 100644 --- a/chromium/cc/trees/ukm_manager.cc +++ b/chromium/cc/trees/ukm_manager.cc @@ -177,7 +177,10 @@ void UkmManager::RecordCompositorLatencyUKM( CompositorFrameReporter::FrameReportType report_type, const std::vector<CompositorFrameReporter::StageData>& stage_history, const CompositorFrameReporter::ActiveTrackers& active_trackers, - const viz::FrameTimingDetails& viz_breakdown) const { + const CompositorFrameReporter::ProcessedBlinkBreakdown& + processed_blink_breakdown, + const CompositorFrameReporter::ProcessedVizBreakdown& + processed_viz_breakdown) const { using StageType = CompositorFrameReporter::StageType; ukm::builders::Graphics_Smoothness_Latency builder(source_id_); @@ -186,7 +189,7 @@ void UkmManager::RecordCompositorLatencyUKM( builder.SetMissedFrame(true); } - // Record each stage + // Record each stage. for (const CompositorFrameReporter::StageData& stage : stage_history) { switch (stage.stage_type) { #define CASE_FOR_STAGE(name) \ @@ -199,52 +202,68 @@ void UkmManager::RecordCompositorLatencyUKM( CASE_FOR_STAGE(EndCommitToActivation); CASE_FOR_STAGE(Activation); CASE_FOR_STAGE(EndActivateToSubmitCompositorFrame); + CASE_FOR_STAGE(SubmitCompositorFrameToPresentationCompositorFrame); CASE_FOR_STAGE(TotalLatency); #undef CASE_FOR_STAGE - // Break out kSubmitCompositorFrameToPresentationCompositorFrame to report - // the viz breakdown. - case StageType::kSubmitCompositorFrameToPresentationCompositorFrame: - builder.SetSubmitCompositorFrameToPresentationCompositorFrame( - (stage.end_time - stage.start_time).InMicroseconds()); - if (viz_breakdown.received_compositor_frame_timestamp.is_null()) - break; - builder - .SetSubmitCompositorFrameToPresentationCompositorFrame_SubmitToReceiveCompositorFrame( - (viz_breakdown.received_compositor_frame_timestamp - - stage.start_time) - .InMicroseconds()); - if (viz_breakdown.draw_start_timestamp.is_null()) - break; - builder - .SetSubmitCompositorFrameToPresentationCompositorFrame_ReceivedCompositorFrameToStartDraw( - (viz_breakdown.draw_start_timestamp - - viz_breakdown.received_compositor_frame_timestamp) - .InMicroseconds()); - if (viz_breakdown.swap_timings.is_null()) - break; - builder - .SetSubmitCompositorFrameToPresentationCompositorFrame_StartDrawToSwapStart( - (viz_breakdown.swap_timings.swap_start - - viz_breakdown.draw_start_timestamp) - .InMicroseconds()); - builder - .SetSubmitCompositorFrameToPresentationCompositorFrame_SwapStartToSwapEnd( - (viz_breakdown.swap_timings.swap_end - - viz_breakdown.swap_timings.swap_start) - .InMicroseconds()); - builder - .SetSubmitCompositorFrameToPresentationCompositorFrame_SwapEndToPresentationCompositorFrame( - (viz_breakdown.presentation_feedback.timestamp - - viz_breakdown.swap_timings.swap_end) - .InMicroseconds()); + default: + NOTREACHED(); + break; + } + } + + // Record Blink breakdowns. + for (auto it = processed_blink_breakdown.CreateIterator(); it.IsValid(); + it.Advance()) { + switch (it.GetBreakdown()) { +#define CASE_FOR_BLINK_BREAKDOWN(name) \ + case CompositorFrameReporter::BlinkBreakdown::k##name: \ + builder.SetSendBeginMainFrameToCommit_##name( \ + it.GetLatency().InMicroseconds()); \ + break; + CASE_FOR_BLINK_BREAKDOWN(HandleInputEvents); + CASE_FOR_BLINK_BREAKDOWN(Animate); + CASE_FOR_BLINK_BREAKDOWN(StyleUpdate); + CASE_FOR_BLINK_BREAKDOWN(LayoutUpdate); + CASE_FOR_BLINK_BREAKDOWN(Prepaint); + CASE_FOR_BLINK_BREAKDOWN(CompositingInputs); + CASE_FOR_BLINK_BREAKDOWN(CompositingAssignments); + CASE_FOR_BLINK_BREAKDOWN(Paint); + CASE_FOR_BLINK_BREAKDOWN(CompositeCommit); + CASE_FOR_BLINK_BREAKDOWN(UpdateLayers); + CASE_FOR_BLINK_BREAKDOWN(BeginMainSentToStarted); +#undef CASE_FOR_BLINK_BREAKDOWN + default: + NOTREACHED(); break; + } + } + + // Record Viz breakdowns. + for (auto it = processed_viz_breakdown.CreateIterator(false); it.IsValid(); + it.Advance()) { + switch (it.GetBreakdown()) { +#define CASE_FOR_VIZ_BREAKDOWN(name) \ + case CompositorFrameReporter::VizBreakdown::k##name: \ + builder.SetSubmitCompositorFrameToPresentationCompositorFrame_##name( \ + it.GetDuration().InMicroseconds()); \ + break; + CASE_FOR_VIZ_BREAKDOWN(SubmitToReceiveCompositorFrame); + CASE_FOR_VIZ_BREAKDOWN(ReceivedCompositorFrameToStartDraw); + CASE_FOR_VIZ_BREAKDOWN(StartDrawToSwapStart); + CASE_FOR_VIZ_BREAKDOWN(SwapStartToSwapEnd); + CASE_FOR_VIZ_BREAKDOWN(SwapEndToPresentationCompositorFrame); + CASE_FOR_VIZ_BREAKDOWN(SwapStartToBufferAvailable); + CASE_FOR_VIZ_BREAKDOWN(BufferAvailableToBufferReady); + CASE_FOR_VIZ_BREAKDOWN(BufferReadyToLatch); + CASE_FOR_VIZ_BREAKDOWN(LatchToSwapEnd); +#undef CASE_FOR_VIZ_BREAKDOWN default: NOTREACHED(); break; } } - // Record the active trackers + // Record the active trackers. for (size_t type = 0; type < active_trackers.size(); ++type) { if (!active_trackers.test(type)) continue; @@ -278,7 +297,10 @@ void UkmManager::RecordCompositorLatencyUKM( void UkmManager::RecordEventLatencyUKM( const EventMetrics::List& events_metrics, const std::vector<CompositorFrameReporter::StageData>& stage_history, - const viz::FrameTimingDetails& viz_breakdown) const { + const CompositorFrameReporter::ProcessedBlinkBreakdown& + processed_blink_breakdown, + const CompositorFrameReporter::ProcessedVizBreakdown& + processed_viz_breakdown) const { using StageType = CompositorFrameReporter::StageType; for (const auto& event_metrics : events_metrics) { @@ -286,46 +308,162 @@ void UkmManager::RecordEventLatencyUKM( builder.SetEventType(static_cast<int64_t>(event_metrics->type())); + base::TimeTicks generated_timestamp = + event_metrics->GetDispatchStageTimestamp( + EventMetrics::DispatchStage::kGenerated); + if (event_metrics->scroll_type()) { builder.SetScrollInputType( static_cast<int64_t>(*event_metrics->scroll_type())); - if (!viz_breakdown.swap_timings.is_null()) { + if (event_metrics->ShouldReportScrollingTotalLatency() && + !processed_viz_breakdown.swap_start().is_null()) { builder.SetTotalLatencyToSwapBegin( - (viz_breakdown.swap_timings.swap_start - - event_metrics->time_stamp()) + (processed_viz_breakdown.swap_start() - generated_timestamp) .InMicroseconds()); } } - // It is possible for an event to arrive in the compositor in the middle of - // a frame (e.g. the browser received the event *after* renderer received a - // begin-impl, and the event reached the compositor before that frame + // Record event dispatch metrics. + EventMetrics::DispatchStage dispatch_stage = + EventMetrics::DispatchStage::kGenerated; + base::TimeTicks dispatch_timestamp = generated_timestamp; + while (dispatch_stage != EventMetrics::DispatchStage::kMaxValue) { + DCHECK(!dispatch_timestamp.is_null()); + int dispatch_index = static_cast<int>(dispatch_stage); + + // Find the end dispatch stage. + auto end_stage = + static_cast<EventMetrics::DispatchStage>(dispatch_index + 1); + base::TimeTicks end_timestamp = + event_metrics->GetDispatchStageTimestamp(end_stage); + while (end_timestamp.is_null() && + end_stage != EventMetrics::DispatchStage::kMaxValue) { + end_stage = static_cast<EventMetrics::DispatchStage>( + static_cast<int>(end_stage) + 1); + end_timestamp = event_metrics->GetDispatchStageTimestamp(end_stage); + } + if (end_timestamp.is_null()) + break; + + const int64_t dispatch_latency = + (end_timestamp - dispatch_timestamp).InMicroseconds(); + switch (dispatch_stage) { + case EventMetrics::DispatchStage::kGenerated: + DCHECK_EQ(end_stage, + EventMetrics::DispatchStage::kArrivedInRendererCompositor); + builder.SetGenerationToRendererCompositor(dispatch_latency); + break; + case EventMetrics::DispatchStage::kArrivedInRendererCompositor: + switch (end_stage) { + case EventMetrics::DispatchStage::kRendererCompositorStarted: + builder.SetRendererCompositorQueueingDelay(dispatch_latency); + break; + case EventMetrics::DispatchStage::kRendererMainStarted: + builder.SetRendererCompositorToMain(dispatch_latency); + break; + default: + NOTREACHED(); + break; + } + break; + case EventMetrics::DispatchStage::kRendererCompositorStarted: + DCHECK_EQ(end_stage, + EventMetrics::DispatchStage::kRendererCompositorFinished); + builder.SetRendererCompositorProcessing(dispatch_latency); + break; + case EventMetrics::DispatchStage::kRendererCompositorFinished: + DCHECK_EQ(end_stage, + EventMetrics::DispatchStage::kRendererMainStarted); + builder.SetRendererCompositorToMain(dispatch_latency); + break; + case EventMetrics::DispatchStage::kRendererMainStarted: + DCHECK_EQ(end_stage, + EventMetrics::DispatchStage::kRendererMainFinished); + builder.SetRendererMainProcessing(dispatch_latency); + break; + case EventMetrics::DispatchStage::kRendererMainFinished: + NOTREACHED(); + break; + } + + dispatch_stage = end_stage; + dispatch_timestamp = end_timestamp; + } + + // It is possible for an event to be handled on the renderer in the middle + // of a frame (e.g. the browser received the event *after* renderer received + // a begin-impl, and the event was handled on the renderer before that frame // ended). To handle such cases, find the first stage that happens after the - // event's arrival in the browser. + // event's processing finished on the renderer. auto stage_it = std::find_if( stage_history.begin(), stage_history.end(), - [&event_metrics](const CompositorFrameReporter::StageData& stage) { - return stage.start_time > event_metrics->time_stamp(); + [dispatch_timestamp](const CompositorFrameReporter::StageData& stage) { + return stage.start_time > dispatch_timestamp; }); // TODO(crbug.com/1079116): Ideally, at least the start time of // SubmitCompositorFrameToPresentationCompositorFrame stage should be - // greater than the event time stamp, but apparently, this is not always the - // case (see crbug.com/1093698). For now, skip to the next event in such - // cases. Hopefully, the work to reduce discrepancies between the new - // EventLatency and the old Event.Latency metrics would fix this issue. If - // not, we need to reconsider investigating this issue. + // greater than the final event dispatch timestamp, but apparently, this is + // not always the case (see crbug.com/1093698). For now, skip to the next + // event in such cases. Hopefully, the work to reduce discrepancies between + // the new EventLatency and the old Event.Latency metrics would fix this + // issue. If not, we need to reconsider investigating this issue. if (stage_it == stage_history.end()) continue; - builder.SetBrowserToRendererCompositor( - (stage_it->start_time - event_metrics->time_stamp()).InMicroseconds()); + switch (dispatch_stage) { + case EventMetrics::DispatchStage::kRendererCompositorFinished: + switch (stage_it->stage_type) { +#define CASE_FOR_STAGE(stage_name, metrics_suffix) \ + case StageType::k##stage_name: \ + builder.SetRendererCompositorFinishedTo##metrics_suffix( \ + (stage_it->start_time - dispatch_timestamp).InMicroseconds()); \ + break; + CASE_FOR_STAGE(BeginImplFrameToSendBeginMainFrame, BeginImplFrame); + CASE_FOR_STAGE(SendBeginMainFrameToCommit, SendBeginMainFrame); + CASE_FOR_STAGE(Commit, Commit); + CASE_FOR_STAGE(EndCommitToActivation, EndCommit); + CASE_FOR_STAGE(Activation, Activation); + CASE_FOR_STAGE(EndActivateToSubmitCompositorFrame, EndActivate); + CASE_FOR_STAGE(SubmitCompositorFrameToPresentationCompositorFrame, + SubmitCompositorFrame); +#undef CASE_FOR_STAGE + default: + NOTREACHED(); + break; + } + break; + case EventMetrics::DispatchStage::kRendererMainFinished: + switch (stage_it->stage_type) { +#define CASE_FOR_STAGE(stage_name, metrics_suffix) \ + case StageType::k##stage_name: \ + builder.SetRendererMainFinishedTo##metrics_suffix( \ + (stage_it->start_time - dispatch_timestamp).InMicroseconds()); \ + break; + CASE_FOR_STAGE(BeginImplFrameToSendBeginMainFrame, BeginImplFrame); + CASE_FOR_STAGE(SendBeginMainFrameToCommit, SendBeginMainFrame); + CASE_FOR_STAGE(Commit, Commit); + CASE_FOR_STAGE(EndCommitToActivation, EndCommit); + CASE_FOR_STAGE(Activation, Activation); + CASE_FOR_STAGE(EndActivateToSubmitCompositorFrame, EndActivate); + CASE_FOR_STAGE(SubmitCompositorFrameToPresentationCompositorFrame, + SubmitCompositorFrame); +#undef CASE_FOR_STAGE + default: + NOTREACHED(); + break; + } + break; + default: + NOTREACHED(); + break; + } for (; stage_it != stage_history.end(); ++stage_it) { // Total latency is calculated since the event timestamp. const base::TimeTicks start_time = stage_it->stage_type == StageType::kTotalLatency - ? event_metrics->time_stamp() + ? generated_timestamp : stage_it->start_time; switch (stage_it->stage_type) { @@ -348,6 +486,58 @@ void UkmManager::RecordEventLatencyUKM( } } + // Record Blink breakdowns. + for (auto it = processed_blink_breakdown.CreateIterator(); it.IsValid(); + it.Advance()) { + switch (it.GetBreakdown()) { +#define CASE_FOR_BLINK_BREAKDOWN(name) \ + case CompositorFrameReporter::BlinkBreakdown::k##name: \ + builder.SetSendBeginMainFrameToCommit_##name( \ + it.GetLatency().InMicroseconds()); \ + break; + CASE_FOR_BLINK_BREAKDOWN(HandleInputEvents); + CASE_FOR_BLINK_BREAKDOWN(Animate); + CASE_FOR_BLINK_BREAKDOWN(StyleUpdate); + CASE_FOR_BLINK_BREAKDOWN(LayoutUpdate); + CASE_FOR_BLINK_BREAKDOWN(Prepaint); + CASE_FOR_BLINK_BREAKDOWN(CompositingInputs); + CASE_FOR_BLINK_BREAKDOWN(CompositingAssignments); + CASE_FOR_BLINK_BREAKDOWN(Paint); + CASE_FOR_BLINK_BREAKDOWN(CompositeCommit); + CASE_FOR_BLINK_BREAKDOWN(UpdateLayers); + CASE_FOR_BLINK_BREAKDOWN(BeginMainSentToStarted); +#undef CASE_FOR_BLINK_BREAKDOWN + default: + NOTREACHED(); + break; + } + } + + // Record Viz breakdowns. + for (auto it = processed_viz_breakdown.CreateIterator(false); it.IsValid(); + it.Advance()) { + switch (it.GetBreakdown()) { +#define CASE_FOR_VIZ_BREAKDOWN(name) \ + case CompositorFrameReporter::VizBreakdown::k##name: \ + builder.SetSubmitCompositorFrameToPresentationCompositorFrame_##name( \ + it.GetDuration().InMicroseconds()); \ + break; + CASE_FOR_VIZ_BREAKDOWN(SubmitToReceiveCompositorFrame); + CASE_FOR_VIZ_BREAKDOWN(ReceivedCompositorFrameToStartDraw); + CASE_FOR_VIZ_BREAKDOWN(StartDrawToSwapStart); + CASE_FOR_VIZ_BREAKDOWN(SwapStartToSwapEnd); + CASE_FOR_VIZ_BREAKDOWN(SwapEndToPresentationCompositorFrame); + CASE_FOR_VIZ_BREAKDOWN(SwapStartToBufferAvailable); + CASE_FOR_VIZ_BREAKDOWN(BufferAvailableToBufferReady); + CASE_FOR_VIZ_BREAKDOWN(BufferReadyToLatch); + CASE_FOR_VIZ_BREAKDOWN(LatchToSwapEnd); +#undef CASE_FOR_VIZ_BREAKDOWN + default: + NOTREACHED(); + break; + } + } + builder.Record(recorder_.get()); } } diff --git a/chromium/cc/trees/ukm_manager.h b/chromium/cc/trees/ukm_manager.h index b51147f71c3..03f82915e0e 100644 --- a/chromium/cc/trees/ukm_manager.h +++ b/chromium/cc/trees/ukm_manager.h @@ -12,7 +12,6 @@ #include "cc/metrics/compositor_frame_reporter.h" #include "cc/metrics/event_metrics.h" #include "cc/metrics/frame_sequence_metrics.h" -#include "components/viz/common/frame_timing_details.h" #include "services/metrics/public/cpp/ukm_source_id.h" #include "url/gurl.h" @@ -57,12 +56,18 @@ class CC_EXPORT UkmManager { CompositorFrameReporter::FrameReportType report_type, const std::vector<CompositorFrameReporter::StageData>& stage_history, const CompositorFrameReporter::ActiveTrackers& active_trackers, - const viz::FrameTimingDetails& viz_breakdown) const; + const CompositorFrameReporter::ProcessedBlinkBreakdown& + processed_blink_breakdown, + const CompositorFrameReporter::ProcessedVizBreakdown& + processed_viz_breakdown) const; void RecordEventLatencyUKM( const EventMetrics::List& events_metrics, const std::vector<CompositorFrameReporter::StageData>& stage_history, - const viz::FrameTimingDetails& viz_breakdown) const; + const CompositorFrameReporter::ProcessedBlinkBreakdown& + processed_blink_breakdown, + const CompositorFrameReporter::ProcessedVizBreakdown& + processed_viz_breakdown) const; ukm::UkmRecorder* recorder_for_testing() { return recorder_.get(); } diff --git a/chromium/cc/trees/ukm_manager_unittest.cc b/chromium/cc/trees/ukm_manager_unittest.cc index 6449021af38..4f7aabe28f0 100644 --- a/chromium/cc/trees/ukm_manager_unittest.cc +++ b/chromium/cc/trees/ukm_manager_unittest.cc @@ -4,10 +4,13 @@ #include "cc/trees/ukm_manager.h" +#include <algorithm> #include <utility> #include <vector> +#include "base/test/simple_test_tick_clock.h" #include "base/time/time.h" +#include "cc/metrics/begin_main_frame_metrics.h" #include "cc/metrics/compositor_frame_reporter.h" #include "cc/metrics/event_metrics.h" #include "components/ukm/test_ukm_recorder.h" @@ -42,10 +45,36 @@ const char kScrollInputType[] = "ScrollInputType"; // Names of compositor stages and substages used in compositor/event latency UKM // metrics. -const char kBrowserToRendererCompositor[] = "BrowserToRendererCompositor"; +const char kGenerationToRendererCompositor[] = "GenerationToRendererCompositor"; +const char kRendererCompositorQueueingDelay[] = + "RendererCompositorQueueingDelay"; +const char kRendererCompositorProcessing[] = "RendererCompositorProcessing"; +const char kRendererCompositorToMain[] = "RendererCompositorToMain"; +const char kRendererMainProcessing[] = "RendererMainProcessing"; +const char kRendererMainFinishedToBeginImplFrame[] = + "RendererMainFinishedToBeginImplFrame"; const char kBeginImplFrameToSendBeginMainFrame[] = "BeginImplFrameToSendBeginMainFrame"; const char kSendBeginMainFrameToCommit[] = "SendBeginMainFrameToCommit"; +const char kBlinkBreakdownHandleInputEvents[] = + "SendBeginMainFrameToCommit.HandleInputEvents"; +const char kBlinkBreakdownAnimate[] = "SendBeginMainFrameToCommit.Animate"; +const char kBlinkBreakdownStyleUpdate[] = + "SendBeginMainFrameToCommit.StyleUpdate"; +const char kBlinkBreakdownLayoutUpdate[] = + "SendBeginMainFrameToCommit.LayoutUpdate"; +const char kBlinkBreakdownPrepaint[] = "SendBeginMainFrameToCommit.Prepaint"; +const char kBlinkBreakdownCompositingInputs[] = + "SendBeginMainFrameToCommit.CompositingInputs"; +const char kBlinkBreakdownCompositingAssignments[] = + "SendBeginMainFrameToCommit.CompositingAssignments"; +const char kBlinkBreakdownPaint[] = "SendBeginMainFrameToCommit.Paint"; +const char kBlinkBreakdownCompositeCommit[] = + "SendBeginMainFrameToCommit.CompositeCommit"; +const char kBlinkBreakdownUpdateLayers[] = + "SendBeginMainFrameToCommit.UpdateLayers"; +const char kBlinkBreakdownBeginMainSentToStarted[] = + "SendBeginMainFrameToCommit.BeginMainSentToStarted"; const char kCommit[] = "Commit"; const char kEndCommitToActivation[] = "EndCommitToActivation"; const char kActivation[] = "Activation"; @@ -63,6 +92,16 @@ const char kVizBreakdownStartDrawToSwapStart[] = "SubmitCompositorFrameToPresentationCompositorFrame.StartDrawToSwapStart"; const char kVizBreakdownSwapStartToSwapEnd[] = "SubmitCompositorFrameToPresentationCompositorFrame.SwapStartToSwapEnd"; +const char kVizBreakdownSwapStartToBufferAvailable[] = + "SubmitCompositorFrameToPresentationCompositorFrame." + "SwapStartToBufferAvailable"; +const char kVizBreakdownBufferAvailableToBufferReady[] = + "SubmitCompositorFrameToPresentationCompositorFrame." + "BufferAvailableToBufferReady"; +const char kVizBreakdownBufferReadyToLatch[] = + "SubmitCompositorFrameToPresentationCompositorFrame.BufferReadyToLatch"; +const char kVizBreakdownLatchToSwapEnd[] = + "SubmitCompositorFrameToPresentationCompositorFrame.LatchToSwapEnd"; const char kVizBreakdownSwapEndToPresentationCompositorFrame[] = "SubmitCompositorFrameToPresentationCompositorFrame." "SwapEndToPresentationCompositorFrame"; @@ -96,8 +135,105 @@ class UkmManagerTest : public testing::Test { ~UkmManagerTest() override = default; protected: + base::TimeTicks AdvanceNowByMs(int advance_ms) { + test_tick_clock_.Advance(base::TimeDelta::FromMicroseconds(advance_ms)); + return test_tick_clock_.NowTicks(); + } + + std::unique_ptr<EventMetrics> CreateEventMetrics( + ui::EventType type, + base::Optional<EventMetrics::ScrollUpdateType> scroll_update_type, + base::Optional<ui::ScrollInputType> scroll_input_type) { + base::TimeTicks event_time = AdvanceNowByMs(10); + AdvanceNowByMs(10); + std::unique_ptr<EventMetrics> metrics = EventMetrics::CreateForTesting( + type, scroll_update_type, scroll_input_type, event_time, + &test_tick_clock_); + if (metrics) { + AdvanceNowByMs(10); + metrics->SetDispatchStageTimestamp( + EventMetrics::DispatchStage::kRendererCompositorStarted); + AdvanceNowByMs(10); + metrics->SetDispatchStageTimestamp( + EventMetrics::DispatchStage::kRendererCompositorFinished); + AdvanceNowByMs(10); + metrics->SetDispatchStageTimestamp( + EventMetrics::DispatchStage::kRendererMainStarted); + AdvanceNowByMs(10); + metrics->SetDispatchStageTimestamp( + EventMetrics::DispatchStage::kRendererMainFinished); + } + return metrics; + } + + struct DispatchTimestamps { + base::TimeTicks generated; + base::TimeTicks arrived_in_renderer; + base::TimeTicks renderer_compositor_started; + base::TimeTicks renderer_compositor_finished; + base::TimeTicks renderer_main_started; + base::TimeTicks renderer_main_finished; + }; + std::vector<DispatchTimestamps> GetEventDispatchTimestamps( + const EventMetrics::List& events_metrics) { + std::vector<DispatchTimestamps> event_times; + event_times.reserve(events_metrics.size()); + std::transform( + events_metrics.cbegin(), events_metrics.cend(), + std::back_inserter(event_times), [](const auto& event_metrics) { + return DispatchTimestamps{ + event_metrics->GetDispatchStageTimestamp( + EventMetrics::DispatchStage::kGenerated), + event_metrics->GetDispatchStageTimestamp( + EventMetrics::DispatchStage::kArrivedInRendererCompositor), + event_metrics->GetDispatchStageTimestamp( + EventMetrics::DispatchStage::kRendererCompositorStarted), + event_metrics->GetDispatchStageTimestamp( + EventMetrics::DispatchStage::kRendererCompositorFinished), + event_metrics->GetDispatchStageTimestamp( + EventMetrics::DispatchStage::kRendererMainStarted), + event_metrics->GetDispatchStageTimestamp( + EventMetrics::DispatchStage::kRendererMainFinished), + }; + }); + return event_times; + } + + BeginMainFrameMetrics BuildBlinkBreakdown() { + BeginMainFrameMetrics breakdown; + breakdown.handle_input_events = base::TimeDelta::FromMicroseconds(10); + breakdown.animate = base::TimeDelta::FromMicroseconds(9); + breakdown.style_update = base::TimeDelta::FromMicroseconds(8); + breakdown.layout_update = base::TimeDelta::FromMicroseconds(7); + breakdown.compositing_inputs = base::TimeDelta::FromMicroseconds(6); + breakdown.prepaint = base::TimeDelta::FromMicroseconds(5); + breakdown.compositing_assignments = base::TimeDelta::FromMicroseconds(4); + breakdown.paint = base::TimeDelta::FromMicroseconds(3); + breakdown.composite_commit = base::TimeDelta::FromMicroseconds(2); + breakdown.update_layers = base::TimeDelta::FromMicroseconds(1); + + // Advance now by the sum of the breakdowns. + AdvanceNowByMs(10 + 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1); + + return breakdown; + } + + viz::FrameTimingDetails BuildVizBreakdown() { + viz::FrameTimingDetails breakdown; + breakdown.received_compositor_frame_timestamp = AdvanceNowByMs(1); + breakdown.draw_start_timestamp = AdvanceNowByMs(2); + breakdown.swap_timings.swap_start = AdvanceNowByMs(3); + breakdown.presentation_feedback.available_timestamp = AdvanceNowByMs(1); + breakdown.presentation_feedback.ready_timestamp = AdvanceNowByMs(1); + breakdown.presentation_feedback.latch_timestamp = AdvanceNowByMs(1); + breakdown.swap_timings.swap_end = AdvanceNowByMs(1); + breakdown.presentation_feedback.timestamp = AdvanceNowByMs(5); + return breakdown; + } + ukm::TestUkmRecorder* test_ukm_recorder_; std::unique_ptr<UkmManager> manager_; + base::SimpleTestTickClock test_tick_clock_; }; TEST_F(UkmManagerTest, Basic) { @@ -185,34 +321,19 @@ INSTANTIATE_TEST_SUITE_P( CompositorFrameReporter::FrameReportType::kCompositorOnlyFrame)); TEST_P(UkmManagerCompositorLatencyTest, CompositorLatency) { - base::TimeTicks now = base::TimeTicks::Now(); - - const base::TimeTicks begin_impl_time = - (now += base::TimeDelta::FromMicroseconds(10)); - const base::TimeTicks begin_main_time = - (now += base::TimeDelta::FromMicroseconds(10)); - const base::TimeTicks begin_commit_time = - (now += base::TimeDelta::FromMicroseconds(10)); - const base::TimeTicks end_commit_time = - (now += base::TimeDelta::FromMicroseconds(10)); - const base::TimeTicks begin_activate_time = - (now += base::TimeDelta::FromMicroseconds(10)); - const base::TimeTicks end_activate_time = - (now += base::TimeDelta::FromMicroseconds(10)); - const base::TimeTicks submit_time = - (now += base::TimeDelta::FromMicroseconds(10)); - - viz::FrameTimingDetails viz_breakdown; - viz_breakdown.received_compositor_frame_timestamp = - (now += base::TimeDelta::FromMicroseconds(1)); - viz_breakdown.draw_start_timestamp = - (now += base::TimeDelta::FromMicroseconds(2)); - viz_breakdown.swap_timings.swap_start = - (now += base::TimeDelta::FromMicroseconds(3)); - viz_breakdown.swap_timings.swap_end = - (now += base::TimeDelta::FromMicroseconds(4)); - viz_breakdown.presentation_feedback.timestamp = - (now += base::TimeDelta::FromMicroseconds(5)); + const base::TimeTicks begin_impl_time = AdvanceNowByMs(10); + const base::TimeTicks begin_main_time = AdvanceNowByMs(10); + const base::TimeTicks begin_main_start_time = AdvanceNowByMs(10); + + BeginMainFrameMetrics blink_breakdown = BuildBlinkBreakdown(); + + const base::TimeTicks begin_commit_time = AdvanceNowByMs(10); + const base::TimeTicks end_commit_time = AdvanceNowByMs(10); + const base::TimeTicks begin_activate_time = AdvanceNowByMs(10); + const base::TimeTicks end_activate_time = AdvanceNowByMs(10); + const base::TimeTicks submit_time = AdvanceNowByMs(10); + + viz::FrameTimingDetails viz_breakdown = BuildVizBreakdown(); std::vector<CompositorFrameReporter::StageData> stage_history = { { @@ -268,8 +389,13 @@ TEST_P(UkmManagerCompositorLatencyTest, CompositorLatency) { active_trackers.set( static_cast<size_t>(FrameSequenceTrackerType::kCompositorAnimation)); - manager_->RecordCompositorLatencyUKM(report_type(), stage_history, - active_trackers, viz_breakdown); + CompositorFrameReporter::ProcessedBlinkBreakdown processed_blink_breakdown( + begin_main_time, begin_main_start_time, blink_breakdown); + CompositorFrameReporter::ProcessedVizBreakdown processed_viz_breakdown( + submit_time, viz_breakdown); + manager_->RecordCompositorLatencyUKM( + report_type(), stage_history, active_trackers, processed_blink_breakdown, + processed_viz_breakdown); const auto& entries = test_ukm_recorder_->GetEntriesByName(kCompositorLatency); @@ -293,6 +419,37 @@ TEST_P(UkmManagerCompositorLatencyTest, CompositorLatency) { entry, kSendBeginMainFrameToCommit, (begin_commit_time - begin_main_time).InMicroseconds()); test_ukm_recorder_->ExpectEntryMetric( + entry, kBlinkBreakdownHandleInputEvents, + blink_breakdown.handle_input_events.InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( + entry, kBlinkBreakdownAnimate, blink_breakdown.animate.InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( + entry, kBlinkBreakdownStyleUpdate, + blink_breakdown.style_update.InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( + entry, kBlinkBreakdownLayoutUpdate, + blink_breakdown.layout_update.InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( + entry, kBlinkBreakdownPrepaint, + blink_breakdown.prepaint.InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( + entry, kBlinkBreakdownCompositingInputs, + blink_breakdown.compositing_inputs.InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( + entry, kBlinkBreakdownCompositingAssignments, + blink_breakdown.compositing_assignments.InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric(entry, kBlinkBreakdownPaint, + blink_breakdown.paint.InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( + entry, kBlinkBreakdownCompositeCommit, + blink_breakdown.composite_commit.InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( + entry, kBlinkBreakdownUpdateLayers, + blink_breakdown.update_layers.InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( + entry, kBlinkBreakdownBeginMainSentToStarted, + (begin_main_start_time - begin_main_time).InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( entry, kCommit, (end_commit_time - begin_commit_time).InMicroseconds()); test_ukm_recorder_->ExpectEntryMetric( entry, kEndCommitToActivation, @@ -326,6 +483,26 @@ TEST_P(UkmManagerCompositorLatencyTest, CompositorLatency) { viz_breakdown.swap_timings.swap_start) .InMicroseconds()); test_ukm_recorder_->ExpectEntryMetric( + entry, kVizBreakdownSwapStartToBufferAvailable, + (viz_breakdown.presentation_feedback.available_timestamp - + viz_breakdown.swap_timings.swap_start) + .InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( + entry, kVizBreakdownBufferAvailableToBufferReady, + (viz_breakdown.presentation_feedback.ready_timestamp - + viz_breakdown.presentation_feedback.available_timestamp) + .InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( + entry, kVizBreakdownBufferReadyToLatch, + (viz_breakdown.presentation_feedback.latch_timestamp - + viz_breakdown.presentation_feedback.ready_timestamp) + .InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( + entry, kVizBreakdownLatchToSwapEnd, + (viz_breakdown.swap_timings.swap_end - + viz_breakdown.presentation_feedback.latch_timestamp) + .InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( entry, kVizBreakdownSwapEndToPresentationCompositorFrame, (viz_breakdown.presentation_feedback.timestamp - viz_breakdown.swap_timings.swap_end) @@ -346,52 +523,62 @@ TEST_P(UkmManagerCompositorLatencyTest, CompositorLatency) { } TEST_F(UkmManagerTest, EventLatency) { - base::TimeTicks now = base::TimeTicks::Now(); - - const base::TimeTicks event_time = now; std::unique_ptr<EventMetrics> event_metrics_ptrs[] = { - EventMetrics::Create(ui::ET_GESTURE_SCROLL_BEGIN, base::nullopt, - event_time, ui::ScrollInputType::kWheel), - EventMetrics::Create(ui::ET_GESTURE_SCROLL_UPDATE, - EventMetrics::ScrollUpdateType::kStarted, event_time, - ui::ScrollInputType::kWheel), - EventMetrics::Create(ui::ET_GESTURE_SCROLL_UPDATE, - EventMetrics::ScrollUpdateType::kContinued, - event_time, ui::ScrollInputType::kWheel), + CreateEventMetrics(ui::ET_GESTURE_SCROLL_BEGIN, base::nullopt, + ui::ScrollInputType::kWheel), + CreateEventMetrics(ui::ET_GESTURE_SCROLL_UPDATE, + EventMetrics::ScrollUpdateType::kStarted, + ui::ScrollInputType::kWheel), + CreateEventMetrics(ui::ET_GESTURE_SCROLL_UPDATE, + EventMetrics::ScrollUpdateType::kContinued, + ui::ScrollInputType::kWheel), }; EXPECT_THAT(event_metrics_ptrs, ::testing::Each(::testing::NotNull())); EventMetrics::List events_metrics( std::make_move_iterator(std::begin(event_metrics_ptrs)), std::make_move_iterator(std::end(event_metrics_ptrs))); + std::vector<DispatchTimestamps> event_dispatch_times = + GetEventDispatchTimestamps(events_metrics); + + const base::TimeTicks begin_impl_time = AdvanceNowByMs(10); + const base::TimeTicks begin_main_time = AdvanceNowByMs(10); + const base::TimeTicks begin_main_start_time = AdvanceNowByMs(10); + + BeginMainFrameMetrics blink_breakdown = BuildBlinkBreakdown(); + + const base::TimeTicks begin_commit_time = AdvanceNowByMs(10); + const base::TimeTicks end_commit_time = AdvanceNowByMs(10); + const base::TimeTicks begin_activate_time = AdvanceNowByMs(10); + const base::TimeTicks end_activate_time = AdvanceNowByMs(10); + const base::TimeTicks submit_time = AdvanceNowByMs(10); - const base::TimeTicks begin_impl_time = - (now += base::TimeDelta::FromMicroseconds(10)); - const base::TimeTicks end_activate_time = - (now += base::TimeDelta::FromMicroseconds(10)); - const base::TimeTicks submit_time = - (now += base::TimeDelta::FromMicroseconds(10)); - - viz::FrameTimingDetails viz_breakdown; - viz_breakdown.received_compositor_frame_timestamp = - (now += base::TimeDelta::FromMicroseconds(1)); - viz_breakdown.draw_start_timestamp = - (now += base::TimeDelta::FromMicroseconds(2)); - viz_breakdown.swap_timings.swap_start = - (now += base::TimeDelta::FromMicroseconds(3)); - viz_breakdown.swap_timings.swap_end = - (now += base::TimeDelta::FromMicroseconds(4)); - viz_breakdown.presentation_feedback.timestamp = - (now += base::TimeDelta::FromMicroseconds(5)); - - const base::TimeTicks swap_start_time = viz_breakdown.swap_timings.swap_start; - const base::TimeTicks present_time = - viz_breakdown.presentation_feedback.timestamp; + viz::FrameTimingDetails viz_breakdown = BuildVizBreakdown(); std::vector<CompositorFrameReporter::StageData> stage_history = { { CompositorFrameReporter::StageType:: kBeginImplFrameToSendBeginMainFrame, begin_impl_time, + begin_main_time, + }, + { + CompositorFrameReporter::StageType::kSendBeginMainFrameToCommit, + begin_main_time, + begin_commit_time, + }, + { + CompositorFrameReporter::StageType::kCommit, + begin_commit_time, + end_commit_time, + }, + { + CompositorFrameReporter::StageType::kEndCommitToActivation, + end_commit_time, + begin_activate_time, + }, + { + CompositorFrameReporter::StageType::kActivation, + begin_activate_time, end_activate_time, }, { @@ -404,16 +591,22 @@ TEST_F(UkmManagerTest, EventLatency) { CompositorFrameReporter::StageType:: kSubmitCompositorFrameToPresentationCompositorFrame, submit_time, - present_time, + viz_breakdown.presentation_feedback.timestamp, }, { CompositorFrameReporter::StageType::kTotalLatency, - event_time, - present_time, + begin_impl_time, + viz_breakdown.presentation_feedback.timestamp, }, }; - manager_->RecordEventLatencyUKM(events_metrics, stage_history, viz_breakdown); + CompositorFrameReporter::ProcessedBlinkBreakdown processed_blink_breakdown( + begin_main_time, begin_main_start_time, blink_breakdown); + CompositorFrameReporter::ProcessedVizBreakdown processed_viz_breakdown( + submit_time, viz_breakdown); + manager_->RecordEventLatencyUKM(events_metrics, stage_history, + processed_blink_breakdown, + processed_viz_breakdown); const auto& entries = test_ukm_recorder_->GetEntriesByName(kEventLatency); EXPECT_EQ(3u, entries.size()); @@ -431,28 +624,185 @@ TEST_F(UkmManagerTest, EventLatency) { static_cast<int64_t>(*event_metrics->scroll_type())); test_ukm_recorder_->ExpectEntryMetric( - entry, kBrowserToRendererCompositor, - (begin_impl_time - event_time).InMicroseconds()); + entry, kGenerationToRendererCompositor, + (event_dispatch_times[i].arrived_in_renderer - + event_dispatch_times[i].generated) + .InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( + entry, kRendererCompositorQueueingDelay, + (event_dispatch_times[i].renderer_compositor_started - + event_dispatch_times[i].arrived_in_renderer) + .InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( + entry, kRendererCompositorProcessing, + (event_dispatch_times[i].renderer_compositor_finished - + event_dispatch_times[i].renderer_compositor_started) + .InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( + entry, kRendererCompositorToMain, + (event_dispatch_times[i].renderer_main_started - + event_dispatch_times[i].renderer_compositor_finished) + .InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( + entry, kRendererMainProcessing, + (event_dispatch_times[i].renderer_main_finished - + event_dispatch_times[i].renderer_main_started) + .InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( + entry, kRendererMainFinishedToBeginImplFrame, + (begin_impl_time - event_dispatch_times[i].renderer_main_finished) + .InMicroseconds()); test_ukm_recorder_->ExpectEntryMetric( entry, kBeginImplFrameToSendBeginMainFrame, - (end_activate_time - begin_impl_time).InMicroseconds()); - EXPECT_FALSE( - test_ukm_recorder_->EntryHasMetric(entry, kSendBeginMainFrameToCommit)); - EXPECT_FALSE(test_ukm_recorder_->EntryHasMetric(entry, kCommit)); - EXPECT_FALSE( - test_ukm_recorder_->EntryHasMetric(entry, kEndCommitToActivation)); - EXPECT_FALSE(test_ukm_recorder_->EntryHasMetric(entry, kActivation)); + (begin_main_time - begin_impl_time).InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( + entry, kSendBeginMainFrameToCommit, + (begin_commit_time - begin_main_time).InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( + entry, kBlinkBreakdownHandleInputEvents, + blink_breakdown.handle_input_events.InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( + entry, kBlinkBreakdownAnimate, + blink_breakdown.animate.InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( + entry, kBlinkBreakdownStyleUpdate, + blink_breakdown.style_update.InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( + entry, kBlinkBreakdownLayoutUpdate, + blink_breakdown.layout_update.InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( + entry, kBlinkBreakdownPrepaint, + blink_breakdown.prepaint.InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( + entry, kBlinkBreakdownCompositingInputs, + blink_breakdown.compositing_inputs.InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( + entry, kBlinkBreakdownCompositingAssignments, + blink_breakdown.compositing_assignments.InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( + entry, kBlinkBreakdownPaint, blink_breakdown.paint.InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( + entry, kBlinkBreakdownCompositeCommit, + blink_breakdown.composite_commit.InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( + entry, kBlinkBreakdownUpdateLayers, + blink_breakdown.update_layers.InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( + entry, kBlinkBreakdownBeginMainSentToStarted, + (begin_main_start_time - begin_main_time).InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( + entry, kCommit, (end_commit_time - begin_commit_time).InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( + entry, kEndCommitToActivation, + (begin_activate_time - end_commit_time).InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( + entry, kActivation, + (end_activate_time - begin_activate_time).InMicroseconds()); test_ukm_recorder_->ExpectEntryMetric( entry, kEndActivateToSubmitCompositorFrame, (submit_time - end_activate_time).InMicroseconds()); test_ukm_recorder_->ExpectEntryMetric( entry, kSubmitCompositorFrameToPresentationCompositorFrame, - (present_time - submit_time).InMicroseconds()); + (viz_breakdown.presentation_feedback.timestamp - submit_time) + .InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( + entry, kVizBreakdownSubmitToReceiveCompositorFrame, + (viz_breakdown.received_compositor_frame_timestamp - submit_time) + .InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( + entry, kVizBreakdownReceivedCompositorFrameToStartDraw, + (viz_breakdown.draw_start_timestamp - + viz_breakdown.received_compositor_frame_timestamp) + .InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( + entry, kVizBreakdownStartDrawToSwapStart, + (viz_breakdown.swap_timings.swap_start - + viz_breakdown.draw_start_timestamp) + .InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( + entry, kVizBreakdownSwapStartToSwapEnd, + (viz_breakdown.swap_timings.swap_end - + viz_breakdown.swap_timings.swap_start) + .InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( + entry, kVizBreakdownSwapStartToBufferAvailable, + (viz_breakdown.presentation_feedback.available_timestamp - + viz_breakdown.swap_timings.swap_start) + .InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( + entry, kVizBreakdownBufferAvailableToBufferReady, + (viz_breakdown.presentation_feedback.ready_timestamp - + viz_breakdown.presentation_feedback.available_timestamp) + .InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( + entry, kVizBreakdownBufferReadyToLatch, + (viz_breakdown.presentation_feedback.latch_timestamp - + viz_breakdown.presentation_feedback.ready_timestamp) + .InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( + entry, kVizBreakdownLatchToSwapEnd, + (viz_breakdown.swap_timings.swap_end - + viz_breakdown.presentation_feedback.latch_timestamp) + .InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( + entry, kVizBreakdownSwapEndToPresentationCompositorFrame, + (viz_breakdown.presentation_feedback.timestamp - + viz_breakdown.swap_timings.swap_end) + .InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( + entry, kVizBreakdownSubmitToReceiveCompositorFrame, + (viz_breakdown.received_compositor_frame_timestamp - submit_time) + .InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( + entry, kVizBreakdownReceivedCompositorFrameToStartDraw, + (viz_breakdown.draw_start_timestamp - + viz_breakdown.received_compositor_frame_timestamp) + .InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( + entry, kVizBreakdownStartDrawToSwapStart, + (viz_breakdown.swap_timings.swap_start - + viz_breakdown.draw_start_timestamp) + .InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( + entry, kVizBreakdownSwapStartToSwapEnd, + (viz_breakdown.swap_timings.swap_end - + viz_breakdown.swap_timings.swap_start) + .InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( + entry, kVizBreakdownSwapStartToBufferAvailable, + (viz_breakdown.presentation_feedback.available_timestamp - + viz_breakdown.swap_timings.swap_start) + .InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( + entry, kVizBreakdownBufferAvailableToBufferReady, + (viz_breakdown.presentation_feedback.ready_timestamp - + viz_breakdown.presentation_feedback.available_timestamp) + .InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( + entry, kVizBreakdownBufferReadyToLatch, + (viz_breakdown.presentation_feedback.latch_timestamp - + viz_breakdown.presentation_feedback.ready_timestamp) + .InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( + entry, kVizBreakdownLatchToSwapEnd, + (viz_breakdown.swap_timings.swap_end - + viz_breakdown.presentation_feedback.latch_timestamp) + .InMicroseconds()); + test_ukm_recorder_->ExpectEntryMetric( + entry, kVizBreakdownSwapEndToPresentationCompositorFrame, + (viz_breakdown.presentation_feedback.timestamp - + viz_breakdown.swap_timings.swap_end) + .InMicroseconds()); test_ukm_recorder_->ExpectEntryMetric( entry, kTotalLatencyToSwapBegin, - (swap_start_time - event_time).InMicroseconds()); + (viz_breakdown.swap_timings.swap_start - + event_dispatch_times[i].generated) + .InMicroseconds()); test_ukm_recorder_->ExpectEntryMetric( - entry, kTotalLatency, (present_time - event_time).InMicroseconds()); + entry, kTotalLatency, + (viz_breakdown.presentation_feedback.timestamp - + event_dispatch_times[i].generated) + .InMicroseconds()); } } |