diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2022-09-29 16:16:15 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2022-11-09 10:04:06 +0000 |
commit | a95a7417ad456115a1ef2da4bb8320531c0821f1 (patch) | |
tree | edcd59279e486d2fd4a8f88a7ed025bcf925c6e6 /chromium/cc | |
parent | 33fc33aa94d4add0878ec30dc818e34e1dd3cc2a (diff) | |
download | qtwebengine-chromium-a95a7417ad456115a1ef2da4bb8320531c0821f1.tar.gz |
BASELINE: Update Chromium to 106.0.5249.126
Change-Id: Ib0bb21c437a7d1686e21c33f2d329f2ac425b7ab
Reviewed-on: https://codereview.qt-project.org/c/qt/qtwebengine-chromium/+/438936
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/cc')
203 files changed, 5978 insertions, 2962 deletions
diff --git a/chromium/cc/BUILD.gn b/chromium/cc/BUILD.gn index e52844d44a8..d8b4ab2e4db 100644 --- a/chromium/cc/BUILD.gn +++ b/chromium/cc/BUILD.gn @@ -43,6 +43,7 @@ cc_component("cc") { "input/browser_controls_offset_manager_client.h", "input/browser_controls_state.h", "input/compositor_input_interfaces.h", + "input/input_handler.cc", "input/input_handler.h", "input/layer_selection_bound.cc", "input/layer_selection_bound.h", @@ -74,8 +75,6 @@ cc_component("cc") { "input/snap_fling_curve.h", "input/snap_selection_strategy.cc", "input/snap_selection_strategy.h", - "input/threaded_input_handler.cc", - "input/threaded_input_handler.h", "input/touch_action.h", "layers/append_quads_data.cc", "layers/append_quads_data.h", @@ -346,8 +345,6 @@ cc_component("cc") { "trees/animation_options.h", "trees/browser_controls_params.cc", "trees/browser_controls_params.h", - "trees/clip_expander.cc", - "trees/clip_expander.h", "trees/clip_node.cc", "trees/clip_node.h", "trees/commit_state.cc", @@ -565,8 +562,6 @@ cc_test_static_library("test_support") { "test/mock_latency_info_swap_promise_monitor.h", "test/mock_layer_tree_mutator.cc", "test/mock_layer_tree_mutator.h", - "test/mock_mutator_host.cc", - "test/mock_mutator_host.h", "test/mock_occlusion_tracker.h", "test/paint_op_helper.h", "test/pixel_comparator.cc", @@ -777,7 +772,6 @@ cc_test("cc_unittests") { "resources/resource_pool_unittest.cc", "scheduler/scheduler_state_machine_unittest.cc", "scheduler/scheduler_unittest.cc", - "test/mock_mutator_host_unittest.cc", "tiles/checker_image_tracker_unittest.cc", "tiles/decoded_image_tracker_unittest.cc", "tiles/gpu_image_decode_cache_unittest.cc", diff --git a/chromium/cc/OWNERS b/chromium/cc/OWNERS index e695df24414..cac07ec5e8a 100644 --- a/chromium/cc/OWNERS +++ b/chromium/cc/OWNERS @@ -49,8 +49,7 @@ flackr@chromium.org skobes@chromium.org # metrics -sadrul@chromium.org -behdadb@chromium.org +jonross@chromium.org # paint sunnyps@chromium.org diff --git a/chromium/cc/animation/animation_host_unittest.cc b/chromium/cc/animation/animation_host_unittest.cc index fa79bed6c24..bebb3bcbdc1 100644 --- a/chromium/cc/animation/animation_host_unittest.cc +++ b/chromium/cc/animation/animation_host_unittest.cc @@ -337,9 +337,7 @@ TEST_F(AnimationHostTest, LayerTreeMutatorUpdateReflectsScrollAnimations) { // Create scroll timeline that links scroll animation and worklet animation // together. - std::vector<double> scroll_offsets; - scroll_offsets.push_back(0); - scroll_offsets.push_back(100); + ScrollTimeline::ScrollOffsets scroll_offsets(0, 100); auto scroll_timeline = ScrollTimeline::Create( element_id, ScrollTimeline::ScrollDown, scroll_offsets); @@ -380,9 +378,7 @@ TEST_F(AnimationHostTest, TickScrollLinkedAnimation) { // Create scroll timeline that links scroll animation and scroll-linked // animation together. - std::vector<double> scroll_offsets; - scroll_offsets.push_back(0); - scroll_offsets.push_back(100); + ScrollTimeline::ScrollOffsets scroll_offsets(0, 100); auto scroll_timeline = ScrollTimeline::Create( element_id_, ScrollTimeline::ScrollDown, scroll_offsets); @@ -458,10 +454,7 @@ TEST_F(AnimationHostTest, ScrollTimelineOffsetUpdatedByScrollAnimation) { timeline_->AttachAnimation(mock_scroll_animation); host_impl_->AddToTicking(mock_scroll_animation); - std::vector<double> scroll_offsets; - scroll_offsets.push_back(0); - scroll_offsets.push_back(100); - + ScrollTimeline::ScrollOffsets scroll_offsets(0, 100); auto scroll_timeline = ScrollTimeline::Create( element_id_, ScrollTimeline::ScrollDown, scroll_offsets); diff --git a/chromium/cc/animation/animation_unittest.cc b/chromium/cc/animation/animation_unittest.cc index 42203ab3fb1..2d35841af50 100644 --- a/chromium/cc/animation/animation_unittest.cc +++ b/chromium/cc/animation/animation_unittest.cc @@ -7,6 +7,7 @@ #include <memory> #include "base/strings/stringprintf.h" +#include "base/test/gtest_util.h" #include "base/time/time.h" #include "cc/animation/animation_delegate.h" #include "cc/animation/animation_host.h" @@ -470,6 +471,28 @@ TEST_F(AnimationTest, AddRemoveAnimationToNonAttachedAnimation) { element_id_, ElementListType::ACTIVE, TargetProperty::FILTER)); } +using AnimationDeathTest = AnimationTest; + +TEST_F(AnimationDeathTest, RemoveAddInSameFrame) { + client_.RegisterElementId(element_id_, ElementListType::ACTIVE); + host_->AddAnimationTimeline(timeline_); + timeline_->AttachAnimation(animation_); + animation_->AttachElement(element_id_); + + EXPECT_TRUE(client_.mutators_need_commit()); + client_.set_mutators_need_commit(false); + + const int keyframe_model_id = + AddOpacityTransitionToAnimation(animation_.get(), 1., .7f, .3f, false); + host_->PushPropertiesTo(host_impl_, client_.GetPropertyTrees()); + + animation_->RemoveKeyframeModel(keyframe_model_id); + AddOpacityTransitionToAnimation(animation_.get(), 1., .7f, .3f, false, + keyframe_model_id); + EXPECT_DCHECK_DEATH( + host_->PushPropertiesTo(host_impl_, client_.GetPropertyTrees())); +} + TEST_F(AnimationTest, AddRemoveAnimationCausesSetNeedsCommit) { client_.RegisterElementId(element_id_, ElementListType::ACTIVE); host_->AddAnimationTimeline(timeline_); diff --git a/chromium/cc/animation/element_animations.cc b/chromium/cc/animation/element_animations.cc index 153e0997a4f..5473179a222 100644 --- a/chromium/cc/animation/element_animations.cc +++ b/chromium/cc/animation/element_animations.cc @@ -238,9 +238,10 @@ void ElementAnimations::OnColorAnimated(const SkColor& value, gfx::KeyframeModel* keyframe_model) { DCHECK_EQ(keyframe_model->TargetProperty(), TargetProperty::CSS_CUSTOM_PROPERTY); - OnCustomPropertyAnimated(PaintWorkletInput::PropertyValue(value), - KeyframeModel::ToCcKeyframeModel(keyframe_model), - target_property_id); + // TODO(crbug/1308932): Remove FromColor and make all SkColor4f. + OnCustomPropertyAnimated( + PaintWorkletInput::PropertyValue(SkColor4f::FromColor(value)), + KeyframeModel::ToCcKeyframeModel(keyframe_model), target_property_id); } void ElementAnimations::OnTransformAnimated( diff --git a/chromium/cc/animation/element_animations_unittest.cc b/chromium/cc/animation/element_animations_unittest.cc index 63d3e3d442b..d7d288e2ca5 100644 --- a/chromium/cc/animation/element_animations_unittest.cc +++ b/chromium/cc/animation/element_animations_unittest.cc @@ -1151,9 +1151,11 @@ TEST_F(ElementAnimationsTest, ScrollOffsetRemovalClearsScrollDelta) { ScrollOffsetAnimationCurveFactory::CreateEaseInOutAnimationForTesting( target_value)); - int keyframe_model_id = 1; + int keyframe_model_id = AnimationIdProvider::NextKeyframeModelId(); + int group_id = AnimationIdProvider::NextGroupId(); + std::unique_ptr<KeyframeModel> keyframe_model(KeyframeModel::Create( - std::move(curve), keyframe_model_id, 0, + std::move(curve), keyframe_model_id, group_id, KeyframeModel::TargetPropertyId(TargetProperty::SCROLL_OFFSET))); keyframe_model->set_needs_synchronized_start_time(true); animation_->AddKeyframeModel(std::move(keyframe_model)); @@ -1178,11 +1180,14 @@ TEST_F(ElementAnimationsTest, ScrollOffsetRemovalClearsScrollDelta) { EXPECT_FALSE(animation_impl_->keyframe_effect() ->scroll_offset_animation_was_interrupted()); + keyframe_model_id = AnimationIdProvider::NextKeyframeModelId(); + group_id = AnimationIdProvider::NextGroupId(); + // Now, test the 2-argument version of RemoveKeyframeModel. curve = ScrollOffsetAnimationCurveFactory::CreateEaseInOutAnimationForTesting( target_value); keyframe_model = KeyframeModel::Create( - std::move(curve), keyframe_model_id, 0, + std::move(curve), keyframe_model_id, group_id, KeyframeModel::TargetPropertyId(TargetProperty::SCROLL_OFFSET)); keyframe_model->set_needs_synchronized_start_time(true); animation_->AddKeyframeModel(std::move(keyframe_model)); diff --git a/chromium/cc/animation/keyframe_model.cc b/chromium/cc/animation/keyframe_model.cc index 95d6a7a651b..67a0be62093 100644 --- a/chromium/cc/animation/keyframe_model.cc +++ b/chromium/cc/animation/keyframe_model.cc @@ -20,6 +20,16 @@ namespace cc { +namespace { +#if DCHECK_IS_ON() +int GetNextDebugId() { + static int g_nextDebugId = 0; + g_nextDebugId++; + return g_nextDebugId; +} +#endif +} // namespace + // static const KeyframeModel* KeyframeModel::ToCcKeyframeModel( const gfx::KeyframeModel* keyframe_model) { @@ -92,6 +102,9 @@ std::unique_ptr<KeyframeModel> KeyframeModel::CreateImplInstance( to_return->set_fill_mode(fill_mode()); DCHECK(!to_return->is_controlling_instance_); to_return->is_controlling_instance_ = true; +#if DCHECK_IS_ON() + to_return->debug_id_ = debug_id_; +#endif return to_return; } @@ -104,12 +117,16 @@ KeyframeModel::KeyframeModel(std::unique_ptr<gfx::AnimationCurve> curve, target_property_id.target_property_type()), group_(group_id), target_property_id_(std::move(target_property_id)), +#if DCHECK_IS_ON() + debug_id_(GetNextDebugId()), +#endif 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) {} + affects_pending_elements_(true) { +} KeyframeModel::~KeyframeModel() = default; @@ -158,6 +175,12 @@ bool KeyframeModel::InEffect(base::TimeTicks monotonic_time) const { } void KeyframeModel::PushPropertiesTo(KeyframeModel* other) const { +#if DCHECK_IS_ON() + DCHECK_EQ(debug_id_, other->debug_id_) + << "Attempted to push properties to a model with a mismatched debug id " + "(i.e., different keyframe models). This can happen when keyframe " + "model ids are reused."; +#endif other->element_id_ = element_id_; if (run_state() == KeyframeModel::PAUSED || other->run_state() == KeyframeModel::PAUSED) { diff --git a/chromium/cc/animation/keyframe_model.h b/chromium/cc/animation/keyframe_model.h index 2c9edb9170d..8bc551f3dbe 100644 --- a/chromium/cc/animation/keyframe_model.h +++ b/chromium/cc/animation/keyframe_model.h @@ -8,6 +8,7 @@ #include <memory> #include <string> +#include "base/check.h" #include "base/time/time.h" #include "cc/animation/animation_export.h" #include "cc/paint/element_id.h" @@ -156,6 +157,11 @@ class CC_ANIMATION_EXPORT KeyframeModel : public gfx::KeyframeModel { // value on. ElementId element_id_; +#if DCHECK_IS_ON() + // This id is unique, modulo overflow. Permits quick instance equality checks. + int debug_id_ = 0; +#endif + bool needs_synchronized_start_time_; bool received_finished_event_; diff --git a/chromium/cc/animation/scroll_offset_animation_curve_factory.cc b/chromium/cc/animation/scroll_offset_animation_curve_factory.cc index 761b54947f2..553bffed391 100644 --- a/chromium/cc/animation/scroll_offset_animation_curve_factory.cc +++ b/chromium/cc/animation/scroll_offset_animation_curve_factory.cc @@ -35,7 +35,7 @@ ScrollOffsetAnimationCurveFactory::CreateAnimation( if (scroll_type == ScrollType::kAutoScroll) return CreateLinearAnimation(target_value); - if (base::FeatureList::IsEnabled(features::kImpulseScrollAnimations)) + if (features::IsImpulseScrollAnimationEnabled()) return CreateImpulseAnimation(target_value); return CreateEaseInOutAnimation( diff --git a/chromium/cc/animation/scroll_timeline.cc b/chromium/cc/animation/scroll_timeline.cc index f8d24815f2f..06f54a413a9 100644 --- a/chromium/cc/animation/scroll_timeline.cc +++ b/chromium/cc/animation/scroll_timeline.cc @@ -27,33 +27,23 @@ bool IsReverse(ScrollTimeline::ScrollDirection direction) { direction == ScrollTimeline::ScrollLeft; } -bool ValidateScrollOffsets(const std::vector<double>& scroll_offsets) { - return scroll_offsets.empty() || scroll_offsets.size() >= 2.0; -} - } // namespace -template double ComputeProgress<std::vector<double>>( - double, - const std::vector<double>&); - ScrollTimeline::ScrollTimeline(absl::optional<ElementId> scroller_id, ScrollDirection direction, - const std::vector<double> scroll_offsets, + absl::optional<ScrollOffsets> scroll_offsets, int animation_timeline_id) : AnimationTimeline(animation_timeline_id), pending_id_(scroller_id), direction_(direction), - scroll_offsets_(scroll_offsets) { - DCHECK(ValidateScrollOffsets(scroll_offsets_)); -} + pending_offsets_(scroll_offsets) {} ScrollTimeline::~ScrollTimeline() = default; scoped_refptr<ScrollTimeline> ScrollTimeline::Create( absl::optional<ElementId> scroller_id, ScrollTimeline::ScrollDirection direction, - const std::vector<double> scroll_offsets) { + absl::optional<ScrollOffsets> scroll_offsets) { return base::WrapRefCounted( new ScrollTimeline(scroller_id, direction, scroll_offsets, AnimationIdProvider::NextTimelineId())); @@ -61,15 +51,16 @@ scoped_refptr<ScrollTimeline> ScrollTimeline::Create( scoped_refptr<AnimationTimeline> ScrollTimeline::CreateImplInstance() const { return base::WrapRefCounted( - new ScrollTimeline(pending_id_, direction_, scroll_offsets_, id())); + new ScrollTimeline(pending_id_, direction_, pending_offsets_, id())); } bool ScrollTimeline::IsActive(const ScrollTree& scroll_tree, bool is_active_tree) const { // Blink passes empty scroll offsets when the timeline is inactive. - if (scroll_offsets_.empty()) { + if ((is_active_tree && !active_offsets_) || + (!is_active_tree && !pending_offsets_)) return false; - } + // If pending tree with our scroller hasn't been activated, or the scroller // has been removed (e.g. if it is no longer composited). if ((is_active_tree && !active_id_) || (!is_active_tree && !pending_id_)) @@ -113,35 +104,26 @@ absl::optional<base::TimeTicks> ScrollTimeline::CurrentTime( DCHECK_GE(max_offset, 0); DCHECK_GE(current_offset, 0); - DCHECK_GE(scroll_offsets_.size(), 2u); - double resolved_start_scroll_offset = scroll_offsets_[0]; - double resolved_end_scroll_offset = - scroll_offsets_[scroll_offsets_.size() - 1]; - - // TODO(crbug.com/1060384): Once the spec has been updated to state what the - // expected result is when startScrollOffset >= endScrollOffset, we might need - // to add a special case here. See - // https://github.com/WICG/scroll-animations/issues/20 - - // 3. If current scroll offset is less than startScrollOffset: - if (current_offset < resolved_start_scroll_offset) { - return base::TimeTicks(); + double start_offset = 0; + double end_offset = 0; + if (is_active_tree) { + DCHECK(active_offsets_); + start_offset = active_offsets_->start; + end_offset = active_offsets_->end; + } else { + DCHECK(pending_offsets_); + start_offset = pending_offsets_->start; + end_offset = pending_offsets_->end; } - // 4. If current scroll offset is greater than or equal to endScrollOffset: - if (current_offset >= resolved_end_scroll_offset) { - return base::TimeTicks() + base::Milliseconds(kScrollTimelineDurationMs); - } - - // Otherwise, - // 5.1 Let progress be a result of applying calculate scroll timeline progress - // procedure for current scroll offset. - // 5.2 The current time is the result of evaluating the following expression: - // progress × timeline duration to get the percentage + // TODO(crbug.com/1338167): Update once + // github.com/w3c/csswg-drafts/issues/7401 is resolved. + double progress = + end_offset == start_offset + ? 1 + : (current_offset - start_offset) / (end_offset - start_offset); return base::TimeTicks() + - base::Milliseconds(ComputeProgress<std::vector<double>>( - current_offset, scroll_offsets_) * - kScrollTimelineDurationMs); + base::Milliseconds(progress * kScrollTimelineDurationMs); } void ScrollTimeline::PushPropertiesTo(AnimationTimeline* impl_timeline) { @@ -149,16 +131,12 @@ void ScrollTimeline::PushPropertiesTo(AnimationTimeline* impl_timeline) { DCHECK(impl_timeline); ScrollTimeline* scroll_timeline = ToScrollTimeline(impl_timeline); scroll_timeline->pending_id_ = pending_id_; - // TODO(smcgruer): This leads to incorrect behavior in the current design, - // because we end up using the pending start/end scroll offset for the active - // tree too. Instead we need to either split these (like pending_id_ and - // active_id_) or have a ScrollTimeline per tree. - scroll_timeline->scroll_offsets_ = scroll_offsets_; - DCHECK(ValidateScrollOffsets(scroll_timeline->scroll_offsets_)); + scroll_timeline->pending_offsets_ = pending_offsets_; } void ScrollTimeline::ActivateTimeline() { active_id_ = pending_id_; + active_offsets_ = pending_offsets_; for (auto& kv : id_to_animation_map_) { auto& animation = kv.second; if (animation->IsWorkletAnimation()) @@ -200,17 +178,15 @@ bool ScrollTimeline::TickScrollLinkedAnimations( void ScrollTimeline::UpdateScrollerIdAndScrollOffsets( absl::optional<ElementId> pending_id, - const std::vector<double> scroll_offsets) { - if (pending_id_ == pending_id && scroll_offsets_ == scroll_offsets) { + absl::optional<ScrollOffsets> pending_offsets) { + if (pending_id_ == pending_id && pending_offsets_ == pending_offsets) return; - } // When the scroller id changes it will first be modified in the pending tree. // Then later (when the pending tree is promoted to active) // |ActivateTimeline| will be called and will set the |active_id_|. pending_id_ = pending_id; - scroll_offsets_ = scroll_offsets; - DCHECK(ValidateScrollOffsets(scroll_offsets_)); + pending_offsets_ = pending_offsets; SetNeedsPushProperties(); } diff --git a/chromium/cc/animation/scroll_timeline.h b/chromium/cc/animation/scroll_timeline.h index a6548550235..482a874dc01 100644 --- a/chromium/cc/animation/scroll_timeline.h +++ b/chromium/cc/animation/scroll_timeline.h @@ -35,19 +35,31 @@ class CC_ANIMATION_EXPORT ScrollTimeline : public AnimationTimeline { ScrollRight, }; + struct ScrollOffsets { + ScrollOffsets(double start_offset, double end_offset) { + start = start_offset; + end = end_offset; + } + bool operator==(const ScrollOffsets& other) const { + return start == other.start && end == other.end; + } + double start = 0; + double end = 0; + }; + // 100% is represented as 100s or 100000ms. We store it here in Milliseconds // because that is the time unit returned by functions like CurrentTime. static constexpr double kScrollTimelineDurationMs = 100000; ScrollTimeline(absl::optional<ElementId> scroller_id, ScrollDirection direction, - const std::vector<double> scroll_offsets, + absl::optional<ScrollOffsets> scroll_offsets, int animation_timeline_id); static scoped_refptr<ScrollTimeline> Create( absl::optional<ElementId> scroller_id, ScrollDirection direction, - const std::vector<double> scroll_offsets); + absl::optional<ScrollOffsets> scroll_offsets); // Create a copy of this ScrollTimeline intended for the impl thread in the // compositor. @@ -68,7 +80,7 @@ class CC_ANIMATION_EXPORT ScrollTimeline : public AnimationTimeline { void UpdateScrollerIdAndScrollOffsets( absl::optional<ElementId> scroller_id, - const std::vector<double> scroll_offsets); + absl::optional<ScrollOffsets> scroll_offsets); void PushPropertiesTo(AnimationTimeline* impl_timeline) override; void ActivateTimeline() override; @@ -82,14 +94,14 @@ class CC_ANIMATION_EXPORT ScrollTimeline : public AnimationTimeline { absl::optional<ElementId> GetPendingIdForTest() const { return pending_id_; } ScrollDirection GetDirectionForTest() const { return direction_; } absl::optional<double> GetStartScrollOffsetForTest() const { - if (scroll_offsets_.empty()) + if (!pending_offsets_) return absl::nullopt; - return scroll_offsets_[0]; + return pending_offsets_->start; } absl::optional<double> GetEndScrollOffsetForTest() const { - if (scroll_offsets_.empty()) + if (!pending_offsets_) return absl::nullopt; - return scroll_offsets_[1]; + return pending_offsets_->end; } bool IsScrollTimeline() const override; @@ -108,9 +120,8 @@ class CC_ANIMATION_EXPORT ScrollTimeline : public AnimationTimeline { // it should base its current time on, and where the origin point is. ScrollDirection direction_; - // This defines scroll ranges of the scroller that the ScrollTimeline is - // active within. If no ranges are defined the timeline is inactive. - std::vector<double> scroll_offsets_; + absl::optional<ScrollOffsets> pending_offsets_; + absl::optional<ScrollOffsets> active_offsets_; }; inline ScrollTimeline* ToScrollTimeline(AnimationTimeline* timeline) { @@ -124,53 +135,6 @@ inline const ScrollTimeline* ToScrollTimeline( return static_cast<const ScrollTimeline*>(timeline); } -// https://drafts.csswg.org/scroll-animations-1/#progress-calculation-algorithm -template <typename T> -double ComputeProgress(double current_offset, const T& resolved_offsets) { - // 1. Let scroll offsets be the result of applying the procedure to resolve - // scroll timeline offsets for scrollOffsets. - DCHECK_GE(resolved_offsets.size(), 2u); - // When start offset is greater than end offset, current time is calculated - // outside of this method. - DCHECK_LT(resolved_offsets[0], resolved_offsets[resolved_offsets.size() - 1]); - // When animation is in before or after phase, current time is calculated - // outside of this method. - DCHECK_GE(current_offset, resolved_offsets[0]); - DCHECK_LT(current_offset, resolved_offsets[resolved_offsets.size() - 1]); - // 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. - - // 2. Let offset index correspond to the position of the last offset in scroll - // offsets whose value is less than or equal to offset and the value at the - // following position greater than offset - int offset_index; - for (offset_index = resolved_offsets.size() - 2; - offset_index > 0 && resolved_offsets[offset_index] > current_offset; - offset_index--) { - DCHECK_LT(current_offset, resolved_offsets[offset_index + 1]); - } - // 3. Let start offset be the offset value at position offset index in - // scroll offsets. - double start_offset = resolved_offsets[offset_index]; - // 4. Let end offset be the value of next offset in scroll offsets after - // start offset. - double end_offset = resolved_offsets[offset_index + 1]; - // 5. Let size be the number of offsets in scroll offsets. - unsigned int size = resolved_offsets.size(); - // 6. Let offset weight be the result of evaluating 1 / (size - 1). - double offset_weight = 1.0 / (size - 1); - // 7. Let interval progress be the result of evaluating - // (offset - start offset) / (end offset - start offset). - double interval_progress = - (current_offset - start_offset) / (end_offset - start_offset); - // 8. Return the result of evaluating - // (offset index + interval progress) × offset weight. - return (offset_index + interval_progress) * offset_weight; -} - } // namespace cc #endif // CC_ANIMATION_SCROLL_TIMELINE_H_ diff --git a/chromium/cc/animation/scroll_timeline_unittest.cc b/chromium/cc/animation/scroll_timeline_unittest.cc index 6d66c54cf6b..5318a05efe6 100644 --- a/chromium/cc/animation/scroll_timeline_unittest.cc +++ b/chromium/cc/animation/scroll_timeline_unittest.cc @@ -24,6 +24,12 @@ static constexpr double time_error_ms = 0.001; #define EXPECT_SCROLL_TIMELINE_TIME_NEAR(expected, value) \ EXPECT_NEAR(expected, ToDouble(value), time_error_ms) +#define EXPECT_SCROLL_TIMELINE_BEFORE_START(value) \ + EXPECT_LT(ToDouble(value), 0); + +#define EXPECT_SCROLL_TIMELINE_AFTER_END(value) \ + EXPECT_GT(ToDouble(value), ScrollTimeline::kScrollTimelineDurationMs); + void SetScrollOffset(PropertyTrees* property_trees, ElementId scroller_id, gfx::PointF offset) { @@ -123,9 +129,7 @@ class ScrollTimelineTest : public ::testing::Test, }; TEST_F(ScrollTimelineTest, BasicCurrentTimeCalculations) { - std::vector<double> scroll_offsets; - scroll_offsets.push_back(0); - scroll_offsets.push_back(100); + ScrollTimeline::ScrollOffsets scroll_offsets(0, 100); scoped_refptr<ScrollTimeline> vertical_timeline = ScrollTimeline::Create( scroller_id(), ScrollTimeline::ScrollDown, scroll_offsets); @@ -150,118 +154,6 @@ TEST_F(ScrollTimelineTest, BasicCurrentTimeCalculations) { horizontal_timeline->CurrentTime(scroll_tree(), false)); } -TEST_F(ScrollTimelineTest, MultipleScrollOffsetsCurrentTimeCalculations) { - double scroll_size = - content_size().height() - container_size().height(); // 400 - - std::vector<double> scroll_offsets; - scroll_offsets.push_back(0); - scroll_offsets.push_back(100.0); - scroll_offsets.push_back(250.0); - scroll_offsets.push_back(scroll_size); - - scoped_refptr<ScrollTimeline> vertical_timeline = ScrollTimeline::Create( - scroller_id(), ScrollTimeline::ScrollDown, scroll_offsets); - - unsigned int offset = 0; - double w = 1.0 / 3.0; // offset weight - double p = 0; // progress within the offset - - // Scale necessary to convert absolute unit times to progress based values - double scale = ScrollTimeline::kScrollTimelineDurationMs / scroll_size; - - SetScrollOffset(&property_trees(), scroller_id(), gfx::PointF()); - EXPECT_SCROLL_TIMELINE_TIME_NEAR( - (offset + p) * w * scroll_size * scale, - vertical_timeline->CurrentTime(scroll_tree(), false)); - - p = (70.0 - 0.0) / (100.0 - 0.0); - SetScrollOffset(&property_trees(), scroller_id(), gfx::PointF(0, 70)); - EXPECT_SCROLL_TIMELINE_TIME_NEAR( - (offset + p) * w * scroll_size * scale, - vertical_timeline->CurrentTime(scroll_tree(), false)); - - offset = 1; - p = 0; - SetScrollOffset(&property_trees(), scroller_id(), gfx::PointF(0, 100)); - EXPECT_SCROLL_TIMELINE_TIME_NEAR( - (offset + p) * w * scroll_size * scale, - vertical_timeline->CurrentTime(scroll_tree(), false)); - - p = (150.0 - 100.0) / (250.0 - 100.0); - SetScrollOffset(&property_trees(), scroller_id(), gfx::PointF(0, 150)); - EXPECT_SCROLL_TIMELINE_TIME_NEAR( - (offset + p) * w * scroll_size * scale, - vertical_timeline->CurrentTime(scroll_tree(), false)); - - offset = 2; - p = 0; - SetScrollOffset(&property_trees(), scroller_id(), gfx::PointF(0, 250)); - EXPECT_SCROLL_TIMELINE_TIME_NEAR( - (offset + p) * w * scroll_size * scale, - vertical_timeline->CurrentTime(scroll_tree(), false)); - - p = (350.0 - 250.0) / (400.0 - 250.0); - SetScrollOffset(&property_trees(), scroller_id(), gfx::PointF(0, 350)); - EXPECT_SCROLL_TIMELINE_TIME_NEAR( - (offset + p) * w * scroll_size * scale, - vertical_timeline->CurrentTime(scroll_tree(), false)); - - SetScrollOffset(&property_trees(), scroller_id(), gfx::PointF(0, 400)); - EXPECT_SCROLL_TIMELINE_TIME_NEAR( - ScrollTimeline::kScrollTimelineDurationMs, - vertical_timeline->CurrentTime(scroll_tree(), false)); -} - -TEST_F(ScrollTimelineTest, OverlappingScrollOffsets) { - double scroll_size = 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); - - // Offset is less than start offset ==> current time is 0. - SetScrollOffset(&property_trees(), scroller_id(), gfx::PointF(0, 300)); - EXPECT_SCROLL_TIMELINE_TIME_NEAR( - 0, vertical_timeline->CurrentTime(scroll_tree(), false)); - - // Scale necessary to convert absolute unit times to progress based values - double scale = ScrollTimeline::kScrollTimelineDurationMs / scroll_size; - - // Offset is greater than end offset ==> current time is 100%. - SetScrollOffset(&property_trees(), scroller_id(), gfx::PointF(0, 360)); - EXPECT_SCROLL_TIMELINE_TIME_NEAR( - scroll_size * scale, - vertical_timeline->CurrentTime(scroll_tree(), false)); - - scroll_offsets = {0.0, 400.0, 200.0}; - - vertical_timeline = ScrollTimeline::Create( - scroller_id(), ScrollTimeline::ScrollDown, scroll_offsets); - - SetScrollOffset(&property_trees(), scroller_id(), gfx::PointF(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( - scroll_size * 0.5 * 0.25 * scale, - vertical_timeline->CurrentTime(scroll_tree(), false)); - - scroll_offsets = {200.0, 0.0, 400.0}; - - vertical_timeline = ScrollTimeline::Create( - scroller_id(), ScrollTimeline::ScrollDown, scroll_offsets); - - SetScrollOffset(&property_trees(), scroller_id(), gfx::PointF(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( - scroll_size * (0.5 + 0.5 * 0.75) * scale, - vertical_timeline->CurrentTime(scroll_tree(), false)); -} - // This test ensures that the ScrollTimeline's active scroller id is correct. We // had a few crashes caused by assuming that the id would be available in the // active tree before the activation happened; see http://crbug.com/853231 @@ -283,9 +175,7 @@ TEST_F(ScrollTimelineTest, ActiveTimeIsSetOnlyAfterPromotion) { container_size()); double scroll_size = content_size().height() - container_size().height(); - std::vector<double> scroll_offsets; - scroll_offsets.push_back(0); - scroll_offsets.push_back(scroll_size); + ScrollTimeline::ScrollOffsets scroll_offsets(0, scroll_size); double halfwayY = scroll_size / 2.; double expectedTime = 0.5 * ScrollTimeline::kScrollTimelineDurationMs; @@ -321,9 +211,7 @@ TEST_F(ScrollTimelineTest, ActiveTimeIsSetOnlyAfterPromotion) { TEST_F(ScrollTimelineTest, CurrentTimeIsAdjustedForPixelSnapping) { double scroll_size = content_size().height() - container_size().height(); - std::vector<double> scroll_offsets; - scroll_offsets.push_back(0); - scroll_offsets.push_back(scroll_size); + ScrollTimeline::ScrollOffsets scroll_offsets(0, scroll_size); scoped_refptr<ScrollTimeline> timeline = ScrollTimeline::Create( scroller_id(), ScrollTimeline::ScrollDown, scroll_offsets); @@ -346,21 +234,20 @@ TEST_F(ScrollTimelineTest, CurrentTimeIsAdjustedForPixelSnapping) { TEST_F(ScrollTimelineTest, CurrentTimeHandlesStartScrollOffset) { double scroll_size = content_size().height() - container_size().height(); const double start_scroll_offset = 20; - std::vector<double> scroll_offsets; - scroll_offsets.push_back(start_scroll_offset); - scroll_offsets.push_back(scroll_size); + ScrollTimeline::ScrollOffsets scroll_offsets(start_scroll_offset, + scroll_size); scoped_refptr<ScrollTimeline> timeline = ScrollTimeline::Create( scroller_id(), ScrollTimeline::ScrollDown, scroll_offsets); - // Unscrolled, the timeline should read a current time of 0 since the current - // offset (0) will be less than the startScrollOffset. + // Unscrolled, the timeline should read a current time of < 0 since the + // current offset (0) will be less than the startScrollOffset. SetScrollOffset(&property_trees(), scroller_id(), gfx::PointF()); - EXPECT_SCROLL_TIMELINE_TIME_NEAR(0, - timeline->CurrentTime(scroll_tree(), false)); + EXPECT_SCROLL_TIMELINE_BEFORE_START( + timeline->CurrentTime(scroll_tree(), false)); SetScrollOffset(&property_trees(), scroller_id(), gfx::PointF(0, 19)); - EXPECT_SCROLL_TIMELINE_TIME_NEAR(0, - timeline->CurrentTime(scroll_tree(), false)); + EXPECT_SCROLL_TIMELINE_BEFORE_START( + timeline->CurrentTime(scroll_tree(), false).value()); SetScrollOffset(&property_trees(), scroller_id(), gfx::PointF(0, 20)); EXPECT_SCROLL_TIMELINE_TIME_NEAR(0, @@ -380,16 +267,13 @@ TEST_F(ScrollTimelineTest, CurrentTimeHandlesStartScrollOffset) { TEST_F(ScrollTimelineTest, CurrentTimeHandlesEndScrollOffset) { double scroll_size = content_size().height() - container_size().height(); const double end_scroll_offset = scroll_size - 20; - std::vector<double> scroll_offsets; - scroll_offsets.push_back(0); // should be absl::nullopt - scroll_offsets.push_back(end_scroll_offset); + ScrollTimeline::ScrollOffsets scroll_offsets(0, end_scroll_offset); scoped_refptr<ScrollTimeline> timeline = ScrollTimeline::Create( scroller_id(), ScrollTimeline::ScrollDown, scroll_offsets); SetScrollOffset(&property_trees(), scroller_id(), gfx::PointF(0, scroll_size)); - EXPECT_SCROLL_TIMELINE_TIME_NEAR(ScrollTimeline::kScrollTimelineDurationMs, - timeline->CurrentTime(scroll_tree(), false)); + EXPECT_SCROLL_TIMELINE_AFTER_END(timeline->CurrentTime(scroll_tree(), false)); SetScrollOffset(&property_trees(), scroller_id(), gfx::PointF(0, scroll_size - 20)); @@ -413,9 +297,8 @@ TEST_F(ScrollTimelineTest, CurrentTimeHandlesCombinedStartAndEndScrollOffset) { double scroll_size = content_size().height() - container_size().height(); double start_scroll_offset = 20; double end_scroll_offset = scroll_size - 50; - std::vector<double> scroll_offsets; - scroll_offsets.push_back(start_scroll_offset); - scroll_offsets.push_back(end_scroll_offset); + ScrollTimeline::ScrollOffsets scroll_offsets(start_scroll_offset, + end_scroll_offset); scoped_refptr<ScrollTimeline> timeline = ScrollTimeline::Create( scroller_id(), ScrollTimeline::ScrollDown, scroll_offsets); SetScrollOffset(&property_trees(), scroller_id(), @@ -427,11 +310,15 @@ TEST_F(ScrollTimelineTest, CurrentTimeHandlesCombinedStartAndEndScrollOffset) { } TEST_F(ScrollTimelineTest, CurrentTimeHandlesEqualStartAndEndScrollOffset) { - std::vector<double> scroll_offsets; - scroll_offsets.push_back(20); - scroll_offsets.push_back(20); + ScrollTimeline::ScrollOffsets scroll_offsets(20, 20); scoped_refptr<ScrollTimeline> timeline = ScrollTimeline::Create( scroller_id(), ScrollTimeline::ScrollDown, scroll_offsets); + + SetScrollOffset(&property_trees(), scroller_id(), gfx::PointF(0, 20)); + + EXPECT_SCROLL_TIMELINE_TIME_NEAR(ScrollTimeline::kScrollTimelineDurationMs, + timeline->CurrentTime(scroll_tree(), false)); + SetScrollOffset(&property_trees(), scroller_id(), gfx::PointF(0, 150)); EXPECT_SCROLL_TIMELINE_TIME_NEAR(ScrollTimeline::kScrollTimelineDurationMs, @@ -440,19 +327,22 @@ TEST_F(ScrollTimelineTest, CurrentTimeHandlesEqualStartAndEndScrollOffset) { TEST_F(ScrollTimelineTest, CurrentTimeHandlesStartOffsetLargerThanEndScrollOffset) { - std::vector<double> scroll_offsets; - scroll_offsets.push_back(50); - scroll_offsets.push_back(10); + ScrollTimeline::ScrollOffsets scroll_offsets(50, 10); scoped_refptr<ScrollTimeline> timeline = ScrollTimeline::Create( scroller_id(), ScrollTimeline::ScrollDown, scroll_offsets); - SetScrollOffset(&property_trees(), scroller_id(), gfx::PointF(0, 40)); - EXPECT_SCROLL_TIMELINE_TIME_NEAR(0, - timeline->CurrentTime(scroll_tree(), false)); - SetScrollOffset(&property_trees(), scroller_id(), gfx::PointF(0, 150)); + // Timeline direction reversed. + SetScrollOffset(&property_trees(), scroller_id(), gfx::PointF(0, 0)); + EXPECT_SCROLL_TIMELINE_AFTER_END(timeline->CurrentTime(scroll_tree(), false)); - EXPECT_SCROLL_TIMELINE_TIME_NEAR(ScrollTimeline::kScrollTimelineDurationMs, - timeline->CurrentTime(scroll_tree(), false)); + SetScrollOffset(&property_trees(), scroller_id(), gfx::PointF(0, 30)); + EXPECT_SCROLL_TIMELINE_TIME_NEAR( + ScrollTimeline::kScrollTimelineDurationMs / 2, + timeline->CurrentTime(scroll_tree(), false)); + + SetScrollOffset(&property_trees(), scroller_id(), gfx::PointF(0, 150)); + EXPECT_SCROLL_TIMELINE_BEFORE_START( + timeline->CurrentTime(scroll_tree(), false)); } TEST_F(ScrollTimelineTest, CurrentTimeHandlesScrollOffsets) { @@ -460,18 +350,17 @@ TEST_F(ScrollTimelineTest, CurrentTimeHandlesScrollOffsets) { const double scroller_height = content_size().height() - container_size().height(); const double end_scroll_offset = scroller_height - 20; - std::vector<double> scroll_offsets; - scroll_offsets.push_back(start_scroll_offset); - scroll_offsets.push_back(end_scroll_offset); + ScrollTimeline::ScrollOffsets scroll_offsets(start_scroll_offset, + end_scroll_offset); scoped_refptr<ScrollTimeline> timeline = ScrollTimeline::Create( scroller_id(), ScrollTimeline::ScrollDown, scroll_offsets); - // Before the start_scroll_offset the current time should be 0 + // Before the start_scroll_offset the current time should be < 0 SetScrollOffset(&property_trees(), scroller_id(), gfx::PointF(0, start_scroll_offset - 10)); - EXPECT_SCROLL_TIMELINE_TIME_NEAR(0, - timeline->CurrentTime(scroll_tree(), false)); + EXPECT_SCROLL_TIMELINE_BEFORE_START( + timeline->CurrentTime(scroll_tree(), false)); // At the end_scroll_offset the current time should be 100% SetScrollOffset(&property_trees(), scroller_id(), @@ -479,19 +368,16 @@ TEST_F(ScrollTimelineTest, CurrentTimeHandlesScrollOffsets) { EXPECT_SCROLL_TIMELINE_TIME_NEAR(ScrollTimeline::kScrollTimelineDurationMs, timeline->CurrentTime(scroll_tree(), false)); - // After the end_scroll_offset the current time should be 100% + // After the end_scroll_offset the current time should be > 100% SetScrollOffset(&property_trees(), scroller_id(), gfx::PointF(0, end_scroll_offset + 10)); - EXPECT_SCROLL_TIMELINE_TIME_NEAR(ScrollTimeline::kScrollTimelineDurationMs, - timeline->CurrentTime(scroll_tree(), false)); + EXPECT_SCROLL_TIMELINE_AFTER_END(timeline->CurrentTime(scroll_tree(), false)); } TEST_F(ScrollTimelineTest, Activeness) { // ScrollTimeline with zero scroller id is inactive. - std::vector<double> scroll_offsets; double scroll_size = content_size().height() - container_size().height(); - scroll_offsets.push_back(0); - scroll_offsets.push_back(scroll_size); + ScrollTimeline::ScrollOffsets scroll_offsets(0, scroll_size); scoped_refptr<ScrollTimeline> inactive_timeline1 = ScrollTimeline::Create( absl::nullopt, ScrollTimeline::ScrollDown, scroll_offsets); EXPECT_FALSE( @@ -511,9 +397,9 @@ TEST_F(ScrollTimelineTest, Activeness) { inactive_timeline2->IsActive(scroll_tree(), true /*is_active_tree*/)); // ScrollTimeline with empty scroll offsets is inactive. - std::vector<double> empty_scroll_offsets; - scoped_refptr<ScrollTimeline> inactive_timeline3 = ScrollTimeline::Create( - scroller_id(), ScrollTimeline::ScrollDown, empty_scroll_offsets); + scoped_refptr<ScrollTimeline> inactive_timeline3 = + ScrollTimeline::Create(scroller_id(), ScrollTimeline::ScrollDown, + /* scroll_offsets */ absl::nullopt); EXPECT_FALSE( inactive_timeline3->IsActive(scroll_tree(), false /*is_active_tree*/)); EXPECT_FALSE( diff --git a/chromium/cc/animation/worklet_animation_unittest.cc b/chromium/cc/animation/worklet_animation_unittest.cc index 7cfe9e80e03..823ae300c0c 100644 --- a/chromium/cc/animation/worklet_animation_unittest.cc +++ b/chromium/cc/animation/worklet_animation_unittest.cc @@ -60,7 +60,7 @@ class MockScrollTimeline : public ScrollTimeline { MockScrollTimeline() : ScrollTimeline(ElementId(), ScrollTimeline::ScrollDown, - std::vector<double>(), + /* scroll_offsets */ absl::nullopt, AnimationIdProvider::NextTimelineId()) {} MOCK_CONST_METHOD2(CurrentTime, absl::optional<base::TimeTicks>(const ScrollTree&, bool)); diff --git a/chromium/cc/base/BUILD.gn b/chromium/cc/base/BUILD.gn index 2d9ea4073b6..29d6aaa9b06 100644 --- a/chromium/cc/base/BUILD.gn +++ b/chromium/cc/base/BUILD.gn @@ -56,6 +56,7 @@ cc_component("base") { "//base", "//base/third_party/dynamic_annotations", "//skia", + "//ui/base:features", "//ui/gfx/animation", "//ui/gfx/geometry", "//ui/gfx/geometry:geometry_skia", diff --git a/chromium/cc/base/features.cc b/chromium/cc/base/features.cc index 0caa7b11875..0f5953f6783 100644 --- a/chromium/cc/base/features.cc +++ b/chromium/cc/base/features.cc @@ -6,6 +6,7 @@ #include "base/feature_list.h" #include "build/build_config.h" +#include "ui/base/ui_base_features.h" namespace features { @@ -20,17 +21,8 @@ namespace features { const base::Feature kAnimatedImageResume = {"AnimatedImageResume", base::FEATURE_DISABLED_BY_DEFAULT}; -// Enables impulse-style scroll animations in place of the default ones. -// -// Note: Do not enable this on the Mac. The animation does not match the system -// scroll animation curve to such an extent that it makes Chromium stand out in -// a bad way. -const base::Feature kImpulseScrollAnimations = { - "ImpulseScrollAnimations", - base::FEATURE_DISABLED_BY_DEFAULT}; - bool IsImpulseScrollAnimationEnabled() { - return base::FeatureList::IsEnabled(features::kImpulseScrollAnimations); + return base::FeatureList::IsEnabled(features::kWindowsScrollingPersonality); } // Whether the compositor should attempt to sync with the scroll handlers before @@ -71,4 +63,7 @@ const base::Feature kNonBlockingCommit{"NonBlockingCommit", const base::Feature kSlidingWindowForDroppedFrameCounter{ "SlidingWindowForDroppedFrameCounter", base::FEATURE_DISABLED_BY_DEFAULT}; + +const base::Feature kNormalPriorityImageDecoding{ + "NormalPriorityImageDecoding", base::FEATURE_DISABLED_BY_DEFAULT}; } // namespace features diff --git a/chromium/cc/base/features.h b/chromium/cc/base/features.h index aab0e400bab..5dd95a616c0 100644 --- a/chromium/cc/base/features.h +++ b/chromium/cc/base/features.h @@ -12,7 +12,6 @@ namespace features { CC_BASE_EXPORT extern const base::Feature kAnimatedImageResume; -CC_BASE_EXPORT extern const base::Feature kImpulseScrollAnimations; CC_BASE_EXPORT extern bool IsImpulseScrollAnimationEnabled(); CC_BASE_EXPORT extern const base::Feature kSynchronizedScrolling; @@ -64,6 +63,11 @@ CC_BASE_EXPORT extern const base::Feature kNonBlockingCommit; // When enabled, DroppedFrameCounter will use an adjusted sliding window // interval specified by field trial params. CC_BASE_EXPORT extern const base::Feature kSlidingWindowForDroppedFrameCounter; + +// When enabled, SupportsBackgroundThreadPriority is set to kNo for +// GpuImageDecodeTaskImpl and SoftwareImageDecodeTaskImpl. +// Introduced to fix https://crbug.com/1116624 +CC_BASE_EXPORT extern const base::Feature kNormalPriorityImageDecoding; } // namespace features #endif // CC_BASE_FEATURES_H_ diff --git a/chromium/cc/base/math_util.cc b/chromium/cc/base/math_util.cc index 7c700f83238..e7f0e37e2b2 100644 --- a/chromium/cc/base/math_util.cc +++ b/chromium/cc/base/math_util.cc @@ -1017,7 +1017,7 @@ void MathUtil::AddToTracedValue(const char* name, res->AppendInteger(gradient.angle()); res->AppendInteger(gradient.step_count()); for (size_t i = 0; i < gradient.step_count(); i++) { - res->AppendDouble(gradient.steps()[i].percent); + res->AppendDouble(gradient.steps()[i].fraction); res->AppendInteger(gradient.steps()[i].alpha); } res->EndArray(); diff --git a/chromium/cc/base/rtree.h b/chromium/cc/base/rtree.h index d59c08a7f22..e1492ce9368 100644 --- a/chromium/cc/base/rtree.h +++ b/chromium/cc/base/rtree.h @@ -152,8 +152,8 @@ class RTree { // This is the count of data elements (rather than total nodes in the tree) size_t num_data_elements_ = 0u; - Branch<T> root_; std::vector<Node<T>> nodes_; + Branch<T> root_; // If false, the rtree encountered overflow does not have reliable bounds. bool has_valid_bounds_ = true; @@ -432,6 +432,7 @@ void RTree<T>::GetAllBoundsRecursive(Node<T>* node, template <typename T> void RTree<T>::Reset() { num_data_elements_ = 0; + root_.subtree = nullptr; nodes_.clear(); root_.bounds = gfx::Rect(); has_valid_bounds_ = true; diff --git a/chromium/cc/base/synced_property.h b/chromium/cc/base/synced_property.h index f5173bcdb68..8d29bc797a7 100644 --- a/chromium/cc/base/synced_property.h +++ b/chromium/cc/base/synced_property.h @@ -5,7 +5,10 @@ #ifndef CC_BASE_SYNCED_PROPERTY_H_ #define CC_BASE_SYNCED_PROPERTY_H_ +#include <utility> + #include "base/memory/ref_counted.h" +#include "third_party/abseil-cpp/absl/types/optional.h" namespace cc { @@ -39,8 +42,7 @@ class SyncedProperty : public base::RefCounted<SyncedProperty<T>> { // Sets the value on the impl thread, due to an impl-thread-originating // action. Returns true if this had any effect. This will remain // impl-thread-only information at first, and will get pulled back to the main - // thread on the next call of PullDeltaToMainThread (which happens right - // before the commit). + // thread on the next call of PullDeltaForMainThread. bool SetCurrent(BaseT current) { DeltaT delta = T::DeltaBetweenBases(current, active_base_); if (active_delta_ == delta) @@ -57,16 +59,27 @@ class SyncedProperty : public base::RefCounted<SyncedProperty<T>> { // Returns the latest active tree delta and also makes a note that this value // was sent to the main thread. DeltaT PullDeltaForMainThread() { - reflected_delta_in_main_tree_ = PendingDelta(); - return reflected_delta_in_main_tree_; + DCHECK(!next_reflected_delta_in_main_tree_.has_value()); + DeltaT result = UnsentDelta(); + if (reflected_delta_in_main_tree_.has_value()) { + next_reflected_delta_in_main_tree_.emplace(result); + } else { + reflected_delta_in_main_tree_.emplace(result); + } + return result; } // Push the latest value from the main thread onto pending tree-associated // state. Returns true if pushing the value results in different values // between the main layer tree and the pending tree. bool PushMainToPending(BaseT main_thread_value) { - reflected_delta_in_pending_tree_ = reflected_delta_in_main_tree_; - reflected_delta_in_main_tree_ = T::IdentityDelta(); + DCHECK(reflected_delta_in_main_tree_.has_value() || + !next_reflected_delta_in_main_tree_.has_value()); + reflected_delta_in_pending_tree_ = + reflected_delta_in_main_tree_.value_or(T::IdentityDelta()); + reflected_delta_in_main_tree_ = + std::move(next_reflected_delta_in_main_tree_); + next_reflected_delta_in_main_tree_.reset(); pending_base_ = main_thread_value; return Current(false) != main_thread_value; @@ -97,16 +110,52 @@ class SyncedProperty : public base::RefCounted<SyncedProperty<T>> { active_value_before_push != current_active_value; } - // This simulates the consequences of the sent value getting committed and - // activated. - void AbortCommit() { - pending_base_ = T::ApplyDelta(pending_base_, reflected_delta_in_main_tree_); - active_base_ = T::ApplyDelta(active_base_, reflected_delta_in_main_tree_); - active_delta_ = - T::DeltaBetweenDeltas(active_delta_, reflected_delta_in_main_tree_); - reflected_delta_in_main_tree_ = T::IdentityDelta(); + void AbortCommit(bool main_frame_applied_deltas) { + // Finish processing the delta that was sent to the main thread, and reset + // the corresponding the delta_in_main_tree_ variable. If + // main_frame_applied_deltas is true, we send the delta on to the active + // tree just as would happen for a successful commit. Otherwise, we treat + // the delta as never having been sent to the main thread and just drop it. + if (next_reflected_delta_in_main_tree_.has_value()) { + // If next_reflected_delta_in_main_tree_ is populated, we know two things: + // - This abort corresponds to next_reflected_delta_in_main_tree_, + // because we only send a "next" BeginMainFrame if the previous one + // has already signaled "ready to commit". + // - The previous main frame has not yet run commit. If it had, then + // PushMainToPending would have promoted + // next_reflected_delta_in_main_tree_ to reflected_delta_in_main_tree_ + // and next_reflected_delta_in_main_tree_ would be empty. + // In this case, if the main thread processed the delta from this aborted + // commit we can simply add the delta to reflected_delta_in_main_tree_. + if (main_frame_applied_deltas) { + reflected_delta_in_main_tree_ = + T::CombineDeltas(reflected_delta_in_main_tree_.value(), + next_reflected_delta_in_main_tree_.value()); + } + next_reflected_delta_in_main_tree_.reset(); + } else { + // There is no "next" main frame, this abort was for the primary. + if (main_frame_applied_deltas) { + DeltaT delta = + reflected_delta_in_main_tree_.value_or(T::IdentityDelta()); + // This simulates the consequences of the sent value getting committed + // and activated. + pending_base_ = T::ApplyDelta(pending_base_, delta); + active_base_ = T::ApplyDelta(active_base_, delta); + active_delta_ = T::DeltaBetweenDeltas(active_delta_, delta); + } + reflected_delta_in_main_tree_.reset(); + } } + // Values sent to the main thread and not yet resolved in the pending or + // active tree. + const absl::optional<DeltaT>& reflected_delta_in_main_tree() const { + return reflected_delta_in_main_tree_; + } + const absl::optional<DeltaT>& next_reflected_delta_in_main_tree() const { + return next_reflected_delta_in_main_tree_; + } // Values as last pushed to the pending or active tree respectively, with no // impl-thread delta applied. BaseT PendingBase() const { return pending_base_; } @@ -122,6 +171,12 @@ class SyncedProperty : public base::RefCounted<SyncedProperty<T>> { reflected_delta_in_pending_tree_); } + DeltaT UnsentDelta() const { + return T::DeltaBetweenDeltas( + PendingDelta(), + reflected_delta_in_main_tree_.value_or(T::IdentityDelta())); + } + void set_clobber_active_value() { clobber_active_value_ = true; } private: @@ -134,9 +189,13 @@ class SyncedProperty : public base::RefCounted<SyncedProperty<T>> { BaseT active_base_ = T::IdentityBase(); // The difference between |active_base_| and the user-perceived value. DeltaT active_delta_ = T::IdentityDelta(); - // The value sent to the main thread on the last BeginMainFrame. This is - // always identity outside of the BeginMainFrame to (aborted)commit interval. - DeltaT reflected_delta_in_main_tree_ = T::IdentityDelta(); + // A value sent to the main thread on a BeginMainFrame, but not yet applied to + // the resulting pending tree. + absl::optional<DeltaT> reflected_delta_in_main_tree_; + // A value sent to the main thread on a BeginMainFrame at a time when + // reflected_delta_in_main_tree_ is populated. This is used when a main frame + // is sent to the main thread before the previous one has committed. + absl::optional<DeltaT> next_reflected_delta_in_main_tree_; // The value that was sent to the main thread for BeginMainFrame for the // current pending tree. This is always identity outside of the // BeginMainFrame to activation interval. @@ -164,6 +223,7 @@ class AdditionGroup { static BaseT ApplyDelta(BaseT v, DeltaT delta) { return v + delta; } static DeltaT DeltaBetweenBases(BaseT v1, BaseT v2) { return v1 - v2; } static DeltaT DeltaBetweenDeltas(DeltaT d1, DeltaT d2) { return d1 - d2; } + static DeltaT CombineDeltas(DeltaT d1, DeltaT d2) { return d1 + d2; } }; class ScaleGroup { @@ -175,6 +235,7 @@ class ScaleGroup { static float ApplyDelta(float v, float delta) { return v * delta; } static float DeltaBetweenBases(float v1, float v2) { return v1 / v2; } static float DeltaBetweenDeltas(float d1, float d2) { return d1 / d2; } + static float CombineDeltas(float d1, float d2) { return d1 * d2; } }; } // namespace cc diff --git a/chromium/cc/input/compositor_input_interfaces.h b/chromium/cc/input/compositor_input_interfaces.h index cdc8b4f10d4..fea6c64acbe 100644 --- a/chromium/cc/input/compositor_input_interfaces.h +++ b/chromium/cc/input/compositor_input_interfaces.h @@ -29,10 +29,9 @@ class ScrollTree; enum class ScrollbarOrientation; // This is the interface that LayerTreeHostImpl and the "graphics" side of the -// compositor uses to talk to the compositor ThreadedInputHandler. This -// interface is two-way; it's used used both to communicate state changes from -// the LayerTree to the input handler and also to query and update state in the -// input handler. +// compositor uses to talk to the compositor InputHandler. This interface is +// two-way; it's used used both to communicate state changes from the LayerTree +// to the input handler and also to query and update state in the input handler. class InputDelegateForCompositor { public: virtual ~InputDelegateForCompositor() = default; diff --git a/chromium/cc/input/threaded_input_handler.cc b/chromium/cc/input/input_handler.cc index be15f495eaa..e6313f3b006 100644 --- a/chromium/cc/input/threaded_input_handler.cc +++ b/chromium/cc/input/input_handler.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "cc/input/threaded_input_handler.h" +#include "cc/input/input_handler.h" #include <utility> #include <vector> @@ -53,45 +53,42 @@ InputHandlerCommitData::~InputHandlerCommitData() = default; // static base::WeakPtr<InputHandler> InputHandler::Create( CompositorDelegateForInput& compositor_delegate) { - auto input_handler = - std::make_unique<ThreadedInputHandler>(compositor_delegate); + auto input_handler = std::make_unique<InputHandler>(compositor_delegate); base::WeakPtr<InputHandler> input_handler_weak = input_handler->AsWeakPtr(); compositor_delegate.BindToInputHandler(std::move(input_handler)); return input_handler_weak; } -ThreadedInputHandler::ThreadedInputHandler( - CompositorDelegateForInput& compositor_delegate) +InputHandler::InputHandler(CompositorDelegateForInput& compositor_delegate) : compositor_delegate_(compositor_delegate), scrollbar_controller_(std::make_unique<ScrollbarController>( &compositor_delegate_.GetImplDeprecated())) {} -ThreadedInputHandler::~ThreadedInputHandler() = default; +InputHandler::~InputHandler() = default; // // =========== InputHandler Interface // -base::WeakPtr<InputHandler> ThreadedInputHandler::AsWeakPtr() const { +base::WeakPtr<InputHandler> InputHandler::AsWeakPtr() const { return weak_factory_.GetWeakPtr(); } -void ThreadedInputHandler::BindToClient(InputHandlerClient* client) { +void InputHandler::BindToClient(InputHandlerClient* client) { DCHECK(input_handler_client_ == nullptr); input_handler_client_ = client; input_handler_client_->SetPrefersReducedMotion(prefers_reduced_motion_); } -InputHandler::ScrollStatus ThreadedInputHandler::ScrollBegin( - ScrollState* scroll_state, - ui::ScrollInputType type) { +InputHandler::ScrollStatus InputHandler::ScrollBegin(ScrollState* scroll_state, + ui::ScrollInputType type) { DCHECK(scroll_state); DCHECK(scroll_state->delta_x() == 0 && scroll_state->delta_y() == 0); InputHandler::ScrollStatus scroll_status; scroll_status.main_thread_scrolling_reasons = MainThreadScrollingReason::kNotScrollingOnMain; - TRACE_EVENT0("cc", "ThreadedInputHandler::ScrollBegin"); + TRACE_EVENT0("cc", "InputHandler::ScrollBegin"); // If this ScrollBegin is non-animated then ensure we cancel any ongoing // animated scrolls. @@ -317,10 +314,9 @@ InputHandler::ScrollStatus ThreadedInputHandler::ScrollBegin( DCHECK(scrolling_node); ActiveTree().SetCurrentlyScrollingNode(scrolling_node); - if (unification_enabled && - !scroll_tree.CanRealizeScrollsOnCompositor(*scrolling_node)) { - scroll_status.needs_main_thread_repaint = true; - } + if (unification_enabled) + scroll_status.main_thread_repaint_reasons = + scroll_tree.GetMainThreadRepaintReasons(*scrolling_node); DidLatchToScroller(*scroll_state, type); @@ -340,10 +336,10 @@ InputHandler::ScrollStatus ThreadedInputHandler::ScrollBegin( return scroll_status; } -InputHandler::ScrollStatus ThreadedInputHandler::RootScrollBegin( +InputHandler::ScrollStatus InputHandler::RootScrollBegin( ScrollState* scroll_state, ui::ScrollInputType type) { - TRACE_EVENT0("cc", "ThreadedInputHandler::RootScrollBegin"); + TRACE_EVENT0("cc", "InputHandler::RootScrollBegin"); if (!OuterViewportScrollNode()) { InputHandler::ScrollStatus scroll_status; scroll_status.thread = InputHandler::ScrollThread::SCROLL_IGNORED; @@ -363,14 +359,14 @@ InputHandler::ScrollStatus ThreadedInputHandler::RootScrollBegin( return scroll_status; } -InputHandlerScrollResult ThreadedInputHandler::ScrollUpdate( +InputHandlerScrollResult InputHandler::ScrollUpdate( ScrollState* scroll_state, base::TimeDelta delayed_by) { DCHECK(scroll_state); // The current_native_scrolling_element should only be set for ScrollBegin. DCHECK(!scroll_state->data()->current_native_scrolling_element()); - TRACE_EVENT2("cc", "ThreadedInputHandler::ScrollUpdate", "dx", + TRACE_EVENT2("cc", "InputHandler::ScrollUpdate", "dx", scroll_state->delta_x(), "dy", scroll_state->delta_y()); if (!CurrentlyScrollingNode()) @@ -494,7 +490,7 @@ InputHandlerScrollResult ThreadedInputHandler::ScrollUpdate( return scroll_result; } -void ThreadedInputHandler::AdjustScrollDeltaForScrollbarSnap( +void InputHandler::AdjustScrollDeltaForScrollbarSnap( ScrollState* scroll_state) { ScrollNode* scroll_node = CurrentlyScrollingNode(); if (!scroll_node || !scroll_node->snap_container_data) @@ -524,7 +520,7 @@ void ThreadedInputHandler::AdjustScrollDeltaForScrollbarSnap( scroll_state->data()->delta_y = snap_position.y() - current_position.y(); } -void ThreadedInputHandler::ScrollEnd(bool should_snap) { +void InputHandler::ScrollEnd(bool should_snap) { scrollbar_controller_->ResetState(); if (!CurrentlyScrollingNode()) return; @@ -556,7 +552,7 @@ void ThreadedInputHandler::ScrollEnd(bool should_snap) { SetNeedsCommit(); } -void ThreadedInputHandler::RecordScrollBegin( +void InputHandler::RecordScrollBegin( ui::ScrollInputType input_type, ScrollBeginThreadState scroll_start_state) { auto tracker_type = GetTrackerTypeForScroll(input_type); @@ -583,12 +579,12 @@ void ThreadedInputHandler::RecordScrollBegin( tracker_type, scrolling_thread); } -void ThreadedInputHandler::RecordScrollEnd(ui::ScrollInputType input_type) { +void InputHandler::RecordScrollEnd(ui::ScrollInputType input_type) { compositor_delegate_.GetImplDeprecated().frame_trackers().StopSequence( GetTrackerTypeForScroll(input_type)); } -InputHandlerPointerResult ThreadedInputHandler::MouseMoveAt( +InputHandlerPointerResult InputHandler::MouseMoveAt( const gfx::Point& viewport_point) { InputHandlerPointerResult result; if (compositor_delegate_.GetSettings() @@ -641,15 +637,14 @@ InputHandlerPointerResult ThreadedInputHandler::MouseMoveAt( return result; } -PointerResultType ThreadedInputHandler::HitTest( - const gfx::PointF& viewport_point) { +PointerResultType InputHandler::HitTest(const gfx::PointF& viewport_point) { return compositor_delegate_.GetSettings() .compositor_threaded_scrollbar_scrolling ? scrollbar_controller_->HitTest(viewport_point) : PointerResultType::kUnhandled; } -InputHandlerPointerResult ThreadedInputHandler::MouseDown( +InputHandlerPointerResult InputHandler::MouseDown( const gfx::PointF& viewport_point, bool shift_modifier) { ScrollbarAnimationController* animation_controller = @@ -672,7 +667,7 @@ InputHandlerPointerResult ThreadedInputHandler::MouseDown( return result; } -InputHandlerPointerResult ThreadedInputHandler::MouseUp( +InputHandlerPointerResult InputHandler::MouseUp( const gfx::PointF& viewport_point) { if (scroll_element_id_mouse_currently_captured_) { ScrollbarAnimationController* animation_controller = @@ -694,27 +689,27 @@ InputHandlerPointerResult ThreadedInputHandler::MouseUp( return result; } -void ThreadedInputHandler::MouseLeave() { +void InputHandler::MouseLeave() { compositor_delegate_.DidMouseLeave(); scroll_element_id_mouse_currently_over_ = ElementId(); } -ElementId ThreadedInputHandler::FindFrameElementIdAtPoint( +ElementId InputHandler::FindFrameElementIdAtPoint( const gfx::PointF& viewport_point) { gfx::PointF device_viewport_point = gfx::ScalePoint( gfx::PointF(viewport_point), compositor_delegate_.DeviceScaleFactor()); return ActiveTree().FindFrameElementIdAtPoint(device_viewport_point); } -void ThreadedInputHandler::RequestUpdateForSynchronousInputHandler() { +void InputHandler::RequestUpdateForSynchronousInputHandler() { UpdateRootLayerStateForSynchronousInputHandler(); } -void ThreadedInputHandler::SetSynchronousInputHandlerRootScrollOffset( +void InputHandler::SetSynchronousInputHandlerRootScrollOffset( const gfx::PointF& root_content_offset) { - TRACE_EVENT2( - "cc", "ThreadedInputHandler::SetSynchronousInputHandlerRootScrollOffset", - "offset_x", root_content_offset.x(), "offset_y", root_content_offset.y()); + TRACE_EVENT2("cc", "InputHandler::SetSynchronousInputHandlerRootScrollOffset", + "offset_x", root_content_offset.x(), "offset_y", + root_content_offset.y()); gfx::Vector2dF physical_delta = root_content_offset - GetViewport().TotalScrollOffset(); @@ -741,8 +736,8 @@ void ThreadedInputHandler::SetSynchronousInputHandlerRootScrollOffset( compositor_delegate_.SetNeedsFullViewportRedraw(); } -void ThreadedInputHandler::PinchGestureBegin(const gfx::Point& anchor, - ui::ScrollInputType source) { +void InputHandler::PinchGestureBegin(const gfx::Point& anchor, + ui::ScrollInputType source) { DCHECK(source == ui::ScrollInputType::kTouchscreen || source == ui::ScrollInputType::kWheel); @@ -778,9 +773,9 @@ void ThreadedInputHandler::PinchGestureBegin(const gfx::Point& anchor, compositor_delegate_.DidStartPinchZoom(); } -void ThreadedInputHandler::PinchGestureUpdate(float magnify_delta, - const gfx::Point& anchor) { - TRACE_EVENT0("cc", "ThreadedInputHandler::PinchGestureUpdate"); +void InputHandler::PinchGestureUpdate(float magnify_delta, + const gfx::Point& anchor) { + TRACE_EVENT0("cc", "InputHandler::PinchGestureUpdate"); if (!InnerViewportScrollNode()) return; has_pinch_zoomed_ = true; @@ -792,7 +787,7 @@ void ThreadedInputHandler::PinchGestureUpdate(float magnify_delta, UpdateRootLayerStateForSynchronousInputHandler(); } -void ThreadedInputHandler::PinchGestureEnd(const gfx::Point& anchor) { +void InputHandler::PinchGestureEnd(const gfx::Point& anchor) { // Some tests create a pinch gesture without creating a viewport scroll node. // In those cases, PinchGestureBegin will not latch to a scroll node. DCHECK(latched_scroll_type_.has_value() || !CurrentlyScrollingNode()); @@ -811,23 +806,23 @@ void ThreadedInputHandler::PinchGestureEnd(const gfx::Point& anchor) { compositor_delegate_.DidEndPinchZoom(); } -void ThreadedInputHandler::SetNeedsAnimateInput() { +void InputHandler::SetNeedsAnimateInput() { compositor_delegate_.GetImplDeprecated().SetNeedsAnimateInput(); } -bool ThreadedInputHandler::IsCurrentlyScrollingViewport() const { +bool InputHandler::IsCurrentlyScrollingViewport() const { auto* node = CurrentlyScrollingNode(); if (!node) return false; return GetViewport().ShouldScroll(*node); } -EventListenerProperties ThreadedInputHandler::GetEventListenerProperties( +EventListenerProperties InputHandler::GetEventListenerProperties( EventListenerClass event_class) const { return ActiveTree().event_listener_properties(event_class); } -bool ThreadedInputHandler::HasBlockingWheelEventHandlerAt( +bool InputHandler::HasBlockingWheelEventHandlerAt( const gfx::Point& viewport_point) const { gfx::PointF device_viewport_point = gfx::ScalePoint( gfx::PointF(viewport_point), compositor_delegate_.DeviceScaleFactor()); @@ -840,7 +835,7 @@ bool ThreadedInputHandler::HasBlockingWheelEventHandlerAt( } InputHandler::TouchStartOrMoveEventListenerType -ThreadedInputHandler::EventListenerTypeForTouchStartOrMoveAt( +InputHandler::EventListenerTypeForTouchStartOrMoveAt( const gfx::Point& viewport_point, TouchAction* out_touch_action) { gfx::PointF device_viewport_point = gfx::ScalePoint( @@ -894,20 +889,19 @@ ThreadedInputHandler::EventListenerTypeForTouchStartOrMoveAt( } std::unique_ptr<LatencyInfoSwapPromiseMonitor> -ThreadedInputHandler::CreateLatencyInfoSwapPromiseMonitor( - ui::LatencyInfo* latency) { +InputHandler::CreateLatencyInfoSwapPromiseMonitor(ui::LatencyInfo* latency) { return compositor_delegate_.GetImplDeprecated() .CreateLatencyInfoSwapPromiseMonitor(latency); } std::unique_ptr<EventsMetricsManager::ScopedMonitor> -ThreadedInputHandler::GetScopedEventMetricsMonitor( +InputHandler::GetScopedEventMetricsMonitor( EventsMetricsManager::ScopedMonitor::DoneCallback done_callback) { return compositor_delegate_.GetImplDeprecated().GetScopedEventMetricsMonitor( std::move(done_callback)); } -ScrollElasticityHelper* ThreadedInputHandler::CreateScrollElasticityHelper() { +ScrollElasticityHelper* InputHandler::CreateScrollElasticityHelper() { DCHECK(!scroll_elasticity_helper_); if (compositor_delegate_.GetSettings().enable_elastic_overscroll) { scroll_elasticity_helper_.reset( @@ -917,14 +911,14 @@ ScrollElasticityHelper* ThreadedInputHandler::CreateScrollElasticityHelper() { return scroll_elasticity_helper_.get(); } -void ThreadedInputHandler::DestroyScrollElasticityHelper() { +void InputHandler::DestroyScrollElasticityHelper() { // Remove any stretch before destroying helper. scroll_elasticity_helper_->SetStretchAmount(gfx::Vector2dF()); scroll_elasticity_helper_.reset(); } -bool ThreadedInputHandler::GetScrollOffsetForLayer(ElementId element_id, - gfx::PointF* offset) { +bool InputHandler::GetScrollOffsetForLayer(ElementId element_id, + gfx::PointF* offset) { ScrollTree& scroll_tree = GetScrollTree(); ScrollNode* scroll_node = scroll_tree.FindNodeFromElementId(element_id); if (!scroll_node) @@ -933,8 +927,8 @@ bool ThreadedInputHandler::GetScrollOffsetForLayer(ElementId element_id, return true; } -bool ThreadedInputHandler::ScrollLayerTo(ElementId element_id, - const gfx::PointF& offset) { +bool InputHandler::ScrollLayerTo(ElementId element_id, + const gfx::PointF& offset) { ScrollTree& scroll_tree = GetScrollTree(); ScrollNode* scroll_node = scroll_tree.FindNodeFromElementId(element_id); if (!scroll_node) @@ -946,7 +940,7 @@ bool ThreadedInputHandler::ScrollLayerTo(ElementId element_id, return true; } -bool ThreadedInputHandler::ScrollingShouldSwitchtoMainThread() { +bool InputHandler::ScrollingShouldSwitchtoMainThread() { DCHECK(!base::FeatureList::IsEnabled(features::kScrollUnification)); ScrollTree& scroll_tree = GetScrollTree(); ScrollNode* scroll_node = scroll_tree.CurrentlyScrollingNode(); @@ -963,7 +957,7 @@ bool ThreadedInputHandler::ScrollingShouldSwitchtoMainThread() { return false; } -bool ThreadedInputHandler::GetSnapFlingInfoAndSetAnimatingSnapTarget( +bool InputHandler::GetSnapFlingInfoAndSetAnimatingSnapTarget( const gfx::Vector2dF& natural_displacement_in_viewport, gfx::PointF* out_initial_position, gfx::PointF* out_target_position) { @@ -995,7 +989,7 @@ bool ThreadedInputHandler::GetSnapFlingInfoAndSetAnimatingSnapTarget( return true; } -void ThreadedInputHandler::ScrollEndForSnapFling(bool did_finish) { +void InputHandler::ScrollEndForSnapFling(bool did_finish) { ScrollNode* scroll_node = CurrentlyScrollingNode(); // When a snap fling animation reaches its intended target then we update the // scrolled node's snap targets. This also ensures blink learns about the new @@ -1012,7 +1006,7 @@ void ThreadedInputHandler::ScrollEndForSnapFling(bool did_finish) { ScrollEnd(false /* should_snap */); } -void ThreadedInputHandler::NotifyInputEvent() { +void InputHandler::NotifyInputEvent() { compositor_delegate_.GetImplDeprecated().NotifyInputEvent(); } @@ -1020,8 +1014,7 @@ void ThreadedInputHandler::NotifyInputEvent() { // =========== InputDelegateForCompositor Interface // -void ThreadedInputHandler::ProcessCommitDeltas( - CompositorCommitData* commit_data) { +void InputHandler::ProcessCommitDeltas(CompositorCommitData* commit_data) { DCHECK(commit_data); if (ActiveTree().LayerListIsEmpty()) return; @@ -1080,7 +1073,7 @@ void ThreadedInputHandler::ProcessCommitDeltas( last_latched_scroller_ = ElementId(); } -void ThreadedInputHandler::TickAnimations(base::TimeTicks monotonic_time) { +void InputHandler::TickAnimations(base::TimeTicks monotonic_time) { if (input_handler_client_) { // This does not set did_animate, because if the InputHandlerClient // changes anything it will be through the InputHandler interface which @@ -1089,36 +1082,35 @@ void ThreadedInputHandler::TickAnimations(base::TimeTicks monotonic_time) { } } -void ThreadedInputHandler::WillShutdown() { +void InputHandler::WillShutdown() { if (input_handler_client_) { - input_handler_client_->WillShutdown(); - input_handler_client_ = nullptr; + input_handler_client_.ExtractAsDangling()->WillShutdown(); } if (scroll_elasticity_helper_) scroll_elasticity_helper_.reset(); } -void ThreadedInputHandler::WillDraw() { +void InputHandler::WillDraw() { if (input_handler_client_) input_handler_client_->ReconcileElasticOverscrollAndRootScroll(); } -void ThreadedInputHandler::WillBeginImplFrame(const viz::BeginFrameArgs& args) { +void InputHandler::WillBeginImplFrame(const viz::BeginFrameArgs& args) { if (input_handler_client_) { scrollbar_controller_->WillBeginImplFrame(); input_handler_client_->DeliverInputForBeginFrame(args); } } -void ThreadedInputHandler::DidCommit() { +void InputHandler::DidCommit() { // In high latency mode commit cannot finish within the same frame. We need to // flush input here to make sure they got picked up by |PrepareTiles()|. if (input_handler_client_ && compositor_delegate_.IsInHighLatencyMode()) input_handler_client_->DeliverInputForHighLatencyMode(); } -void ThreadedInputHandler::DidActivatePendingTree() { +void InputHandler::DidActivatePendingTree() { // The previous scrolling node might no longer exist in the new tree. if (!CurrentlyScrollingNode()) ClearCurrentlyScrollingNode(); @@ -1128,24 +1120,22 @@ void ThreadedInputHandler::DidActivatePendingTree() { UpdateRootLayerStateForSynchronousInputHandler(); } -void ThreadedInputHandler::RootLayerStateMayHaveChanged() { +void InputHandler::RootLayerStateMayHaveChanged() { UpdateRootLayerStateForSynchronousInputHandler(); } -void ThreadedInputHandler::DidRegisterScrollbar( - ElementId scroll_element_id, - ScrollbarOrientation orientation) { +void InputHandler::DidRegisterScrollbar(ElementId scroll_element_id, + ScrollbarOrientation orientation) { scrollbar_controller_->DidRegisterScrollbar(scroll_element_id, orientation); } -void ThreadedInputHandler::DidUnregisterScrollbar( - ElementId scroll_element_id, - ScrollbarOrientation orientation) { +void InputHandler::DidUnregisterScrollbar(ElementId scroll_element_id, + ScrollbarOrientation orientation) { scrollbar_controller_->DidUnregisterScrollbar(scroll_element_id, orientation); } -void ThreadedInputHandler::ScrollOffsetAnimationFinished() { - TRACE_EVENT0("cc", "ThreadedInputHandler::ScrollOffsetAnimationFinished"); +void InputHandler::ScrollOffsetAnimationFinished() { + TRACE_EVENT0("cc", "InputHandler::ScrollOffsetAnimationFinished"); // ScrollOffsetAnimationFinished is called in two cases: // 1- smooth scrolling animation is over (IsAnimatingForSnap == false). // 2- snap scroll animation is over (IsAnimatingForSnap == true). @@ -1175,8 +1165,7 @@ void ThreadedInputHandler::ScrollOffsetAnimationFinished() { } } -void ThreadedInputHandler::SetPrefersReducedMotion( - bool prefers_reduced_motion) { +void InputHandler::SetPrefersReducedMotion(bool prefers_reduced_motion) { if (prefers_reduced_motion_ == prefers_reduced_motion) return; prefers_reduced_motion_ = prefers_reduced_motion; @@ -1185,11 +1174,11 @@ void ThreadedInputHandler::SetPrefersReducedMotion( input_handler_client_->SetPrefersReducedMotion(prefers_reduced_motion_); } -bool ThreadedInputHandler::IsCurrentlyScrolling() const { +bool InputHandler::IsCurrentlyScrolling() const { return CurrentlyScrollingNode(); } -ActivelyScrollingType ThreadedInputHandler::GetActivelyScrollingType() const { +ActivelyScrollingType InputHandler::GetActivelyScrollingType() const { if (!CurrentlyScrollingNode()) return ActivelyScrollingType::kNone; @@ -1205,49 +1194,49 @@ ActivelyScrollingType ThreadedInputHandler::GetActivelyScrollingType() const { return ActivelyScrollingType::kPrecise; } -ScrollNode* ThreadedInputHandler::CurrentlyScrollingNode() { +ScrollNode* InputHandler::CurrentlyScrollingNode() { return GetScrollTree().CurrentlyScrollingNode(); } -const ScrollNode* ThreadedInputHandler::CurrentlyScrollingNode() const { +const ScrollNode* InputHandler::CurrentlyScrollingNode() const { return GetScrollTree().CurrentlyScrollingNode(); } -ScrollTree& ThreadedInputHandler::GetScrollTree() { +ScrollTree& InputHandler::GetScrollTree() { return compositor_delegate_.GetScrollTree(); } -ScrollTree& ThreadedInputHandler::GetScrollTree() const { +ScrollTree& InputHandler::GetScrollTree() const { return compositor_delegate_.GetScrollTree(); } -ScrollNode* ThreadedInputHandler::InnerViewportScrollNode() const { +ScrollNode* InputHandler::InnerViewportScrollNode() const { return ActiveTree().InnerViewportScrollNode(); } -ScrollNode* ThreadedInputHandler::OuterViewportScrollNode() const { +ScrollNode* InputHandler::OuterViewportScrollNode() const { return ActiveTree().OuterViewportScrollNode(); } -Viewport& ThreadedInputHandler::GetViewport() const { +Viewport& InputHandler::GetViewport() const { return compositor_delegate_.GetImplDeprecated().viewport(); } -void ThreadedInputHandler::SetNeedsCommit() { +void InputHandler::SetNeedsCommit() { compositor_delegate_.SetNeedsCommit(); } -LayerTreeImpl& ThreadedInputHandler::ActiveTree() { +LayerTreeImpl& InputHandler::ActiveTree() { DCHECK(compositor_delegate_.GetImplDeprecated().active_tree()); return *compositor_delegate_.GetImplDeprecated().active_tree(); } -LayerTreeImpl& ThreadedInputHandler::ActiveTree() const { +LayerTreeImpl& InputHandler::ActiveTree() const { DCHECK(compositor_delegate_.GetImplDeprecated().active_tree()); return *compositor_delegate_.GetImplDeprecated().active_tree(); } -FrameSequenceTrackerType ThreadedInputHandler::GetTrackerTypeForScroll( +FrameSequenceTrackerType InputHandler::GetTrackerTypeForScroll( ui::ScrollInputType input_type) const { switch (input_type) { case ui::ScrollInputType::kWheel: @@ -1261,7 +1250,7 @@ FrameSequenceTrackerType ThreadedInputHandler::GetTrackerTypeForScroll( } } -bool ThreadedInputHandler::IsMainThreadScrolling( +bool InputHandler::IsMainThreadScrolling( const InputHandler::ScrollStatus& status, const ScrollNode* scroll_node) const { if (status.thread == InputHandler::ScrollThread::SCROLL_ON_MAIN_THREAD) { @@ -1277,14 +1266,14 @@ bool ThreadedInputHandler::IsMainThreadScrolling( return false; } -float ThreadedInputHandler::LineStep() const { +float InputHandler::LineStep() const { return kPixelsPerLineStep; } // TODO(mehdika): There is some redundancy between this function and // ScrollbarController::GetScrollDistanceForScrollbarPart, these two need to be // kept in sync. -gfx::Vector2dF ThreadedInputHandler::ResolveScrollGranularityToPixels( +gfx::Vector2dF InputHandler::ResolveScrollGranularityToPixels( const ScrollNode& scroll_node, const gfx::Vector2dF& scroll_delta, ui::ScrollGranularity granularity) { @@ -1320,7 +1309,7 @@ gfx::Vector2dF ThreadedInputHandler::ResolveScrollGranularityToPixels( return pixel_delta; } -InputHandler::ScrollStatus ThreadedInputHandler::TryScroll( +InputHandler::ScrollStatus InputHandler::TryScroll( const ScrollTree& scroll_tree, ScrollNode* scroll_node) const { DCHECK(!base::FeatureList::IsEnabled(features::kScrollUnification)); @@ -1391,7 +1380,7 @@ InputHandler::ScrollStatus ThreadedInputHandler::TryScroll( return scroll_status; } -base::flat_set<int> ThreadedInputHandler::NonFastScrollableNodes( +base::flat_set<int> InputHandler::NonFastScrollableNodes( const gfx::PointF& device_viewport_point) const { base::flat_set<int> non_fast_scrollable_nodes; @@ -1404,7 +1393,7 @@ base::flat_set<int> ThreadedInputHandler::NonFastScrollableNodes( return non_fast_scrollable_nodes; } -ScrollNode* ThreadedInputHandler::FindScrollNodeForCompositedScrolling( +ScrollNode* InputHandler::FindScrollNodeForCompositedScrolling( const gfx::PointF& device_viewport_point, LayerImpl* layer_impl, bool* scroll_on_main_thread, @@ -1486,8 +1475,7 @@ ScrollNode* ThreadedInputHandler::FindScrollNodeForCompositedScrolling( return impl_scroll_node; } -ThreadedInputHandler::ScrollHitTestResult -ThreadedInputHandler::HitTestScrollNode( +InputHandler::ScrollHitTestResult InputHandler::HitTestScrollNode( const gfx::PointF& device_viewport_point) const { ScrollHitTestResult result; result.scroll_node = nullptr; @@ -1559,7 +1547,7 @@ ThreadedInputHandler::HitTestScrollNode( // Requires falling back to main thread scrolling when it hit tests in scrollbar // from touch. -bool ThreadedInputHandler::IsTouchDraggingScrollbar( +bool InputHandler::IsTouchDraggingScrollbar( LayerImpl* first_scrolling_layer_or_scrollbar, ui::ScrollInputType type) { return first_scrolling_layer_or_scrollbar && @@ -1567,7 +1555,7 @@ bool ThreadedInputHandler::IsTouchDraggingScrollbar( type == ui::ScrollInputType::kTouchscreen; } -ScrollNode* ThreadedInputHandler::GetNodeToScroll(ScrollNode* node) const { +ScrollNode* InputHandler::GetNodeToScroll(ScrollNode* node) const { // Blink has a notion of a "root scroller", which is the scroller in a page // that is considered to host the main content. Typically this will be the // document/LayoutView contents; however, in some situations Blink may choose @@ -1599,7 +1587,7 @@ ScrollNode* ThreadedInputHandler::GetNodeToScroll(ScrollNode* node) const { return node; } -bool ThreadedInputHandler::IsInitialScrollHitTestReliable( +bool InputHandler::IsInitialScrollHitTestReliable( const LayerImpl* layer_impl, const LayerImpl* first_scrolling_layer_or_scrollbar) const { if (!first_scrolling_layer_or_scrollbar) @@ -1641,9 +1629,8 @@ bool ThreadedInputHandler::IsInitialScrollHitTestReliable( return false; } -gfx::Vector2dF ThreadedInputHandler::ComputeScrollDelta( - const ScrollNode& scroll_node, - const gfx::Vector2dF& delta) { +gfx::Vector2dF InputHandler::ComputeScrollDelta(const ScrollNode& scroll_node, + const gfx::Vector2dF& delta) { ScrollTree& scroll_tree = GetScrollTree(); float scale_factor = compositor_delegate_.PageScaleFactor(); @@ -1659,7 +1646,7 @@ gfx::Vector2dF ThreadedInputHandler::ComputeScrollDelta( return new_offset - old_offset; } -bool ThreadedInputHandler::CalculateLocalScrollDeltaAndStartPoint( +bool InputHandler::CalculateLocalScrollDeltaAndStartPoint( const ScrollNode& scroll_node, const gfx::PointF& viewport_point, const gfx::Vector2dF& viewport_delta, @@ -1706,7 +1693,7 @@ bool ThreadedInputHandler::CalculateLocalScrollDeltaAndStartPoint( return true; } -gfx::Vector2dF ThreadedInputHandler::ScrollNodeWithViewportSpaceDelta( +gfx::Vector2dF InputHandler::ScrollNodeWithViewportSpaceDelta( const ScrollNode& scroll_node, const gfx::PointF& viewport_point, const gfx::Vector2dF& viewport_delta) { @@ -1755,7 +1742,7 @@ gfx::Vector2dF ThreadedInputHandler::ScrollNodeWithViewportSpaceDelta( return actual_viewport_end_point - viewport_point; } -gfx::Vector2dF ThreadedInputHandler::ScrollNodeWithLocalDelta( +gfx::Vector2dF InputHandler::ScrollNodeWithLocalDelta( const ScrollNode& scroll_node, const gfx::Vector2dF& local_delta) const { bool scrolls_outer_viewport = scroll_node.scrolls_outer_viewport; @@ -1782,11 +1769,10 @@ gfx::Vector2dF ThreadedInputHandler::ScrollNodeWithLocalDelta( // TODO(danakj): Make this into two functions, one with delta, one with // viewport_point, no bool required. -gfx::Vector2dF ThreadedInputHandler::ScrollSingleNode( - const ScrollNode& scroll_node, - const gfx::Vector2dF& delta, - const gfx::Point& viewport_point, - bool is_direct_manipulation) { +gfx::Vector2dF InputHandler::ScrollSingleNode(const ScrollNode& scroll_node, + const gfx::Vector2dF& delta, + const gfx::Point& viewport_point, + bool is_direct_manipulation) { gfx::Vector2dF adjusted_delta = UserScrollableDelta(scroll_node, delta); // Events representing direct manipulation of the screen (such as gesture @@ -1807,15 +1793,15 @@ gfx::Vector2dF ThreadedInputHandler::ScrollSingleNode( return ScrollNodeWithLocalDelta(scroll_node, adjusted_delta); } -void ThreadedInputHandler::ScrollLatchedScroller(ScrollState* scroll_state, - base::TimeDelta delayed_by) { +void InputHandler::ScrollLatchedScroller(ScrollState* scroll_state, + base::TimeDelta delayed_by) { DCHECK(CurrentlyScrollingNode()); DCHECK(scroll_state); DCHECK(latched_scroll_type_.has_value()); ScrollNode& scroll_node = *CurrentlyScrollingNode(); const gfx::Vector2dF delta(scroll_state->delta_x(), scroll_state->delta_y()); - TRACE_EVENT2("cc", "ThreadedInputHandler::ScrollLatchedScroller", "delta_x", + TRACE_EVENT2("cc", "InputHandler::ScrollLatchedScroller", "delta_x", delta.x(), "delta_y", delta.y()); gfx::Vector2dF applied_delta; gfx::Vector2dF delta_applied_to_content; @@ -1934,18 +1920,16 @@ void ThreadedInputHandler::ScrollLatchedScroller(ScrollState* scroll_state, scroll_state->ConsumeDelta(applied_delta.x(), applied_delta.y()); } -bool ThreadedInputHandler::CanPropagate(ScrollNode* scroll_node, - float x, - float y) { +bool InputHandler::CanPropagate(ScrollNode* scroll_node, float x, float y) { return (x == 0 || scroll_node->overscroll_behavior.x == OverscrollBehavior::Type::kAuto) && (y == 0 || scroll_node->overscroll_behavior.y == OverscrollBehavior::Type::kAuto); } -ScrollNode* ThreadedInputHandler::FindNodeToLatch(ScrollState* scroll_state, - ScrollNode* starting_node, - ui::ScrollInputType type) { +ScrollNode* InputHandler::FindNodeToLatch(ScrollState* scroll_state, + ScrollNode* starting_node, + ui::ScrollInputType type) { ScrollTree& scroll_tree = GetScrollTree(); ScrollNode* scroll_node = nullptr; ScrollNode* first_scrollable_node = nullptr; @@ -1999,7 +1983,7 @@ ScrollNode* ThreadedInputHandler::FindNodeToLatch(ScrollState* scroll_state, return scroll_node; } -void ThreadedInputHandler::UpdateRootLayerStateForSynchronousInputHandler() { +void InputHandler::UpdateRootLayerStateForSynchronousInputHandler() { if (!input_handler_client_) return; input_handler_client_->UpdateRootLayerStateForSynchronousInputHandler( @@ -2009,8 +1993,8 @@ void ThreadedInputHandler::UpdateRootLayerStateForSynchronousInputHandler() { ActiveTree().max_page_scale_factor()); } -void ThreadedInputHandler::DidLatchToScroller(const ScrollState& scroll_state, - ui::ScrollInputType type) { +void InputHandler::DidLatchToScroller(const ScrollState& scroll_state, + ui::ScrollInputType type) { DCHECK(CurrentlyScrollingNode()); deferred_scroll_end_ = false; compositor_delegate_.GetImplDeprecated() @@ -2032,8 +2016,8 @@ void ThreadedInputHandler::DidLatchToScroller(const ScrollState& scroll_state, UpdateScrollSourceInfo(scroll_state, type); } -bool ThreadedInputHandler::CanConsumeDelta(const ScrollState& scroll_state, - const ScrollNode& scroll_node) { +bool InputHandler::CanConsumeDelta(const ScrollState& scroll_state, + const ScrollNode& scroll_node) { gfx::Vector2dF delta_to_scroll; if (scroll_state.is_beginning()) { delta_to_scroll = gfx::Vector2dF(scroll_state.delta_x_hint(), @@ -2066,8 +2050,7 @@ bool ThreadedInputHandler::CanConsumeDelta(const ScrollState& scroll_state, return false; } -bool ThreadedInputHandler::ShouldAnimateScroll( - const ScrollState& scroll_state) const { +bool InputHandler::ShouldAnimateScroll(const ScrollState& scroll_state) const { if (!compositor_delegate_.GetSettings().enable_smooth_scroll) return false; @@ -2077,7 +2060,7 @@ bool ThreadedInputHandler::ShouldAnimateScroll( return !has_precise_scroll_deltas; } -bool ThreadedInputHandler::SnapAtScrollEnd(SnapReason reason) { +bool InputHandler::SnapAtScrollEnd(SnapReason reason) { ScrollNode* scroll_node = CurrentlyScrollingNode(); if (!scroll_node || !scroll_node->snap_container_data.has_value()) return false; @@ -2151,19 +2134,19 @@ bool ThreadedInputHandler::SnapAtScrollEnd(SnapReason reason) { return did_animate; } -bool ThreadedInputHandler::IsAnimatingForSnap() const { +bool InputHandler::IsAnimatingForSnap() const { return scroll_animating_snap_target_ids_ != TargetSnapAreaElementIds(); } -gfx::PointF ThreadedInputHandler::GetVisualScrollOffset( +gfx::PointF InputHandler::GetVisualScrollOffset( const ScrollNode& scroll_node) const { if (scroll_node.scrolls_outer_viewport) return GetViewport().TotalScrollOffset(); return GetScrollTree().current_scroll_offset(scroll_node.element_id); } -void ThreadedInputHandler::ClearCurrentlyScrollingNode() { - TRACE_EVENT0("cc", "ThreadedInputHandler::ClearCurrentlyScrollingNode"); +void InputHandler::ClearCurrentlyScrollingNode() { + TRACE_EVENT0("cc", "InputHandler::ClearCurrentlyScrollingNode"); ActiveTree().ClearCurrentlyScrollingNode(); accumulated_root_overscroll_ = gfx::Vector2dF(); did_scroll_x_for_scroll_gesture_ = false; @@ -2176,7 +2159,7 @@ void ThreadedInputHandler::ClearCurrentlyScrollingNode() { compositor_delegate_.DidEndScroll(); } -bool ThreadedInputHandler::ScrollAnimationUpdateTarget( +bool InputHandler::ScrollAnimationUpdateTarget( const ScrollNode& scroll_node, const gfx::Vector2dF& scroll_delta, base::TimeDelta delayed_by) { @@ -2212,9 +2195,8 @@ bool ThreadedInputHandler::ScrollAnimationUpdateTarget( return animation_updated; } -void ThreadedInputHandler::UpdateScrollSourceInfo( - const ScrollState& scroll_state, - ui::ScrollInputType type) { +void InputHandler::UpdateScrollSourceInfo(const ScrollState& scroll_state, + ui::ScrollInputType type) { if (type == ui::ScrollInputType::kWheel && scroll_state.delta_granularity() == ui::ScrollGranularity::kScrollByPrecisePixel) { @@ -2230,8 +2212,7 @@ void ThreadedInputHandler::UpdateScrollSourceInfo( // Return true if scrollable node for 'ancestor' is the same as 'child' or an // ancestor along the scroll tree. -bool ThreadedInputHandler::IsScrolledBy(LayerImpl* child, - ScrollNode* ancestor) { +bool InputHandler::IsScrolledBy(LayerImpl* child, ScrollNode* ancestor) { DCHECK(ancestor && ancestor->scrollable); if (!child) return false; @@ -2245,7 +2226,7 @@ bool ThreadedInputHandler::IsScrolledBy(LayerImpl* child, return false; } -gfx::Vector2dF ThreadedInputHandler::UserScrollableDelta( +gfx::Vector2dF InputHandler::UserScrollableDelta( const ScrollNode& node, const gfx::Vector2dF& delta) const { gfx::Vector2dF adjusted_delta = delta; @@ -2257,12 +2238,11 @@ gfx::Vector2dF ThreadedInputHandler::UserScrollableDelta( return adjusted_delta; } -bool ThreadedInputHandler::ScrollbarScrollIsActive() { +bool InputHandler::ScrollbarScrollIsActive() { return scrollbar_controller_->ScrollbarScrollIsActive(); } -void ThreadedInputHandler::SetDeferBeginMainFrame( - bool defer_begin_main_frame) const { +void InputHandler::SetDeferBeginMainFrame(bool defer_begin_main_frame) const { compositor_delegate_.SetDeferBeginMainFrame(defer_begin_main_frame); } diff --git a/chromium/cc/input/input_handler.h b/chromium/cc/input/input_handler.h index ae2ff545aa7..83e52afddb7 100644 --- a/chromium/cc/input/input_handler.h +++ b/chromium/cc/input/input_handler.h @@ -9,6 +9,7 @@ #include "base/time/time.h" #include "cc/cc_export.h" +#include "cc/input/compositor_input_interfaces.h" #include "cc/input/event_listener_properties.h" #include "cc/input/main_thread_scrolling_reason.h" #include "cc/input/overscroll_behavior.h" @@ -16,6 +17,7 @@ #include "cc/input/scrollbar.h" #include "cc/input/touch_action.h" #include "cc/metrics/events_metrics_manager.h" +#include "cc/metrics/frame_sequence_metrics.h" #include "cc/paint/element_id.h" #include "components/viz/common/frame_sinks/begin_frame_args.h" #include "ui/events/types/scroll_input_type.h" @@ -36,7 +38,10 @@ namespace cc { class CompositorDelegateForInput; class LatencyInfoSwapPromiseMonitor; +class LayerImpl; +class ScrollbarController; class ScrollElasticityHelper; +class Viewport; enum class PointerResultType { kUnhandled = 0, kScrollbarScroll }; @@ -131,7 +136,7 @@ class CC_EXPORT InputHandlerClient { // main thread about changes that have occurred as a result of input since the // last commit. struct InputHandlerCommitData { - // Defined in threaded_input_handler.cc to avoid inlining since flat_set has + // Defined in input_handler.cc to avoid inlining since flat_set has // non-trivial size destructor. InputHandlerCommitData(); ~InputHandlerCommitData(); @@ -157,9 +162,9 @@ struct InputHandlerCommitData { bool has_scrolled_by_precisiontouchpad = false; }; -// The InputHandler interface is a way for the embedders to interact with the -// input system running on the compositor thread. Each instance of a compositor -// (i.e. a LayerTreeHostImpl) is associated with one InputHandler instance. The +// The InputHandler is a way for the embedders to interact with the input system +// running on the compositor thread. Each instance of a compositor (i.e. a +// LayerTreeHostImpl) is associated with one InputHandler instance. The // InputHandler sits in between the embedder (the UI compositor or Blink) and // the compositor (LayerTreeHostImpl); as such, it must be bound to both. // @@ -169,7 +174,10 @@ struct InputHandlerCommitData { // the InputHandlerClient interface and bind it to the handler by calling // BindToClient on the input handler. This should all be done on the // input-handling thread (i.e. the "compositor" thread if one exists). -class CC_EXPORT InputHandler { +// +// Many methods are virtual for input_handler_proxy_unittest.cc. +// TODO: consider revising these tests to reduce reliance on mocking. +class CC_EXPORT InputHandler : public InputDelegateForCompositor { public: // Creates an instance of the InputHandler and binds it to the layer tree // delegate. The delegate owns the InputHandler so their lifetimes @@ -189,6 +197,9 @@ class CC_EXPORT InputHandler { LAST_SCROLL_STATUS = SCROLL_UNKNOWN }; + explicit InputHandler(CompositorDelegateForInput& compositor_delegate); + ~InputHandler() override; + InputHandler(const InputHandler&) = delete; InputHandler& operator=(const InputHandler&) = delete; @@ -221,12 +232,15 @@ class CC_EXPORT InputHandler { // the main thread to perform a hit test. bool needs_main_thread_hit_test = false; - // Used only in scroll unification. Tells the caller that we have performed + // Used only in scroll unification. A nonzero value means we have performed // the scroll (i.e. updated the offset in the scroll tree) on the compositor // thread, but we will need a main thread lifecycle update + commit before // the user will see the new pixels (for example, because the scroller does - // not have a composited layer). - bool needs_main_thread_repaint = false; + // not have a composited layer). If nonzero, this will be one or more values + // from the MainThreadScrollingReason enum. (Unification avoids setting + // main_thread_scrolling_reasons, to keep that field consistent with + // semantics of ScrollThread::SCROLL_ON_IMPL_THREAD.) + uint32_t main_thread_repaint_reasons = 0; }; enum class TouchStartOrMoveEventListenerType { @@ -235,12 +249,12 @@ class CC_EXPORT InputHandler { HANDLER_ON_SCROLLING_LAYER }; - virtual base::WeakPtr<InputHandler> AsWeakPtr() const = 0; + virtual base::WeakPtr<InputHandler> AsWeakPtr() const; // Binds a client to this handler to receive notifications. Only one client // can be bound to an InputHandler. The client must live at least until the // handler calls WillShutdown() on the client. - virtual void BindToClient(InputHandlerClient* client) = 0; + virtual void BindToClient(InputHandlerClient* client); // Selects a ScrollNode to be "latched" for scrolling using the // |scroll_state| start position. The selected node remains latched until the @@ -250,12 +264,12 @@ class CC_EXPORT InputHandler { // SCROLL_IGNORED if there is nothing to be scrolled at the given // coordinates. virtual ScrollStatus ScrollBegin(ScrollState* scroll_state, - ui::ScrollInputType type) = 0; + ui::ScrollInputType type); // Similar to ScrollBegin, except the hit test is skipped and scroll always // targets at the root layer. virtual ScrollStatus RootScrollBegin(ScrollState* scroll_state, - ui::ScrollInputType type) = 0; + ui::ScrollInputType type); // Scroll the layer selected by |ScrollBegin| by given |scroll_state| delta. // Internally, the delta is transformed to local layer's coordinate space for @@ -271,66 +285,66 @@ class CC_EXPORT InputHandler { // |delayed_by| is the delay from the event that caused the scroll. This is // taken into account when determining the duration of the animation if one // is created. - virtual InputHandlerScrollResult ScrollUpdate(ScrollState* scroll_state, - base::TimeDelta delayed_by) = 0; + virtual InputHandlerScrollResult ScrollUpdate( + ScrollState* scroll_state, + base::TimeDelta delayed_by = base::TimeDelta()); // Stop scrolling the selected layer. Must be called only if ScrollBegin() // returned SCROLL_STARTED. No-op if ScrollBegin wasn't called or didn't // result in a successful scroll latch. Snap to a snap position if // |should_snap| is true. - virtual void ScrollEnd(bool should_snap = false) = 0; + virtual void ScrollEnd(bool should_snap = false); // Called to notify every time scroll-begin/end is attempted by an input // event. virtual void RecordScrollBegin(ui::ScrollInputType input_type, - ScrollBeginThreadState scroll_start_state) = 0; - virtual void RecordScrollEnd(ui::ScrollInputType input_type) = 0; + ScrollBeginThreadState scroll_start_state); + virtual void RecordScrollEnd(ui::ScrollInputType input_type); - virtual PointerResultType HitTest(const gfx::PointF& mouse_position) = 0; + virtual PointerResultType HitTest(const gfx::PointF& mouse_position); virtual InputHandlerPointerResult MouseMoveAt( - const gfx::Point& mouse_position) = 0; + const gfx::Point& mouse_position); // TODO(arakeri): Pass in the modifier instead of a bool once the refactor // (crbug.com/1022097) is done. For details, see crbug.com/1016955. virtual InputHandlerPointerResult MouseDown(const gfx::PointF& mouse_position, - bool shift_modifier) = 0; - virtual InputHandlerPointerResult MouseUp( - const gfx::PointF& mouse_position) = 0; - virtual void MouseLeave() = 0; + bool shift_modifier); + virtual InputHandlerPointerResult MouseUp(const gfx::PointF& mouse_position); + virtual void MouseLeave(); // Returns visible_frame_element_id from the layer hit by the given point. // If the hit test failed, an invalid element ID is returned. virtual ElementId FindFrameElementIdAtPoint( - const gfx::PointF& mouse_position) = 0; + const gfx::PointF& mouse_position); // Requests a callback to UpdateRootLayerStateForSynchronousInputHandler() // giving the current root scroll and page scale information. - virtual void RequestUpdateForSynchronousInputHandler() = 0; + virtual void RequestUpdateForSynchronousInputHandler(); // Called when the root scroll offset has been changed in the synchronous // input handler by the application (outside of input event handling). Offset // is expected in "content/page coordinates". virtual void SetSynchronousInputHandlerRootScrollOffset( - const gfx::PointF& root_content_offset) = 0; + const gfx::PointF& root_content_offset); virtual void PinchGestureBegin(const gfx::Point& anchor, - ui::ScrollInputType source) = 0; + ui::ScrollInputType source); virtual void PinchGestureUpdate(float magnify_delta, - const gfx::Point& anchor) = 0; - virtual void PinchGestureEnd(const gfx::Point& anchor) = 0; + const gfx::Point& anchor); + virtual void PinchGestureEnd(const gfx::Point& anchor); // Request another callback to InputHandlerClient::Animate(). - virtual void SetNeedsAnimateInput() = 0; + virtual void SetNeedsAnimateInput(); // Returns true if there is an active scroll on the viewport. - virtual bool IsCurrentlyScrollingViewport() const = 0; + virtual bool IsCurrentlyScrollingViewport() const; virtual EventListenerProperties GetEventListenerProperties( - EventListenerClass event_class) const = 0; + EventListenerClass event_class) const; // Returns true if |viewport_point| hits a wheel event handler region that // could block scrolling. virtual bool HasBlockingWheelEventHandlerAt( - const gfx::Point& viewport_point) const = 0; + const gfx::Point& viewport_point) const; // It returns the type of a touch start or move event listener at // |viewport_point|. Whether the page should be given the opportunity to @@ -343,7 +357,7 @@ class CC_EXPORT InputHandler { // default touch action is auto. virtual TouchStartOrMoveEventListenerType EventListenerTypeForTouchStartOrMoveAt(const gfx::Point& viewport_point, - TouchAction* out_touch_action) = 0; + TouchAction* out_touch_action); // Calling `CreateLatencyInfoSwapPromiseMonitor()` to get a scoped // `LatencyInfoSwapPromiseMonitor`. During the life time of the @@ -351,7 +365,7 @@ class CC_EXPORT InputHandler { // `SetNeedsRedrawRect()` is called on `LayerTreeHostImpl`, the original // latency info will be turned into a `LatencyInfoSwapPromise`. virtual std::unique_ptr<LatencyInfoSwapPromiseMonitor> - CreateLatencyInfoSwapPromiseMonitor(ui::LatencyInfo* latency) = 0; + CreateLatencyInfoSwapPromiseMonitor(ui::LatencyInfo* latency); // Returns a new instance of `EventsMetricsManager::ScopedMonitor` to monitor // the scope of handling an event. If `done_callback` is not a null callback, @@ -364,19 +378,18 @@ class CC_EXPORT InputHandler { // it. virtual std::unique_ptr<EventsMetricsManager::ScopedMonitor> GetScopedEventMetricsMonitor( - EventsMetricsManager::ScopedMonitor::DoneCallback done_callback) = 0; + EventsMetricsManager::ScopedMonitor::DoneCallback done_callback); - virtual ScrollElasticityHelper* CreateScrollElasticityHelper() = 0; - virtual void DestroyScrollElasticityHelper() = 0; + virtual ScrollElasticityHelper* CreateScrollElasticityHelper(); + virtual void DestroyScrollElasticityHelper(); // Called by the single-threaded UI Compositor to get or set the scroll offset // on the impl side. Returns false if |element_id| isn't in the active tree. virtual bool GetScrollOffsetForLayer(ElementId element_id, - gfx::PointF* offset) = 0; - virtual bool ScrollLayerTo(ElementId element_id, - const gfx::PointF& offset) = 0; + gfx::PointF* offset); + virtual bool ScrollLayerTo(ElementId element_id, const gfx::PointF& offset); - virtual bool ScrollingShouldSwitchtoMainThread() = 0; + virtual bool ScrollingShouldSwitchtoMainThread(); // Sets the initial and target offset for scroll snapping for the currently // scrolling node and the given natural displacement. Also sets the target @@ -387,27 +400,379 @@ class CC_EXPORT InputHandler { virtual bool GetSnapFlingInfoAndSetAnimatingSnapTarget( const gfx::Vector2dF& natural_displacement_in_viewport, gfx::PointF* initial_offset, - gfx::PointF* target_offset) = 0; + gfx::PointF* target_offset); // |did_finish| is true if the animation reached its target position (i.e. // it wasn't aborted). - virtual void ScrollEndForSnapFling(bool did_finish) = 0; + virtual void ScrollEndForSnapFling(bool did_finish); // Notifies when any input event is received, irrespective of whether it is // being handled by the InputHandler or not. - virtual void NotifyInputEvent() = 0; + virtual void NotifyInputEvent(); // Returns true if ScrollbarController is in the middle of a scroll operation. - virtual bool ScrollbarScrollIsActive() = 0; + virtual bool ScrollbarScrollIsActive(); // Defers posting BeginMainFrame tasks. This is used during the main thread // hit test for a GestureScrollBegin, to avoid posting a frame before the // compositor thread has had a chance to update the scroll offset. - virtual void SetDeferBeginMainFrame(bool defer_begin_main_frame) const = 0; - - protected: - virtual ~InputHandler() = default; - InputHandler() = default; + virtual void SetDeferBeginMainFrame(bool defer_begin_main_frame) const; + + bool CanConsumeDelta(const ScrollState& scroll_state, + const ScrollNode& scroll_node); + // Returns the amount of delta that can be applied to scroll_node, taking + // page scale into account. + gfx::Vector2dF ComputeScrollDelta(const ScrollNode& scroll_node, + const gfx::Vector2dF& delta); + + gfx::Vector2dF ScrollSingleNode(const ScrollNode& scroll_node, + const gfx::Vector2dF& delta, + const gfx::Point& viewport_point, + bool is_direct_manipulation); + + float LineStep() const; + + // Resolves a delta in the given granularity for the |scroll_node| into + // physical pixels to scroll. + gfx::Vector2dF ResolveScrollGranularityToPixels( + const ScrollNode& scroll_node, + const gfx::Vector2dF& scroll_delta, + ui::ScrollGranularity granularity); + + // Used to set the pinch gesture active state when the pinch gesture is + // handled on another layer tree. In a page with OOPIFs, only the main + // frame's layer tree directly handles pinch events. But layer trees for + // sub-frames need to know when pinch gestures are active so they can + // throttle the re-rastering. This function allows setting this flag on + // OOPIF layer trees using information sent (initially) from the main-frame. + void set_external_pinch_gesture_active(bool external_pinch_gesture_active) { + external_pinch_gesture_active_ = external_pinch_gesture_active; + // Only one of the flags should ever be true at any given time. + DCHECK(!pinch_gesture_active_ || !external_pinch_gesture_active_); + } + + bool pinch_gesture_active() const { + return pinch_gesture_active_ || external_pinch_gesture_active_; + } + + void set_force_smooth_wheel_scrolling_for_testing(bool enabled) { + force_smooth_wheel_scrolling_for_testing_ = enabled; + } + + gfx::Vector2dF accumulated_root_overscroll_for_testing() const { + return accumulated_root_overscroll_; + } + + bool animating_for_snap_for_testing() const { return IsAnimatingForSnap(); } + + // =========== InputDelegateForCompositor Interface - This section implements + // the interface that LayerTreeHostImpl uses to communicate with the input + // system. + void ProcessCommitDeltas(CompositorCommitData* commit_data) override; + void TickAnimations(base::TimeTicks monotonic_time) override; + void WillShutdown() override; + void WillDraw() override; + void WillBeginImplFrame(const viz::BeginFrameArgs& args) override; + void DidCommit() override; + void DidActivatePendingTree() override; + void RootLayerStateMayHaveChanged() override; + void DidRegisterScrollbar(ElementId scroll_element_id, + ScrollbarOrientation orientation) override; + void DidUnregisterScrollbar(ElementId scroll_element_id, + ScrollbarOrientation orientation) override; + void ScrollOffsetAnimationFinished() override; + void SetPrefersReducedMotion(bool prefers_reduced_motion) override; + bool IsCurrentlyScrolling() const override; + ActivelyScrollingType GetActivelyScrollingType() const override; + + private: + FRIEND_TEST_ALL_PREFIXES(ScrollUnifiedLayerTreeHostImplTest, + AbortAnimatedScrollBeforeStartingAutoscroll); + FRIEND_TEST_ALL_PREFIXES(ScrollUnifiedLayerTreeHostImplTest, + AnimatedScrollYielding); + FRIEND_TEST_ALL_PREFIXES(ScrollUnifiedLayerTreeHostImplTest, + AutoscrollOnDeletedScrollbar); + FRIEND_TEST_ALL_PREFIXES(ScrollUnifiedLayerTreeHostImplTest, + ThumbDragAfterJumpClick); + FRIEND_TEST_ALL_PREFIXES(ScrollUnifiedLayerTreeHostImplTest, + ScrollOnLargeThumb); + FRIEND_TEST_ALL_PREFIXES(LayerTreeHostImplTest, AutoscrollTaskAbort); + + // This method gets the scroll offset for a regular scroller, or the combined + // visual and layout offsets of the viewport. + gfx::PointF GetVisualScrollOffset(const ScrollNode& scroll_node) const; + bool IsScrolledBy(LayerImpl* child, ScrollNode* ancestor); + bool IsAnimatingForSnap() const; + + ScrollNode* CurrentlyScrollingNode(); + const ScrollNode* CurrentlyScrollingNode() const; + void ClearCurrentlyScrollingNode(); + ScrollTree& GetScrollTree(); + ScrollTree& GetScrollTree() const; + Viewport& GetViewport() const; + + ScrollNode* InnerViewportScrollNode() const; + ScrollNode* OuterViewportScrollNode() const; + + void SetNeedsCommit(); + LayerTreeImpl& ActiveTree(); + LayerTreeImpl& ActiveTree() const; + + bool IsMainThreadScrolling(const InputHandler::ScrollStatus& status, + const ScrollNode* scroll_node) const; + + bool IsTouchDraggingScrollbar( + LayerImpl* first_scrolling_layer_or_drawn_scrollbar, + ui::ScrollInputType type); + + void UpdateRootLayerStateForSynchronousInputHandler(); + + // Called during ScrollBegin once a scroller was successfully latched to + // (i.e. it can and will consume scroll delta on the compositor thread). The + // latched scroller is now available in CurrentlyScrollingNode(). + // TODO(bokan): There's some debate about the name of this method. We should + // get consensus on terminology to use and apply it consistently. + // https://crrev.com/c/1981336/9/cc/trees/layer_tree_host_impl.cc#4520 + void DidLatchToScroller(const ScrollState& scroll_state, + ui::ScrollInputType type); + + // This function keeps track of sources of scrolls that are handled in the + // compositor side. The information gets shared by the main thread as part of + // the begin_main_frame_state. Finally Use counters are updated in the main + // thread side to keep track of the frequency of scrolling with different + // sources per page load. TODO(crbug.com/691886): Use GRC API to plumb the + // scroll source info for Use Counters. + void UpdateScrollSourceInfo(const ScrollState& scroll_state, + ui::ScrollInputType type); + + // Applies the scroll_state to the currently latched scroller. See comment in + // InputHandler::ScrollUpdate declaration for the meaning of |delayed_by|. + void ScrollLatchedScroller(ScrollState* scroll_state, + base::TimeDelta delayed_by); + + // Determines whether the given scroll node can scroll on the compositor + // thread or if there are any reasons it must be scrolled on the main thread + // or not at all. Note: in general, this is not sufficient to determine if a + // scroll can occur on the compositor thread. If hit testing to a scroll + // node, the caller must also check whether the hit point intersects a + // non-fast-scrolling-region of any ancestor scrolling layers. Can be removed + // after scroll unification https://crbug.com/476553. + InputHandler::ScrollStatus TryScroll(const ScrollTree& scroll_tree, + ScrollNode* scroll_node) const; + + enum class SnapReason { kGestureScrollEnd, kScrollOffsetAnimationFinished }; + + // Creates an animation curve and returns true if we need to update the + // scroll position to a snap point. Otherwise returns false. + bool SnapAtScrollEnd(SnapReason reason); + + // |layer| is returned from a regular hit test, and + // |first_scrolling_layer_or_drawn_scrollbar| is returned from a hit test + // performed only on scrollers and scrollbars. Initial scroll hit testing can + // be unreliable if the latter is not the direct scroll ancestor of the + // former. In this case, we will fall back to main thread scrolling because + // the compositor thread doesn't know which layer to scroll. This happens when + // a layer covers a scroller that doesn't scroll the former, or a scroller is + // masked by a mask layer for mask image, clip-path, rounded border, etc. + // + // Note, position: fixed layers use the inner viewport as their ScrollNode + // (since they don't scroll with the outer viewport), however, scrolls from + // the fixed layer still chain to the outer viewport. It's also possible for a + // node to have the inner viewport as its ancestor without going through the + // outer viewport; however, it may still scroll using the viewport(). Hence, + // this method must use the same scroll chaining logic we use in ApplyScroll. + bool IsInitialScrollHitTestReliable( + const LayerImpl* layer, + const LayerImpl* first_scrolling_layer_or_drawn_scrollbar) const; + + // Similar to above but includes complicated logic to determine whether the + // ScrollNode is able to be scrolled on the compositor or requires main + // thread scrolling. If main thread scrolling is required + // |scroll_on_main_thread| is set to true and the reason is given in + // |main_thread_scrolling_reason| to on of the enum values in + // main_thread_scrolling_reason.h. Can be removed after scroll unification + // https://crbug.com/476553. + ScrollNode* FindScrollNodeForCompositedScrolling( + const gfx::PointF& device_viewport_point, + LayerImpl* layer_hit_by_point, + bool* scroll_on_main_thread, + uint32_t* main_thread_scrolling_reason); + + // Return all ScrollNode indices that have an associated layer with a non-fast + // region that intersects the point. + base::flat_set<int> NonFastScrollableNodes( + const gfx::PointF& device_viewport_point) const; + + // Returns the ScrollNode we should use to scroll, accounting for viewport + // scroll chaining rules. + ScrollNode* GetNodeToScroll(ScrollNode* node) const; + + // Given a starting node (determined by hit-test), walks up the scroll tree + // looking for the first node that can consume scroll from the given + // scroll_state and returns the first such node. If none is found, or if + // starting_node is nullptr, returns nullptr; + ScrollNode* FindNodeToLatch(ScrollState* scroll_state, + ScrollNode* starting_node, + ui::ScrollInputType type); + + bool CanPropagate(ScrollNode* scroll_node, float x, float y); + + // Performs a hit test to determine the ScrollNode to use when scrolling at + // |viewport_point|. If no layer is hit, this falls back to the inner + // viewport scroll node. Returns: + // - If |hit_test_sucessful| is false, hit testing has failed and the + // compositor cannot determine the correct scroll node (e.g. see comments in + // IsInitialScrollHitTestReliable). |scroll_node| is always nullptr in this + // case. + // - If |hit_test_successful| is true, returns the ScrollNode to use in + // |scroll_node|. This can be nullptr if no layer was hit and there are no + // viewport nodes (e.g. OOPIF, UI compositor). + struct ScrollHitTestResult { + ScrollNode* scroll_node; + bool hit_test_successful; + }; + ScrollHitTestResult HitTestScrollNode( + const gfx::PointF& device_viewport_point) const; + + bool ShouldAnimateScroll(const ScrollState& scroll_state) const; + + bool ScrollAnimationUpdateTarget(const ScrollNode& scroll_node, + const gfx::Vector2dF& scroll_delta, + base::TimeDelta delayed_by); + + // Transforms viewport start point and scroll delta to local start point and + // local delta, respectively. If the transformation of either the start or end + // point of a scroll is clipped, the function returns false. + bool CalculateLocalScrollDeltaAndStartPoint( + const ScrollNode& scroll_node, + const gfx::PointF& viewport_point, + const gfx::Vector2dF& viewport_delta, + gfx::Vector2dF* out_local_scroll_delta, + gfx::PointF* out_local_start_point = nullptr); + gfx::Vector2dF ScrollNodeWithViewportSpaceDelta( + const ScrollNode& scroll_node, + const gfx::PointF& viewport_point, + const gfx::Vector2dF& viewport_delta); + gfx::Vector2dF ScrollNodeWithLocalDelta( + const ScrollNode& scroll_node, + const gfx::Vector2dF& local_delta) const; + // This helper returns an adjusted version of |delta| where the scroll delta + // is cleared in any axis in which user scrolling is disabled (e.g. by + // |overflow-x: hidden|). + gfx::Vector2dF UserScrollableDelta(const ScrollNode& node, + const gfx::Vector2dF& delta) const; + + void AdjustScrollDeltaForScrollbarSnap(ScrollState* scroll_state); + + FrameSequenceTrackerType GetTrackerTypeForScroll( + ui::ScrollInputType input_type) const; + + ScrollbarController* scrollbar_controller_for_testing() const { + return scrollbar_controller_.get(); + } + + // The input handler is owned by the delegate so their lifetimes are tied + // together. + CompositorDelegateForInput& compositor_delegate_; + + raw_ptr<InputHandlerClient> input_handler_client_ = nullptr; + + // An object to implement the ScrollElasticityHelper interface and + // hold all state related to elasticity. May be nullptr if never requested. + std::unique_ptr<ScrollElasticityHelper> scroll_elasticity_helper_; + + // Manages composited scrollbar hit testing. + std::unique_ptr<ScrollbarController> scrollbar_controller_; + + // Overscroll delta accumulated on the viewport throughout a scroll gesture; + // reset when the gesture ends. + gfx::Vector2dF accumulated_root_overscroll_; + + // Unconsumed scroll delta sent to the main thread for firing overscroll DOM + // events. Resets after each commit. + gfx::Vector2dF overscroll_delta_for_main_thread_; + + // The source device type that started the scroll gesture. Only set between a + // ScrollBegin and ScrollEnd. + absl::optional<ui::ScrollInputType> latched_scroll_type_; + + // Tracks the last scroll update/begin state received. Used to infer the most + // recent scroll type and direction. + absl::optional<ScrollState> last_scroll_begin_state_; + absl::optional<ScrollState> last_scroll_update_state_; + + // If a scroll snap is being animated, then the value of this will be the + // element id(s) of the target(s). Otherwise, the ids will be invalid. + // At the end of a scroll animation, the target should be set as the scroll + // node's snap target. + TargetSnapAreaElementIds scroll_animating_snap_target_ids_; + + // A set of elements that scroll-snapped to a new target since the last + // begin main frame. The snap target ids of these elements will be sent to + // the main thread in the next begin main frame. + base::flat_map<ElementId, TargetSnapAreaElementIds> updated_snapped_elements_; + + ElementId scroll_element_id_mouse_currently_over_; + ElementId scroll_element_id_mouse_currently_captured_; + + // Set in ScrollBegin and outlives the currently scrolling node so it can be + // used to send the scrollend and overscroll DOM events from the main thread + // when scrolling occurs on the compositor thread. This value is cleared at + // the first commit after a GSE. + ElementId last_latched_scroller_; + + // Scroll animation can finish either before or after GSE arrival. + // deferred_scroll_end_ is set when the GSE has arrvied before scroll + // animation completion. ScrollEnd will get called once the animation is + // over. + bool deferred_scroll_end_ = false; + + // Set to true when a scroll gesture being handled on the compositor has + // ended. i.e. When a GSE has arrived and any ongoing scroll animation has + // ended. + bool scroll_gesture_did_end_ = false; + + // True iff some of the delta has been consumed for the current scroll + // sequence on the specific axis. + bool did_scroll_x_for_scroll_gesture_ = false; + bool did_scroll_y_for_scroll_gesture_ = false; + + // did_scroll_x/y_for_scroll_gesture_ is true when contents consume the delta, + // but delta_consumed_for_scroll_gesture_ can be true when only browser + // controls consume all the delta. + bool delta_consumed_for_scroll_gesture_ = false; + + // TODO(bokan): Mac doesn't yet have smooth scrolling for wheel; however, to + // allow consistency in tests we use this bit to override that decision. + // https://crbug.com/574283. + bool force_smooth_wheel_scrolling_for_testing_ = false; + + // This value is used to allow the compositor to throttle re-rastering during + // pinch gestures, when the page scale factor may be changing frequently. It + // is set in one of two ways: + // i) In a layer tree serving the root of the frame/compositor tree, it is + // directly set during processing of GesturePinch events on the impl thread + // (only the root layer tree has access to these). + // ii) In a layer tree serving a sub-frame in the frame/compositor tree, it + // is set from the main thread during the commit process, using information + // sent from the root layer tree via IPC messaging. + bool pinch_gesture_active_ = false; + bool external_pinch_gesture_active_ = false; + bool pinch_gesture_end_should_clear_scrolling_node_ = false; + + // These are used to transfer usage of different types of scrolling to the + // main thread. + bool has_pinch_zoomed_ = false; + bool has_scrolled_by_wheel_ = false; + bool has_scrolled_by_touch_ = false; + bool has_scrolled_by_precisiontouchpad_ = false; + bool has_scrolled_by_scrollbar_ = false; + + bool prefers_reduced_motion_ = false; + + // Must be the last member to ensure this is destroyed first in the + // destruction order and invalidates all weak pointers. + base::WeakPtrFactory<InputHandler> weak_factory_{this}; }; } // namespace cc diff --git a/chromium/cc/input/main_thread_scrolling_reason.cc b/chromium/cc/input/main_thread_scrolling_reason.cc index eacc3ec2838..483e1785841 100644 --- a/chromium/cc/input/main_thread_scrolling_reason.cc +++ b/chromium/cc/input/main_thread_scrolling_reason.cc @@ -60,4 +60,16 @@ void MainThreadScrollingReason::AddToTracedValue( traced_value.EndArray(); } +int MainThreadScrollingReason::BucketIndexForTesting(uint32_t reason) { + // These two values are already bucket indices. + DCHECK_NE(reason, kNotScrollingOnMain); + DCHECK_NE(reason, kScrollingOnMainForAnyReason); + + int index = 0; + while (reason >>= 1) + ++index; + DCHECK_NE(index, 0); + return index; +} + } // namespace cc diff --git a/chromium/cc/input/main_thread_scrolling_reason.h b/chromium/cc/input/main_thread_scrolling_reason.h index cc411a5ebae..2c77a6f5633 100644 --- a/chromium/cc/input/main_thread_scrolling_reason.h +++ b/chromium/cc/input/main_thread_scrolling_reason.h @@ -21,29 +21,36 @@ namespace cc { // tools/metrics/histograms/enums.xml // When adding a new MainThreadScrollingReason, make sure the corresponding // [MainThread/Compositor]CanSetScrollReasons function is also updated. +// +// More info at: http://bit.ly/mtsr-details +// struct CC_EXPORT MainThreadScrollingReason { enum : uint32_t { kNotScrollingOnMain = 0, + // This is used only to report the histogram of main thread scrolling for + // any reason below. It's a histogram bucket index instead of a bit. + kScrollingOnMainForAnyReason = 1, + // 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. + // the bitmask (which are the numbers after "1 << " below, used as the + // histogram bucket indices), but value 0 and 1 are used as the histogram + // bucket indices for kNotScrollingMain and kScrollingOnMainForAnyReason, + // respectively, so the 0th bit and the 1st bit should never be used. // See also blink::RecordScrollReasonsMetric(). // Non-transient scrolling reasons. These are set on the ScrollNode. - kHasBackgroundAttachmentFixedObjects = 1 << 1, + kHasBackgroundAttachmentFixedObjects = 1 << 2, kThreadedScrollingDisabled = 1 << 3, - kPopupNoThreadedInput = 1 << 26, + kPopupNoThreadedInput = 1 << 4, // Style-related scrolling on main reasons. Subpixel (LCD) text rendering // requires blending glyphs with the background at a specific screen // position; transparency and transforms break this. // These are only reported by the main-thread scroll gesture event codepath. // After scroll unification, we report kNoScrollingLayer instead. - kNonCompositedReasonsFirst = 18, - kNotOpaqueForTextAndLCDText = 1 << 19, - kCantPaintScrollingBackgroundAndLCDText = 1 << 20, - kNonCompositedReasonsLast = 23, + kNotOpaqueForTextAndLCDText = 1 << 5, + kCantPaintScrollingBackgroundAndLCDText = 1 << 6, // Transient scrolling reasons. These are computed for each scroll gesture. // When computed inside ScrollBegin, these prevent the InputHandler from @@ -51,16 +58,17 @@ struct CC_EXPORT MainThreadScrollingReason { // InputHandler is scrolling "on impl", but we report a transient main // thread scrolling reason to UMA when we determine that some other aspect // of handling the scroll has been (or will be) blocked on the main thread. - kScrollbarScrolling = 1 << 4, - kNonFastScrollableRegion = 1 << 6, - kFailedHitTest = 1 << 8, - kNoScrollingLayer = 1 << 9, - kNotScrollable = 1 << 10, + kScrollbarScrolling = 1 << 7, + kNonFastScrollableRegion = 1 << 8, + kFailedHitTest = 1 << 9, + kNoScrollingLayer = 1 << 10, + kNotScrollable = 1 << 11, kNonInvertibleTransform = 1 << 12, - kWheelEventHandlerRegion = 1 << 24, - kTouchEventHandlerRegion = 1 << 25, + kWheelEventHandlerRegion = 1 << 13, + kTouchEventHandlerRegion = 1 << 14, - kMainThreadScrollingReasonLast = 26, + // For blink::RecordScrollReasonsMetric() to know the number of used bits. + kMainThreadScrollingReasonLast = 14, }; static const uint32_t kNonCompositedReasons = @@ -91,6 +99,8 @@ struct CC_EXPORT MainThreadScrollingReason { return (reasons & kNonCompositedReasons) != 0; } + static int BucketIndexForTesting(uint32_t reason); + static std::string AsText(uint32_t reasons); static void AddToTracedValue(uint32_t reasons, base::trace_event::TracedValue&); diff --git a/chromium/cc/input/scroll_utils.cc b/chromium/cc/input/scroll_utils.cc index c90c93a0db0..87e846217b9 100644 --- a/chromium/cc/input/scroll_utils.cc +++ b/chromium/cc/input/scroll_utils.cc @@ -34,4 +34,14 @@ gfx::Vector2dF ScrollUtils::ResolveScrollPercentageToPixels( std::copysign(delta_y, sign_y)); } +gfx::Vector2dF ScrollUtils::ResolvePixelScrollToPercentageForTesting( + const gfx::Vector2dF& delta, + const gfx::SizeF& scroller, + const gfx::SizeF& viewport) { + float delta_x = delta.x() / std::min(scroller.width(), viewport.width()); + float delta_y = delta.y() / std::min(scroller.height(), viewport.height()); + + return gfx::Vector2dF(delta_x, delta_y); +} + } // namespace cc diff --git a/chromium/cc/input/scroll_utils.h b/chromium/cc/input/scroll_utils.h index c73d5c42282..ecb76de1a25 100644 --- a/chromium/cc/input/scroll_utils.h +++ b/chromium/cc/input/scroll_utils.h @@ -31,6 +31,13 @@ class CC_EXPORT ScrollUtils { const gfx::Vector2dF& scroll_delta, const gfx::SizeF& scroller_size, const gfx::SizeF& viewport_size); + + // Transforms a pixel delta into a percentage. Used for when a test needs to + // work with percent based scrolling and non percent based scrolling. + static gfx::Vector2dF ResolvePixelScrollToPercentageForTesting( + const gfx::Vector2dF& pixel_scroll_delta, + const gfx::SizeF& scroller_size, + const gfx::SizeF& viewport_size); }; } // namespace cc diff --git a/chromium/cc/input/scrollbar_animation_controller.cc b/chromium/cc/input/scrollbar_animation_controller.cc index a3eba61f9f8..41f19f63d13 100644 --- a/chromium/cc/input/scrollbar_animation_controller.cc +++ b/chromium/cc/input/scrollbar_animation_controller.cc @@ -380,16 +380,18 @@ void ScrollbarAnimationController::ApplyOpacityToScrollbars(float opacity) { scrollbar->SetOverlayScrollbarLayerOpacityAnimated(effective_opacity); } - bool previouslyVisible = opacity_ > 0.0f; - bool currentlyVisible = opacity > 0.0f; + bool previously_visible_ = opacity_ > 0.0f; + bool currently_visible = opacity > 0.0f; if (opacity_ != opacity) client_->SetNeedsRedrawForScrollbarAnimation(); opacity_ = opacity; - if (previouslyVisible != currentlyVisible) + if (previously_visible_ != currently_visible) { client_->DidChangeScrollbarVisibility(); + visibility_changed_ = true; + } } } // namespace cc diff --git a/chromium/cc/input/scrollbar_animation_controller.h b/chromium/cc/input/scrollbar_animation_controller.h index ce4a3f92197..b3f429677aa 100644 --- a/chromium/cc/input/scrollbar_animation_controller.h +++ b/chromium/cc/input/scrollbar_animation_controller.h @@ -65,6 +65,8 @@ class CC_EXPORT ScrollbarAnimationController { ~ScrollbarAnimationController(); bool ScrollbarsHidden() const; + bool visibility_changed() const { return visibility_changed_; } + void ClearVisibilityChanged() { visibility_changed_ = false; } bool Animate(base::TimeTicks now); @@ -94,7 +96,8 @@ class CC_EXPORT ScrollbarAnimationController { ScrollbarSet Scrollbars() const; - static constexpr float kMouseMoveDistanceToTriggerFadeIn = 30.0f; + SingleScrollbarAnimationControllerThinning& GetScrollbarAnimationController( + ScrollbarOrientation) const; private: // Describes whether the current animation should FadeIn or FadeOut. @@ -113,9 +116,6 @@ class CC_EXPORT ScrollbarAnimationController { base::TimeDelta thinning_duration, float initial_opacity); - SingleScrollbarAnimationControllerThinning& GetScrollbarAnimationController( - ScrollbarOrientation) const; - // Any scrollbar state update would show scrollbar hen post the delay fade out // if needed. void UpdateScrollbarState(); @@ -165,6 +165,8 @@ class CC_EXPORT ScrollbarAnimationController { bool tickmarks_showing_; + bool visibility_changed_ = false; + std::unique_ptr<SingleScrollbarAnimationControllerThinning> vertical_controller_; std::unique_ptr<SingleScrollbarAnimationControllerThinning> diff --git a/chromium/cc/input/scrollbar_animation_controller_unittest.cc b/chromium/cc/input/scrollbar_animation_controller_unittest.cc index 6069fc44543..d3e5389e5f5 100644 --- a/chromium/cc/input/scrollbar_animation_controller_unittest.cc +++ b/chromium/cc/input/scrollbar_animation_controller_unittest.cc @@ -24,18 +24,13 @@ namespace { const float kIdleThicknessScale = SingleScrollbarAnimationControllerThinning::kIdleThicknessScale; -const float kMouseMoveDistanceToTriggerFadeIn = - ScrollbarAnimationController::kMouseMoveDistanceToTriggerFadeIn; -const float kMouseMoveDistanceToTriggerExpand = - SingleScrollbarAnimationControllerThinning:: - kMouseMoveDistanceToTriggerExpand; const int kThumbThickness = 10; class MockScrollbarAnimationControllerClient : public ScrollbarAnimationControllerClient { public: - explicit MockScrollbarAnimationControllerClient(LayerTreeHostImpl* host_impl, - bool is_fluent) + MockScrollbarAnimationControllerClient(LayerTreeHostImpl* host_impl, + bool is_fluent) : host_impl_(host_impl), is_fluent_(is_fluent) {} ~MockScrollbarAnimationControllerClient() override = default; @@ -125,6 +120,14 @@ class ScrollbarAnimationControllerAuraOverlayTest kThinningDuration, 0.0f); v_scrollbar_layer_->SetCurrentPos(0); h_scrollbar_layer_->SetCurrentPos(0); + mouse_move_distance_to_trigger_fade_in_ = + scrollbar_controller_ + ->GetScrollbarAnimationController(ScrollbarOrientation::VERTICAL) + .MouseMoveDistanceToTriggerFadeIn(); + mouse_move_distance_to_trigger_expand_ = + scrollbar_controller_ + ->GetScrollbarAnimationController(ScrollbarOrientation::VERTICAL) + .MouseMoveDistanceToTriggerExpand(); } // Return a point with given offset from the top-left of vertical scrollbar. @@ -149,6 +152,8 @@ class ScrollbarAnimationControllerAuraOverlayTest return p; } + float mouse_move_distance_to_trigger_fade_in_; + float mouse_move_distance_to_trigger_expand_; std::unique_ptr<ScrollbarAnimationController> scrollbar_controller_; raw_ptr<LayerImpl> clip_layer_; raw_ptr<LayerImpl> scroll_layer_; @@ -489,8 +494,8 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, // Then move mouse away, The fade out animation should have been cleared or // cancelled. - scrollbar_controller_->DidMouseMove( - NearVerticalScrollbarBegin(-kMouseMoveDistanceToTriggerExpand, 0)); + scrollbar_controller_->DidMouseMove(NearVerticalScrollbarBegin( + -mouse_move_distance_to_trigger_expand_ - 1, 0)); EXPECT_TRUE(client_.start_fade().is_null() || client_.start_fade().IsCancelled()); @@ -559,8 +564,8 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, FadeAfterReleasedFar) { client_.start_fade().IsCancelled()); // Now move the mouse away from the scrollbar and release it. - scrollbar_controller_->DidMouseMove( - NearVerticalScrollbarBegin(-kMouseMoveDistanceToTriggerFadeIn, 0)); + scrollbar_controller_->DidMouseMove(NearVerticalScrollbarBegin( + -mouse_move_distance_to_trigger_fade_in_ - 1, 0)); scrollbar_controller_->DidMouseUp(); scrollbar_controller_->Animate(time); @@ -886,8 +891,8 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, MouseNearEach) { h_scrollbar_layer_->thumb_thickness_scale_factor()); // Now move away from bar. - scrollbar_controller_->DidMouseMove( - NearVerticalScrollbarBegin(-kMouseMoveDistanceToTriggerExpand, 0)); + scrollbar_controller_->DidMouseMove(NearVerticalScrollbarBegin( + -mouse_move_distance_to_trigger_expand_ - 1, 0)); scrollbar_controller_->Animate(time); time += kThinningDuration; scrollbar_controller_->Animate(time); @@ -925,8 +930,8 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, MouseNearEach) { EXPECT_FLOAT_EQ(1, h_scrollbar_layer_->thumb_thickness_scale_factor()); // Now move away from bar. - scrollbar_controller_->DidMouseMove( - NearHorizontalScrollbarBegin(0, -kMouseMoveDistanceToTriggerExpand)); + scrollbar_controller_->DidMouseMove(NearHorizontalScrollbarBegin( + 0, -mouse_move_distance_to_trigger_expand_ - 1)); scrollbar_controller_->Animate(time); time += kThinningDuration; scrollbar_controller_->Animate(time); @@ -1062,7 +1067,7 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, BasicMouseHoverFadeIn) { // Move mouse over the fade in region of scrollbar. scrollbar_controller_->DidMouseMove( - NearVerticalScrollbarBegin(-kMouseMoveDistanceToTriggerFadeIn + 1, 0)); + NearVerticalScrollbarBegin(-mouse_move_distance_to_trigger_fade_in_, 0)); // An fade in animation should have been enqueued. EXPECT_FALSE(client_.start_fade().is_null()); @@ -1095,7 +1100,7 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, // Move mouse over the fade in region of scrollbar. scrollbar_controller_->DidMouseMove( - NearVerticalScrollbarBegin(-kMouseMoveDistanceToTriggerFadeIn + 1, 0)); + NearVerticalScrollbarBegin(-mouse_move_distance_to_trigger_fade_in_, 0)); // An fade in animation should have been enqueued. EXPECT_FALSE(client_.start_fade().is_null()); @@ -1105,8 +1110,8 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, client_.start_fade().Reset(); // Move mouse still hover the fade in region of scrollbar should not // post a new fade in. - scrollbar_controller_->DidMouseMove( - NearVerticalScrollbarBegin(-kMouseMoveDistanceToTriggerFadeIn + 2, 0)); + scrollbar_controller_->DidMouseMove(NearVerticalScrollbarBegin( + -mouse_move_distance_to_trigger_fade_in_ + 2, 0)); EXPECT_TRUE(client_.start_fade().is_null()); } @@ -1120,7 +1125,7 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, // Move mouse over the fade in region of scrollbar. scrollbar_controller_->DidMouseMove( - NearVerticalScrollbarBegin(-kMouseMoveDistanceToTriggerFadeIn + 1, 0)); + NearVerticalScrollbarBegin(-mouse_move_distance_to_trigger_fade_in_, 0)); // An fade in animation should have been enqueued. EXPECT_FALSE(client_.start_fade().is_null()); @@ -1128,8 +1133,8 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, EXPECT_EQ(kFadeDelay, client_.delay()); // Move mouse far away,delay fade in should be canceled. - scrollbar_controller_->DidMouseMove( - NearVerticalScrollbarBegin(-kMouseMoveDistanceToTriggerFadeIn, 0)); + scrollbar_controller_->DidMouseMove(NearVerticalScrollbarBegin( + -mouse_move_distance_to_trigger_fade_in_ - 1, 0)); EXPECT_TRUE(client_.start_fade().is_null() || client_.start_fade().IsCancelled()); @@ -1144,7 +1149,7 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, // Move mouse over the fade in region of scrollbar. scrollbar_controller_->DidMouseMove( - NearVerticalScrollbarBegin(-kMouseMoveDistanceToTriggerFadeIn + 1, 0)); + NearVerticalScrollbarBegin(-mouse_move_distance_to_trigger_fade_in_, 0)); // An fade in animation should have been enqueued. EXPECT_FALSE(client_.start_fade().is_null()); @@ -1158,7 +1163,7 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, // Move mouse over the fade in region of scrollbar. scrollbar_controller_->DidMouseMove( - NearVerticalScrollbarBegin(-kMouseMoveDistanceToTriggerFadeIn + 1, 0)); + NearVerticalScrollbarBegin(-mouse_move_distance_to_trigger_fade_in_, 0)); // An fade in animation should have been enqueued. EXPECT_FALSE(client_.start_fade().is_null()); @@ -1185,7 +1190,7 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, // Move mouse over the fade in region of scrollbar. scrollbar_controller_->DidMouseMove( - NearVerticalScrollbarBegin(-kMouseMoveDistanceToTriggerFadeIn + 1, 0)); + NearVerticalScrollbarBegin(-mouse_move_distance_to_trigger_fade_in_, 0)); // An fade in animation should have been enqueued. EXPECT_FALSE(client_.start_fade().is_null()); @@ -1199,7 +1204,7 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, // Move mouse hover the fade in region of scrollbar with press. scrollbar_controller_->DidMouseMove( - NearVerticalScrollbarBegin(-kMouseMoveDistanceToTriggerFadeIn + 1, 0)); + NearVerticalScrollbarBegin(-mouse_move_distance_to_trigger_fade_in_, 0)); // Should not have delay fade animation. EXPECT_TRUE(client_.start_fade().is_null() || @@ -1233,7 +1238,7 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, // Move mouse over the fade in region of scrollbar. scrollbar_controller_->DidMouseMove( - NearVerticalScrollbarBegin(-kMouseMoveDistanceToTriggerFadeIn + 1, 0)); + NearVerticalScrollbarBegin(-mouse_move_distance_to_trigger_fade_in_, 0)); // An fade in animation should have been enqueued. EXPECT_FALSE(client_.start_fade().is_null()); @@ -1247,8 +1252,8 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, // Move mouse far from hover the fade in region of scrollbar with // press. - scrollbar_controller_->DidMouseMove( - NearVerticalScrollbarBegin(-kMouseMoveDistanceToTriggerFadeIn, 0)); + scrollbar_controller_->DidMouseMove(NearVerticalScrollbarBegin( + -mouse_move_distance_to_trigger_fade_in_ - 1, 0)); // Should not have delay fade animation. EXPECT_TRUE(client_.start_fade().is_null() || diff --git a/chromium/cc/input/single_scrollbar_animation_controller_thinning.cc b/chromium/cc/input/single_scrollbar_animation_controller_thinning.cc index 5288a8c5d3b..7a4d2489707 100644 --- a/chromium/cc/input/single_scrollbar_animation_controller_thinning.cc +++ b/chromium/cc/input/single_scrollbar_animation_controller_thinning.cc @@ -143,7 +143,10 @@ void SingleScrollbarAnimationControllerThinning::DidMouseUp() { captured_ = false; StopAnimation(); - if (!mouse_is_near_scrollbar_thumb_) { + const bool thickness_should_decrease = client_->IsFluentScrollbar() + ? !mouse_is_near_scrollbar_track_ + : !mouse_is_near_scrollbar_thumb_; + if (thickness_should_decrease) { thickness_change_ = AnimationChange::DECREASE; StartAnimation(); } else { @@ -152,7 +155,11 @@ void SingleScrollbarAnimationControllerThinning::DidMouseUp() { } void SingleScrollbarAnimationControllerThinning::DidMouseLeave() { - if (!mouse_is_over_scrollbar_thumb_ && !mouse_is_near_scrollbar_thumb_) + if (client_->IsFluentScrollbar() && !mouse_is_near_scrollbar_track_) + return; + + if (!client_->IsFluentScrollbar() && !mouse_is_over_scrollbar_thumb_ && + !mouse_is_near_scrollbar_thumb_) return; mouse_is_over_scrollbar_thumb_ = false; @@ -173,35 +180,50 @@ void SingleScrollbarAnimationControllerThinning::DidMouseMove( if (!scrollbar) return; - float distance_to_scrollbar_track = + const float distance_to_scrollbar_track = DistanceToScrollbarPart(device_viewport_point, *scrollbar, ScrollbarPart::TRACK_BUTTONS_TICKMARKS); - float distance_to_scrollbar_thumb = DistanceToScrollbarPart( + const float distance_to_scrollbar_thumb = DistanceToScrollbarPart( device_viewport_point, *scrollbar, ScrollbarPart::THUMB); - mouse_is_near_scrollbar_track_ = - distance_to_scrollbar_track < - ScrollbarAnimationController::kMouseMoveDistanceToTriggerFadeIn; - - bool mouse_is_over_scrollbar_thumb = distance_to_scrollbar_thumb == 0.0f; - bool mouse_is_near_scrollbar_thumb = - distance_to_scrollbar_thumb < kMouseMoveDistanceToTriggerExpand; - - if (!captured_ && - mouse_is_near_scrollbar_thumb != mouse_is_near_scrollbar_thumb_) { - thickness_change_ = mouse_is_near_scrollbar_thumb - ? AnimationChange::INCREASE - : AnimationChange::DECREASE; + const bool mouse_is_near_scrollbar_track = + distance_to_scrollbar_track <= MouseMoveDistanceToTriggerFadeIn(); + + const bool mouse_is_over_scrollbar_thumb = + distance_to_scrollbar_thumb == 0.0f; + const bool mouse_is_near_scrollbar_thumb = + distance_to_scrollbar_thumb <= MouseMoveDistanceToTriggerExpand(); + const bool thickness_should_change = + client_->IsFluentScrollbar() + ? (mouse_is_near_scrollbar_track_ != mouse_is_near_scrollbar_track) + : (mouse_is_near_scrollbar_thumb_ != mouse_is_near_scrollbar_thumb); + + if (!captured_ && thickness_should_change) { + const bool thickness_should_increase = client_->IsFluentScrollbar() + ? mouse_is_near_scrollbar_track + : mouse_is_near_scrollbar_thumb; + thickness_change_ = thickness_should_increase ? AnimationChange::INCREASE + : AnimationChange::DECREASE; StartAnimation(); } + + mouse_is_near_scrollbar_track_ = mouse_is_near_scrollbar_track; mouse_is_near_scrollbar_thumb_ = mouse_is_near_scrollbar_thumb; mouse_is_over_scrollbar_thumb_ = mouse_is_over_scrollbar_thumb; } +float SingleScrollbarAnimationControllerThinning:: + ThumbThicknessScaleByMouseDistanceToScrollbar() const { + const bool mouse_is_near_scrollbar_part = + client_->IsFluentScrollbar() ? mouse_is_near_scrollbar_track_ + : mouse_is_near_scrollbar_thumb_; + return mouse_is_near_scrollbar_part ? 1.f : kIdleThicknessScale; +} + float SingleScrollbarAnimationControllerThinning::ThumbThicknessScaleAt( - float progress) { + float progress) const { if (thickness_change_ == AnimationChange::NONE) - return mouse_is_near_scrollbar_thumb_ ? 1.f : kIdleThicknessScale; + return ThumbThicknessScaleByMouseDistanceToScrollbar(); float factor = thickness_change_ == AnimationChange::INCREASE ? progress : (1.f - progress); @@ -232,8 +254,7 @@ float SingleScrollbarAnimationControllerThinning::AdjustScale( void SingleScrollbarAnimationControllerThinning::UpdateThumbThicknessScale() { StopAnimation(); - ApplyThumbThicknessScale( - mouse_is_near_scrollbar_thumb_ ? 1.f : kIdleThicknessScale); + ApplyThumbThicknessScale(ThumbThicknessScaleByMouseDistanceToScrollbar()); } void SingleScrollbarAnimationControllerThinning::ApplyThumbThicknessScale( @@ -251,4 +272,14 @@ void SingleScrollbarAnimationControllerThinning::ApplyThumbThicknessScale( } } +float SingleScrollbarAnimationControllerThinning:: + MouseMoveDistanceToTriggerExpand() { + return client_->IsFluentScrollbar() ? 0.0f : 25.0f; +} + +float SingleScrollbarAnimationControllerThinning:: + MouseMoveDistanceToTriggerFadeIn() { + return client_->IsFluentScrollbar() ? 0.0f : 30.0f; +} + } // namespace cc diff --git a/chromium/cc/input/single_scrollbar_animation_controller_thinning.h b/chromium/cc/input/single_scrollbar_animation_controller_thinning.h index 44eef269f1a..4241de6ec47 100644 --- a/chromium/cc/input/single_scrollbar_animation_controller_thinning.h +++ b/chromium/cc/input/single_scrollbar_animation_controller_thinning.h @@ -23,7 +23,6 @@ class ScrollbarAnimationControllerClient; class CC_EXPORT SingleScrollbarAnimationControllerThinning { public: static constexpr float kIdleThicknessScale = 0.4f; - static constexpr float kMouseMoveDistanceToTriggerExpand = 25.f; static std::unique_ptr<SingleScrollbarAnimationControllerThinning> Create( ElementId scroll_element_id, @@ -61,6 +60,9 @@ class CC_EXPORT SingleScrollbarAnimationControllerThinning { void DidMouseLeave(); void DidMouseMove(const gfx::PointF& device_viewport_point); + float MouseMoveDistanceToTriggerExpand(); + float MouseMoveDistanceToTriggerFadeIn(); + private: SingleScrollbarAnimationControllerThinning( ElementId scroll_element_id, @@ -75,7 +77,8 @@ class CC_EXPORT SingleScrollbarAnimationControllerThinning { // Describes whether the current animation should INCREASE (thicken) // a bar or DECREASE it (thin). enum class AnimationChange { NONE, INCREASE, DECREASE }; - float ThumbThicknessScaleAt(float progress); + float ThumbThicknessScaleAt(float progress) const; + float ThumbThicknessScaleByMouseDistanceToScrollbar() const; float AdjustScale(float new_value, float current_value, @@ -95,6 +98,8 @@ class CC_EXPORT SingleScrollbarAnimationControllerThinning { bool captured_; bool mouse_is_over_scrollbar_thumb_; bool mouse_is_near_scrollbar_thumb_; + // For Fluent scrollbars the near distance to the track is 0 which is + // equivalent to the mouse being over the thumb/track. bool mouse_is_near_scrollbar_track_; // Are we narrowing or thickening the bars. AnimationChange thickness_change_; diff --git a/chromium/cc/input/single_scrollbar_animation_controller_thinning_unittest.cc b/chromium/cc/input/single_scrollbar_animation_controller_thinning_unittest.cc index ff0221ea9e5..3ce3c9943fe 100644 --- a/chromium/cc/input/single_scrollbar_animation_controller_thinning_unittest.cc +++ b/chromium/cc/input/single_scrollbar_animation_controller_thinning_unittest.cc @@ -12,34 +12,29 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" -using testing::AtLeast; -using testing::Mock; -using testing::NiceMock; -using testing::_; +using ::testing::_; +using ::testing::Bool; +using ::testing::Mock; +using ::testing::NiceMock; namespace cc { namespace { const float kIdleThicknessScale = SingleScrollbarAnimationControllerThinning::kIdleThicknessScale; -const float kMouseMoveDistanceToTriggerExpand = - SingleScrollbarAnimationControllerThinning:: - kMouseMoveDistanceToTriggerExpand; -const float kMouseMoveDistanceToTriggerFadeIn = - ScrollbarAnimationController::kMouseMoveDistanceToTriggerFadeIn; class MockSingleScrollbarAnimationControllerClient : public ScrollbarAnimationControllerClient { public: - explicit MockSingleScrollbarAnimationControllerClient( - LayerTreeHostImpl* host_impl) - : host_impl_(host_impl) {} + MockSingleScrollbarAnimationControllerClient(LayerTreeHostImpl* host_impl, + bool is_fluent) + : host_impl_(host_impl), is_fluent_(is_fluent) {} ~MockSingleScrollbarAnimationControllerClient() override = default; ScrollbarSet ScrollbarsFor(ElementId scroll_element_id) const override { return host_impl_->ScrollbarsFor(scroll_element_id); } - bool IsFluentScrollbar() const override { return false; } + bool IsFluentScrollbar() const override { return is_fluent_; } MOCK_METHOD2(PostDelayedScrollbarAnimationTask, void(base::OnceClosure start_fade, base::TimeDelta delay)); @@ -49,16 +44,21 @@ class MockSingleScrollbarAnimationControllerClient private: raw_ptr<LayerTreeHostImpl> host_impl_; + bool is_fluent_; }; class SingleScrollbarAnimationControllerThinningTest : public LayerTreeImplTestBase, - public testing::Test { + public testing::Test, + public testing::WithParamInterface<bool> { public: - SingleScrollbarAnimationControllerThinningTest() : client_(host_impl()) {} + explicit SingleScrollbarAnimationControllerThinningTest( + bool is_fluent = GetParam()) + : client_(host_impl(), is_fluent) {} protected: const base::TimeDelta kThinningDuration = base::Seconds(2); + const int kThumbThickness = 10; void SetUp() override { root_layer()->SetBounds(gfx::Size(100, 100)); @@ -67,7 +67,6 @@ class SingleScrollbarAnimationControllerThinningTest scroll_layer->SetElementId( LayerIdToElementIdForTesting(scroll_layer->id())); - const int kThumbThickness = 10; const int kTrackStart = 0; const int kTrackLength = 100; const bool kIsLeftSideVerticalScrollbar = false; @@ -91,8 +90,14 @@ class SingleScrollbarAnimationControllerThinningTest scrollbar_controller_ = SingleScrollbarAnimationControllerThinning::Create( scroll_layer->element_id(), ScrollbarOrientation::HORIZONTAL, &client_, kThinningDuration); + mouse_move_distance_to_trigger_fade_in_ = + scrollbar_controller_->MouseMoveDistanceToTriggerFadeIn(); + mouse_move_distance_to_trigger_expand_ = + scrollbar_controller_->MouseMoveDistanceToTriggerExpand(); } + float mouse_move_distance_to_trigger_fade_in_; + float mouse_move_distance_to_trigger_expand_; std::unique_ptr<SingleScrollbarAnimationControllerThinning> scrollbar_controller_; raw_ptr<SolidColorScrollbarLayerImpl> scrollbar_layer_; @@ -106,19 +111,27 @@ gfx::PointF NearScrollbar(float offset_x, float offset_y) { return p; } +class SingleScrollbarAnimationControllerThinningFluentTest + : public SingleScrollbarAnimationControllerThinningTest { + public: + SingleScrollbarAnimationControllerThinningFluentTest() + : SingleScrollbarAnimationControllerThinningTest(/* is_fluent */ true) {} +}; + // Check initialization of scrollbar. Should start thin. -TEST_F(SingleScrollbarAnimationControllerThinningTest, Idle) { +TEST_P(SingleScrollbarAnimationControllerThinningTest, Idle) { EXPECT_FLOAT_EQ(kIdleThicknessScale, scrollbar_layer_->thumb_thickness_scale_factor()); } // Move the pointer near the scrollbar. Confirm it gets thick and narrow when // moved away. -TEST_F(SingleScrollbarAnimationControllerThinningTest, MouseNear) { +TEST_P(SingleScrollbarAnimationControllerThinningTest, MouseNear) { base::TimeTicks time; time += base::Seconds(1); - scrollbar_controller_->DidMouseMove(NearScrollbar(-1, 0)); + scrollbar_controller_->DidMouseMove( + NearScrollbar(-mouse_move_distance_to_trigger_expand_, 0)); scrollbar_controller_->Animate(time); EXPECT_FLOAT_EQ(kIdleThicknessScale, scrollbar_layer_->thumb_thickness_scale_factor()); @@ -129,7 +142,8 @@ TEST_F(SingleScrollbarAnimationControllerThinningTest, MouseNear) { EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor()); // Subsequent moves within the nearness threshold should not change anything. - scrollbar_controller_->DidMouseMove(NearScrollbar(-2, 0)); + scrollbar_controller_->DidMouseMove( + NearScrollbar(-mouse_move_distance_to_trigger_expand_ + 1, 0)); scrollbar_controller_->Animate(time); time += base::Seconds(10); scrollbar_controller_->Animate(time); @@ -137,7 +151,7 @@ TEST_F(SingleScrollbarAnimationControllerThinningTest, MouseNear) { // Now move away from thumb. scrollbar_controller_->DidMouseMove( - NearScrollbar(-kMouseMoveDistanceToTriggerExpand, 0)); + NearScrollbar(-mouse_move_distance_to_trigger_expand_ - 1, 0)); scrollbar_controller_->Animate(time); time += kThinningDuration; scrollbar_controller_->Animate(time); @@ -146,7 +160,7 @@ TEST_F(SingleScrollbarAnimationControllerThinningTest, MouseNear) { // Move away from track. scrollbar_controller_->DidMouseMove( - NearScrollbar(-kMouseMoveDistanceToTriggerFadeIn, 0)); + NearScrollbar(-mouse_move_distance_to_trigger_fade_in_ - 1, 0)); scrollbar_controller_->Animate(time); time += kThinningDuration; scrollbar_controller_->Animate(time); @@ -156,7 +170,7 @@ TEST_F(SingleScrollbarAnimationControllerThinningTest, MouseNear) { // Move the pointer over the scrollbar. Make sure it gets thick that it gets // thin when moved away. -TEST_F(SingleScrollbarAnimationControllerThinningTest, MouseOver) { +TEST_P(SingleScrollbarAnimationControllerThinningTest, MouseOver) { base::TimeTicks time; time += base::Seconds(1); @@ -180,7 +194,7 @@ TEST_F(SingleScrollbarAnimationControllerThinningTest, MouseOver) { // Moving off the scrollbar but still withing the "near" threshold should do // nothing. scrollbar_controller_->DidMouseMove( - NearScrollbar(-kMouseMoveDistanceToTriggerExpand + 1, 0)); + NearScrollbar(-mouse_move_distance_to_trigger_expand_, 0)); scrollbar_controller_->Animate(time); time += base::Seconds(10); scrollbar_controller_->Animate(time); @@ -188,7 +202,7 @@ TEST_F(SingleScrollbarAnimationControllerThinningTest, MouseOver) { // Now move away from thumb. scrollbar_controller_->DidMouseMove( - NearScrollbar(-kMouseMoveDistanceToTriggerExpand, 0)); + NearScrollbar(-mouse_move_distance_to_trigger_expand_ - 1, 0)); scrollbar_controller_->Animate(time); time += kThinningDuration; scrollbar_controller_->Animate(time); @@ -199,7 +213,7 @@ TEST_F(SingleScrollbarAnimationControllerThinningTest, MouseOver) { // First move the pointer over the scrollbar off of it. Make sure the thinning // animation kicked off in DidMouseMoveOffScrollbar gets overridden by the // thickening animation in the DidMouseMove call. -TEST_F(SingleScrollbarAnimationControllerThinningTest, +TEST_P(SingleScrollbarAnimationControllerThinningTest, MouseNearThenAwayWhileAnimating) { base::TimeTicks time; time += base::Seconds(1); @@ -232,7 +246,8 @@ TEST_F(SingleScrollbarAnimationControllerThinningTest, // Now we get a notification for the mouse moving over the scroller. The // animation is reset to the thickening direction but we won't start // thickening until the new animation catches up to the current thickness. - scrollbar_controller_->DidMouseMove(NearScrollbar(-1, 0)); + scrollbar_controller_->DidMouseMove( + NearScrollbar(-mouse_move_distance_to_trigger_expand_, 0)); scrollbar_controller_->Animate(time); EXPECT_FLOAT_EQ(1.0f - (1.0f - kIdleThicknessScale) / 2.0f, scrollbar_layer_->thumb_thickness_scale_factor()); @@ -264,7 +279,7 @@ TEST_F(SingleScrollbarAnimationControllerThinningTest, // First move the pointer on the scrollbar, then press it, then away. // Confirm that the bar gets thick. Then mouse up. Confirm that // the bar gets thin. -TEST_F(SingleScrollbarAnimationControllerThinningTest, +TEST_P(SingleScrollbarAnimationControllerThinningTest, MouseCaptureAndReleaseOutOfBar) { base::TimeTicks time; time += base::Seconds(1); @@ -289,7 +304,7 @@ TEST_F(SingleScrollbarAnimationControllerThinningTest, // Move outside the "near" threshold. Because the scrollbar is captured it // should remain thick. scrollbar_controller_->DidMouseMove( - NearScrollbar(-kMouseMoveDistanceToTriggerExpand, 0)); + NearScrollbar(-mouse_move_distance_to_trigger_expand_ - 1, 0)); time += kThinningDuration; scrollbar_controller_->Animate(time); EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor()); @@ -309,7 +324,7 @@ TEST_F(SingleScrollbarAnimationControllerThinningTest, // First move the pointer on the scrollbar, then press it, then away. Confirm // that the bar gets thick. Then move point on the scrollbar and mouse up. // Confirm that the bar stays thick. -TEST_F(SingleScrollbarAnimationControllerThinningTest, +TEST_P(SingleScrollbarAnimationControllerThinningTest, MouseCaptureAndReleaseOnBar) { base::TimeTicks time; time += base::Seconds(1); @@ -331,7 +346,7 @@ TEST_F(SingleScrollbarAnimationControllerThinningTest, // Move away from scrollbar. Nothing should change. scrollbar_controller_->DidMouseMove( - NearScrollbar(kMouseMoveDistanceToTriggerExpand, 0)); + NearScrollbar(mouse_move_distance_to_trigger_expand_ + 1, 0)); time += base::Seconds(1); scrollbar_controller_->Animate(time); time += base::Seconds(10); @@ -341,7 +356,7 @@ TEST_F(SingleScrollbarAnimationControllerThinningTest, // Move over scrollbar and release. Since we're near the scrollbar, it should // remain thick. scrollbar_controller_->DidMouseMove( - NearScrollbar(-kMouseMoveDistanceToTriggerExpand + 1, 0)); + NearScrollbar(-mouse_move_distance_to_trigger_expand_, 0)); scrollbar_controller_->DidMouseUp(); time += base::Seconds(1); scrollbar_controller_->Animate(time); @@ -351,13 +366,14 @@ TEST_F(SingleScrollbarAnimationControllerThinningTest, } // Tests that the thickening/thinning effects are animated. -TEST_F(SingleScrollbarAnimationControllerThinningTest, ThicknessAnimated) { +TEST_P(SingleScrollbarAnimationControllerThinningTest, ThicknessAnimated) { base::TimeTicks time; time += base::Seconds(1); // Move mouse near scrollbar. Test that at half the duration time, the // thickness is half way through its animation. - scrollbar_controller_->DidMouseMove(NearScrollbar(-1, 0)); + scrollbar_controller_->DidMouseMove( + NearScrollbar(-mouse_move_distance_to_trigger_expand_, 0)); scrollbar_controller_->Animate(time); EXPECT_FLOAT_EQ(kIdleThicknessScale, scrollbar_layer_->thumb_thickness_scale_factor()); @@ -374,7 +390,7 @@ TEST_F(SingleScrollbarAnimationControllerThinningTest, ThicknessAnimated) { // Move mouse away from scrollbar. Same check. time += base::Seconds(1); scrollbar_controller_->DidMouseMove( - NearScrollbar(-kMouseMoveDistanceToTriggerExpand, 0)); + NearScrollbar(-mouse_move_distance_to_trigger_expand_ - 1, 0)); scrollbar_controller_->Animate(time); EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor()); @@ -389,5 +405,64 @@ TEST_F(SingleScrollbarAnimationControllerThinningTest, ThicknessAnimated) { scrollbar_layer_->thumb_thickness_scale_factor()); } +// Make sure the Fluent scrollbar transitions to the full mode (thick) after +// moving the mouse over scrollbar track and get back to the minimal mode (thin) +// when moved away both inside and outside the layer. +TEST_F(SingleScrollbarAnimationControllerThinningFluentTest, + FluentMouseOverTrack) { + base::TimeTicks time; + time += base::Seconds(1); + + // Move the mouse over the scrollbar track. + scrollbar_controller_->DidMouseMove(NearScrollbar(0, 75)); + scrollbar_controller_->Animate(time); + EXPECT_FLOAT_EQ(kIdleThicknessScale, + scrollbar_layer_->thumb_thickness_scale_factor()); + + // Should animate to the full mode. + time += kThinningDuration; + scrollbar_controller_->Animate(time); + EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor()); + + // Subsequent moves should not change anything. + scrollbar_controller_->DidMouseMove(NearScrollbar(0, 75)); + scrollbar_controller_->Animate(time); + time += base::Seconds(10); + scrollbar_controller_->Animate(time); + EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor()); + + // Moving away from the scrollbar track should trigger the transition to the + // minimal mode. + scrollbar_controller_->DidMouseMove(NearScrollbar(-1, 75)); + scrollbar_controller_->Animate(time); + time += kThinningDuration; + scrollbar_controller_->Animate(time); + EXPECT_FLOAT_EQ(kIdleThicknessScale, + scrollbar_layer_->thumb_thickness_scale_factor()); + + // Move the mouse over the scrollbar track again. Scrollbar should be in the + // full mode. + scrollbar_controller_->DidMouseMove(NearScrollbar(0, 75)); + scrollbar_controller_->Animate(time); + EXPECT_FLOAT_EQ(kIdleThicknessScale, + scrollbar_layer_->thumb_thickness_scale_factor()); + time += kThinningDuration; + scrollbar_controller_->Animate(time); + EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor()); + + // Moving away from the scrollbar track out of the layer should also + // trigger the transition to the minimal mode. + scrollbar_controller_->DidMouseMove(NearScrollbar(kThumbThickness + 1, 75)); + scrollbar_controller_->Animate(time); + time += kThinningDuration; + scrollbar_controller_->Animate(time); + EXPECT_FLOAT_EQ(kIdleThicknessScale, + scrollbar_layer_->thumb_thickness_scale_factor()); +} + +INSTANTIATE_TEST_SUITE_P(All, + SingleScrollbarAnimationControllerThinningTest, + Bool()); + } // namespace } // namespace cc diff --git a/chromium/cc/input/threaded_input_handler.h b/chromium/cc/input/threaded_input_handler.h deleted file mode 100644 index 08a356852ae..00000000000 --- a/chromium/cc/input/threaded_input_handler.h +++ /dev/null @@ -1,472 +0,0 @@ -// 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_INPUT_THREADED_INPUT_HANDLER_H_ -#define CC_INPUT_THREADED_INPUT_HANDLER_H_ - -#include <memory> - -#include "base/containers/flat_set.h" -#include "base/gtest_prod_util.h" -#include "base/memory/raw_ptr.h" -#include "base/time/time.h" -#include "cc/input/compositor_input_interfaces.h" -#include "cc/input/event_listener_properties.h" -#include "cc/input/input_handler.h" -#include "cc/input/scroll_snap_data.h" -#include "cc/input/scroll_state.h" -#include "cc/input/touch_action.h" -#include "cc/metrics/events_metrics_manager.h" -#include "cc/metrics/frame_sequence_metrics.h" -#include "cc/paint/element_id.h" -#include "components/viz/common/frame_sinks/begin_frame_args.h" -#include "third_party/abseil-cpp/absl/types/optional.h" -#include "ui/events/types/scroll_input_type.h" - -namespace gfx { -class Point; -class PointF; -class Vector2dF; -} // namespace gfx - -namespace cc { - -class LatencyInfoSwapPromiseMonitor; -class LayerImpl; -class ScrollbarController; -class ScrollElasticityHelper; -struct ScrollNode; -class ScrollTree; -class Viewport; - -class CC_EXPORT ThreadedInputHandler : public InputHandler, - public InputDelegateForCompositor { - public: - explicit ThreadedInputHandler( - CompositorDelegateForInput& compositor_delegate); - ~ThreadedInputHandler() override; - - // =========== InputHandler "Interface" - will override in a future CL - base::WeakPtr<InputHandler> AsWeakPtr() const override; - void BindToClient(InputHandlerClient* client) override; - InputHandler::ScrollStatus ScrollBegin(ScrollState* scroll_state, - ui::ScrollInputType type) override; - InputHandler::ScrollStatus RootScrollBegin(ScrollState* scroll_state, - ui::ScrollInputType type) override; - InputHandlerScrollResult ScrollUpdate( - 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; - InputHandlerPointerResult MouseMoveAt( - const gfx::Point& viewport_point) override; - InputHandlerPointerResult MouseDown(const gfx::PointF& viewport_point, - bool shift_modifier) override; - InputHandlerPointerResult MouseUp(const gfx::PointF& viewport_point) override; - void MouseLeave() override; - ElementId FindFrameElementIdAtPoint( - const gfx::PointF& viewport_point) override; - void RequestUpdateForSynchronousInputHandler() override; - void SetSynchronousInputHandlerRootScrollOffset( - const gfx::PointF& root_content_offset) override; - void PinchGestureBegin(const gfx::Point& anchor, - ui::ScrollInputType source) override; - void PinchGestureUpdate(float magnify_delta, - const gfx::Point& anchor) override; - void PinchGestureEnd(const gfx::Point& anchor) override; - void SetNeedsAnimateInput() override; - bool IsCurrentlyScrollingViewport() const override; - EventListenerProperties GetEventListenerProperties( - EventListenerClass event_class) const override; - bool HasBlockingWheelEventHandlerAt( - const gfx::Point& viewport_point) const override; - InputHandler::TouchStartOrMoveEventListenerType - EventListenerTypeForTouchStartOrMoveAt( - const gfx::Point& viewport_port, - TouchAction* out_touch_action) override; - std::unique_ptr<LatencyInfoSwapPromiseMonitor> - CreateLatencyInfoSwapPromiseMonitor(ui::LatencyInfo* latency) override; - std::unique_ptr<EventsMetricsManager::ScopedMonitor> - GetScopedEventMetricsMonitor( - EventsMetricsManager::ScopedMonitor::DoneCallback done_callback) override; - ScrollElasticityHelper* CreateScrollElasticityHelper() override; - void DestroyScrollElasticityHelper() override; - bool GetScrollOffsetForLayer(ElementId element_id, - gfx::PointF* offset) override; - bool ScrollLayerTo(ElementId element_id, const gfx::PointF& offset) override; - bool ScrollingShouldSwitchtoMainThread() override; - bool GetSnapFlingInfoAndSetAnimatingSnapTarget( - const gfx::Vector2dF& natural_displacement_in_viewport, - gfx::PointF* out_initial_position, - gfx::PointF* out_target_position) override; - void ScrollEndForSnapFling(bool did_finish) override; - void NotifyInputEvent() override; - bool ScrollbarScrollIsActive() override; - void SetDeferBeginMainFrame(bool defer_begin_main_frame) const override; - - // =========== InputDelegateForCompositor Interface - This section implements - // the interface that LayerTreeHostImpl uses to communicate with the input - // system. - void ProcessCommitDeltas(CompositorCommitData* commit_data) override; - void TickAnimations(base::TimeTicks monotonic_time) override; - void WillShutdown() override; - void WillDraw() override; - void WillBeginImplFrame(const viz::BeginFrameArgs& args) override; - void DidCommit() override; - void DidActivatePendingTree() override; - void RootLayerStateMayHaveChanged() override; - void DidRegisterScrollbar(ElementId scroll_element_id, - ScrollbarOrientation orientation) override; - void DidUnregisterScrollbar(ElementId scroll_element_id, - ScrollbarOrientation orientation) override; - void ScrollOffsetAnimationFinished() override; - void SetPrefersReducedMotion(bool prefers_reduced_motion) override; - bool IsCurrentlyScrolling() const override; - ActivelyScrollingType GetActivelyScrollingType() const override; - - // =========== Public Interface - - bool CanConsumeDelta(const ScrollState& scroll_state, - const ScrollNode& scroll_node); - // Returns the amount of delta that can be applied to scroll_node, taking - // page scale into account. - gfx::Vector2dF ComputeScrollDelta(const ScrollNode& scroll_node, - const gfx::Vector2dF& delta); - - gfx::Vector2dF ScrollSingleNode(const ScrollNode& scroll_node, - const gfx::Vector2dF& delta, - const gfx::Point& viewport_point, - bool is_direct_manipulation); - - float LineStep() const; - - // Resolves a delta in the given granularity for the |scroll_node| into - // physical pixels to scroll. - gfx::Vector2dF ResolveScrollGranularityToPixels( - const ScrollNode& scroll_node, - const gfx::Vector2dF& scroll_delta, - ui::ScrollGranularity granularity); - - // Used to set the pinch gesture active state when the pinch gesture is - // handled on another layer tree. In a page with OOPIFs, only the main - // frame's layer tree directly handles pinch events. But layer trees for - // sub-frames need to know when pinch gestures are active so they can - // throttle the re-rastering. This function allows setting this flag on - // OOPIF layer trees using information sent (initially) from the main-frame. - void set_external_pinch_gesture_active(bool external_pinch_gesture_active) { - external_pinch_gesture_active_ = external_pinch_gesture_active; - // Only one of the flags should ever be true at any given time. - DCHECK(!pinch_gesture_active_ || !external_pinch_gesture_active_); - } - - bool pinch_gesture_active() const { - return pinch_gesture_active_ || external_pinch_gesture_active_; - } - - void set_force_smooth_wheel_scrolling_for_testing(bool enabled) { - force_smooth_wheel_scrolling_for_testing_ = enabled; - } - - gfx::Vector2dF accumulated_root_overscroll_for_testing() const { - return accumulated_root_overscroll_; - } - - bool animating_for_snap_for_testing() const { return IsAnimatingForSnap(); } - - private: - FRIEND_TEST_ALL_PREFIXES(ScrollUnifiedLayerTreeHostImplTest, - AbortAnimatedScrollBeforeStartingAutoscroll); - FRIEND_TEST_ALL_PREFIXES(ScrollUnifiedLayerTreeHostImplTest, - AnimatedScrollYielding); - FRIEND_TEST_ALL_PREFIXES(ScrollUnifiedLayerTreeHostImplTest, - AutoscrollOnDeletedScrollbar); - FRIEND_TEST_ALL_PREFIXES(ScrollUnifiedLayerTreeHostImplTest, - ThumbDragAfterJumpClick); - FRIEND_TEST_ALL_PREFIXES(ScrollUnifiedLayerTreeHostImplTest, - ScrollOnLargeThumb); - FRIEND_TEST_ALL_PREFIXES(LayerTreeHostImplTest, AutoscrollTaskAbort); - - // This method gets the scroll offset for a regular scroller, or the combined - // visual and layout offsets of the viewport. - gfx::PointF GetVisualScrollOffset(const ScrollNode& scroll_node) const; - bool IsScrolledBy(LayerImpl* child, ScrollNode* ancestor); - bool IsAnimatingForSnap() const; - - ScrollNode* CurrentlyScrollingNode(); - const ScrollNode* CurrentlyScrollingNode() const; - void ClearCurrentlyScrollingNode(); - ScrollTree& GetScrollTree(); - ScrollTree& GetScrollTree() const; - Viewport& GetViewport() const; - - ScrollNode* InnerViewportScrollNode() const; - ScrollNode* OuterViewportScrollNode() const; - - void SetNeedsCommit(); - LayerTreeImpl& ActiveTree(); - LayerTreeImpl& ActiveTree() const; - - bool IsMainThreadScrolling(const InputHandler::ScrollStatus& status, - const ScrollNode* scroll_node) const; - - bool IsTouchDraggingScrollbar( - LayerImpl* first_scrolling_layer_or_drawn_scrollbar, - ui::ScrollInputType type); - - void UpdateRootLayerStateForSynchronousInputHandler(); - - // Called during ScrollBegin once a scroller was successfully latched to - // (i.e. it can and will consume scroll delta on the compositor thread). The - // latched scroller is now available in CurrentlyScrollingNode(). - // TODO(bokan): There's some debate about the name of this method. We should - // get consensus on terminology to use and apply it consistently. - // https://crrev.com/c/1981336/9/cc/trees/layer_tree_host_impl.cc#4520 - void DidLatchToScroller(const ScrollState& scroll_state, - ui::ScrollInputType type); - - // This function keeps track of sources of scrolls that are handled in the - // compositor side. The information gets shared by the main thread as part of - // the begin_main_frame_state. Finally Use counters are updated in the main - // thread side to keep track of the frequency of scrolling with different - // sources per page load. TODO(crbug.com/691886): Use GRC API to plumb the - // scroll source info for Use Counters. - void UpdateScrollSourceInfo(const ScrollState& scroll_state, - ui::ScrollInputType type); - - // Applies the scroll_state to the currently latched scroller. See comment in - // InputHandler::ScrollUpdate declaration for the meaning of |delayed_by|. - void ScrollLatchedScroller(ScrollState* scroll_state, - base::TimeDelta delayed_by); - - // Determines whether the given scroll node can scroll on the compositor - // thread or if there are any reasons it must be scrolled on the main thread - // or not at all. Note: in general, this is not sufficient to determine if a - // scroll can occur on the compositor thread. If hit testing to a scroll - // node, the caller must also check whether the hit point intersects a - // non-fast-scrolling-region of any ancestor scrolling layers. Can be removed - // after scroll unification https://crbug.com/476553. - InputHandler::ScrollStatus TryScroll(const ScrollTree& scroll_tree, - ScrollNode* scroll_node) const; - - enum class SnapReason { kGestureScrollEnd, kScrollOffsetAnimationFinished }; - - // Creates an animation curve and returns true if we need to update the - // scroll position to a snap point. Otherwise returns false. - bool SnapAtScrollEnd(SnapReason reason); - - // |layer| is returned from a regular hit test, and - // |first_scrolling_layer_or_drawn_scrollbar| is returned from a hit test - // performed only on scrollers and scrollbars. Initial scroll hit testing can - // be unreliable if the latter is not the direct scroll ancestor of the - // former. In this case, we will fall back to main thread scrolling because - // the compositor thread doesn't know which layer to scroll. This happens when - // a layer covers a scroller that doesn't scroll the former, or a scroller is - // masked by a mask layer for mask image, clip-path, rounded border, etc. - // - // Note, position: fixed layers use the inner viewport as their ScrollNode - // (since they don't scroll with the outer viewport), however, scrolls from - // the fixed layer still chain to the outer viewport. It's also possible for a - // node to have the inner viewport as its ancestor without going through the - // outer viewport; however, it may still scroll using the viewport(). Hence, - // this method must use the same scroll chaining logic we use in ApplyScroll. - bool IsInitialScrollHitTestReliable( - const LayerImpl* layer, - const LayerImpl* first_scrolling_layer_or_drawn_scrollbar) const; - - // Similar to above but includes complicated logic to determine whether the - // ScrollNode is able to be scrolled on the compositor or requires main - // thread scrolling. If main thread scrolling is required - // |scroll_on_main_thread| is set to true and the reason is given in - // |main_thread_scrolling_reason| to on of the enum values in - // main_thread_scrolling_reason.h. Can be removed after scroll unification - // https://crbug.com/476553. - ScrollNode* FindScrollNodeForCompositedScrolling( - const gfx::PointF& device_viewport_point, - LayerImpl* layer_hit_by_point, - bool* scroll_on_main_thread, - uint32_t* main_thread_scrolling_reason); - - // Return all ScrollNode indices that have an associated layer with a non-fast - // region that intersects the point. - base::flat_set<int> NonFastScrollableNodes( - const gfx::PointF& device_viewport_point) const; - - // Returns the ScrollNode we should use to scroll, accounting for viewport - // scroll chaining rules. - ScrollNode* GetNodeToScroll(ScrollNode* node) const; - - // Given a starting node (determined by hit-test), walks up the scroll tree - // looking for the first node that can consume scroll from the given - // scroll_state and returns the first such node. If none is found, or if - // starting_node is nullptr, returns nullptr; - ScrollNode* FindNodeToLatch(ScrollState* scroll_state, - ScrollNode* starting_node, - ui::ScrollInputType type); - - bool CanPropagate(ScrollNode* scroll_node, float x, float y); - - // Performs a hit test to determine the ScrollNode to use when scrolling at - // |viewport_point|. If no layer is hit, this falls back to the inner - // viewport scroll node. Returns: - // - If |hit_test_sucessful| is false, hit testing has failed and the - // compositor cannot determine the correct scroll node (e.g. see comments in - // IsInitialScrollHitTestReliable). |scroll_node| is always nullptr in this - // case. - // - If |hit_test_successful| is true, returns the ScrollNode to use in - // |scroll_node|. This can be nullptr if no layer was hit and there are no - // viewport nodes (e.g. OOPIF, UI compositor). - struct ScrollHitTestResult { - ScrollNode* scroll_node; - bool hit_test_successful; - }; - ScrollHitTestResult HitTestScrollNode( - const gfx::PointF& device_viewport_point) const; - - bool ShouldAnimateScroll(const ScrollState& scroll_state) const; - - bool ScrollAnimationUpdateTarget(const ScrollNode& scroll_node, - const gfx::Vector2dF& scroll_delta, - base::TimeDelta delayed_by); - - // Transforms viewport start point and scroll delta to local start point and - // local delta, respectively. If the transformation of either the start or end - // point of a scroll is clipped, the function returns false. - bool CalculateLocalScrollDeltaAndStartPoint( - const ScrollNode& scroll_node, - const gfx::PointF& viewport_point, - const gfx::Vector2dF& viewport_delta, - gfx::Vector2dF* out_local_scroll_delta, - gfx::PointF* out_local_start_point = nullptr); - gfx::Vector2dF ScrollNodeWithViewportSpaceDelta( - const ScrollNode& scroll_node, - const gfx::PointF& viewport_point, - const gfx::Vector2dF& viewport_delta); - gfx::Vector2dF ScrollNodeWithLocalDelta( - const ScrollNode& scroll_node, - const gfx::Vector2dF& local_delta) const; - // This helper returns an adjusted version of |delta| where the scroll delta - // is cleared in any axis in which user scrolling is disabled (e.g. by - // |overflow-x: hidden|). - gfx::Vector2dF UserScrollableDelta(const ScrollNode& node, - const gfx::Vector2dF& delta) const; - - void AdjustScrollDeltaForScrollbarSnap(ScrollState* scroll_state); - - FrameSequenceTrackerType GetTrackerTypeForScroll( - ui::ScrollInputType input_type) const; - - ScrollbarController* scrollbar_controller_for_testing() const { - return scrollbar_controller_.get(); - } - - // The input handler is owned by the delegate so their lifetimes are tied - // together. - CompositorDelegateForInput& compositor_delegate_; - - raw_ptr<InputHandlerClient> input_handler_client_ = nullptr; - - // An object to implement the ScrollElasticityHelper interface and - // hold all state related to elasticity. May be nullptr if never requested. - std::unique_ptr<ScrollElasticityHelper> scroll_elasticity_helper_; - - // Manages composited scrollbar hit testing. - std::unique_ptr<ScrollbarController> scrollbar_controller_; - - // Overscroll delta accumulated on the viewport throughout a scroll gesture; - // reset when the gesture ends. - gfx::Vector2dF accumulated_root_overscroll_; - - // Unconsumed scroll delta sent to the main thread for firing overscroll DOM - // events. Resets after each commit. - gfx::Vector2dF overscroll_delta_for_main_thread_; - - // The source device type that started the scroll gesture. Only set between a - // ScrollBegin and ScrollEnd. - absl::optional<ui::ScrollInputType> latched_scroll_type_; - - // Tracks the last scroll update/begin state received. Used to infer the most - // recent scroll type and direction. - absl::optional<ScrollState> last_scroll_begin_state_; - absl::optional<ScrollState> last_scroll_update_state_; - - // If a scroll snap is being animated, then the value of this will be the - // element id(s) of the target(s). Otherwise, the ids will be invalid. - // At the end of a scroll animation, the target should be set as the scroll - // node's snap target. - TargetSnapAreaElementIds scroll_animating_snap_target_ids_; - - // A set of elements that scroll-snapped to a new target since the last - // begin main frame. The snap target ids of these elements will be sent to - // the main thread in the next begin main frame. - base::flat_map<ElementId, TargetSnapAreaElementIds> updated_snapped_elements_; - - ElementId scroll_element_id_mouse_currently_over_; - ElementId scroll_element_id_mouse_currently_captured_; - - // Set in ScrollBegin and outlives the currently scrolling node so it can be - // used to send the scrollend and overscroll DOM events from the main thread - // when scrolling occurs on the compositor thread. This value is cleared at - // the first commit after a GSE. - ElementId last_latched_scroller_; - - // Scroll animation can finish either before or after GSE arrival. - // deferred_scroll_end_ is set when the GSE has arrvied before scroll - // animation completion. ScrollEnd will get called once the animation is - // over. - bool deferred_scroll_end_ = false; - - // Set to true when a scroll gesture being handled on the compositor has - // ended. i.e. When a GSE has arrived and any ongoing scroll animation has - // ended. - bool scroll_gesture_did_end_ = false; - - // True iff some of the delta has been consumed for the current scroll - // sequence on the specific axis. - bool did_scroll_x_for_scroll_gesture_ = false; - bool did_scroll_y_for_scroll_gesture_ = false; - - // did_scroll_x/y_for_scroll_gesture_ is true when contents consume the delta, - // but delta_consumed_for_scroll_gesture_ can be true when only browser - // controls consume all the delta. - bool delta_consumed_for_scroll_gesture_ = false; - - // TODO(bokan): Mac doesn't yet have smooth scrolling for wheel; however, to - // allow consistency in tests we use this bit to override that decision. - // https://crbug.com/574283. - bool force_smooth_wheel_scrolling_for_testing_ = false; - - // This value is used to allow the compositor to throttle re-rastering during - // pinch gestures, when the page scale factor may be changing frequently. It - // is set in one of two ways: - // i) In a layer tree serving the root of the frame/compositor tree, it is - // directly set during processing of GesturePinch events on the impl thread - // (only the root layer tree has access to these). - // ii) In a layer tree serving a sub-frame in the frame/compositor tree, it - // is set from the main thread during the commit process, using information - // sent from the root layer tree via IPC messaging. - bool pinch_gesture_active_ = false; - bool external_pinch_gesture_active_ = false; - bool pinch_gesture_end_should_clear_scrolling_node_ = false; - - // These are used to transfer usage of different types of scrolling to the - // main thread. - bool has_pinch_zoomed_ = false; - bool has_scrolled_by_wheel_ = false; - bool has_scrolled_by_touch_ = false; - bool has_scrolled_by_precisiontouchpad_ = false; - bool has_scrolled_by_scrollbar_ = false; - - bool prefers_reduced_motion_ = false; - - // Must be the last member to ensure this is destroyed first in the - // destruction order and invalidates all weak pointers. - base::WeakPtrFactory<ThreadedInputHandler> weak_factory_{this}; -}; - -} // namespace cc - -#endif // CC_INPUT_THREADED_INPUT_HANDLER_H_ diff --git a/chromium/cc/input/touch_action.h b/chromium/cc/input/touch_action.h index 21655306c3a..d5a29507ef2 100644 --- a/chromium/cc/input/touch_action.h +++ b/chromium/cc/input/touch_action.h @@ -39,8 +39,15 @@ enum class TouchAction { // and it doesn't have a horizontal scrollable ancestor (including // itself), we don't set this bit. kInternalPanXScrolls = 0x40, - kAuto = kManipulation | kDoubleTapZoom | kInternalPanXScrolls, - kMax = (1 << 7) - 1 + + // This is used internally by stylus handwriting feature. Stylus writing would + // not be started when this bit is set. When the element is non-password edit + // field and has kPan, we don't set this bit. + kInternalNotWritable = 0x80, + + kAuto = kManipulation | kDoubleTapZoom | kInternalPanXScrolls | + kInternalNotWritable, + kMax = (1 << 8) - 1 }; inline TouchAction operator|(TouchAction a, TouchAction b) { @@ -67,6 +74,11 @@ inline const char* TouchActionToString(TouchAction touch_action) { // we skip printing internal panx scrolls since it's not a web exposed touch // action field. touch_action &= ~TouchAction::kInternalPanXScrolls; + + // we skip printing kInternalNotWritable since it's not a web exposed + // touch action field. + touch_action &= ~TouchAction::kInternalNotWritable; + switch (static_cast<int>(touch_action)) { case 0: return "NONE"; diff --git a/chromium/cc/layers/heads_up_display_layer_impl.cc b/chromium/cc/layers/heads_up_display_layer_impl.cc index bf883bd1e01..17138b5f195 100644 --- a/chromium/cc/layers/heads_up_display_layer_impl.cc +++ b/chromium/cc/layers/heads_up_display_layer_impl.cc @@ -223,7 +223,7 @@ void HeadsUpDisplayLayerImpl::AppendQuads( // layers. gfx::Rect quad_rect(internal_content_bounds_); auto* quad = render_pass->CreateAndAppendDrawQuad<viz::SolidColorDrawQuad>(); - quad->SetNew(shared_quad_state, quad_rect, quad_rect, SK_ColorTRANSPARENT, + quad->SetNew(shared_quad_state, quad_rect, quad_rect, SkColors::kTransparent, false); ValidateQuadResources(quad); current_quad_ = quad; @@ -491,7 +491,7 @@ void HeadsUpDisplayLayerImpl::UpdateHudTexture( resource_id, /*premultiplied_alpha=*/true, /*uv_top_left=*/gfx::PointF(), /*uv_bottom_right=*/uv_bottom_right, - /*background_color=*/SK_ColorTRANSPARENT, vertex_opacity, + /*background_color=*/SkColors::kTransparent, vertex_opacity, /*flipped=*/false, /*nearest_neighbor=*/false, /*secure_output_only=*/false, gfx::ProtectedVideoType::kClear); @@ -588,7 +588,7 @@ void HeadsUpDisplayLayerImpl::DrawHudContents(PaintCanvas* canvas) { const LayerTreeDebugState& debug_state = layer_tree_impl()->debug_state(); TRACE_EVENT0("cc", "DrawHudContents"); - canvas->clear(SkColorSetARGB(0, 0, 0, 0)); + canvas->clear(SkColors::kTransparent); canvas->save(); canvas->scale(internal_contents_scale_); @@ -685,8 +685,7 @@ void HeadsUpDisplayLayerImpl::DrawText(PaintCanvas* canvas, void HeadsUpDisplayLayerImpl::DrawGraphBackground(PaintCanvas* canvas, PaintFlags* flags, const SkRect& bounds) const { - // TODO(crbug/1308932): Remove toSkColor and make all SkColor4f. - flags->setColor(DebugColors::HUDBackgroundColor().toSkColor()); + flags->setColor(DebugColors::HUDBackgroundColor()); canvas->drawRect(bounds, *flags); } @@ -694,8 +693,7 @@ void HeadsUpDisplayLayerImpl::DrawGraphLines(PaintCanvas* canvas, PaintFlags* flags, const SkRect& bounds) const { // Draw top and bottom line. - // TODO(crbug/1308932): Remove toSkColor and make all SkColor4f. - flags->setColor(DebugColors::HUDSeparatorLineColor().toSkColor()); + flags->setColor(DebugColors::HUDSeparatorLineColor()); canvas->drawLine(bounds.left(), bounds.top() - 1, bounds.right(), bounds.top() - 1, *flags); canvas->drawLine(bounds.left(), bounds.bottom(), bounds.right(), @@ -706,8 +704,7 @@ 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); + flags->setColor({1.0f, 1.0f, 1.0f, 0.25f}); canvas->drawLine(bounds.left(), bounds.top(), bounds.right(), bounds.top(), *flags); } @@ -757,13 +754,11 @@ SkRect HeadsUpDisplayLayerImpl::DrawFrameThroughputDisplay( } VLOG(1) << value_text; - // TODO(crbug/1308932): Remove toSkColor and make all SkColor4f. - flags.setColor(DebugColors::HUDTitleColor().toSkColor()); + flags.setColor(DebugColors::HUDTitleColor()); DrawText(canvas, flags, title, TextAlign::kLeft, kTitleFontHeight, title_bounds.left(), title_bounds.bottom()); - // TODO(crbug/1308932): Remove toSkColor and make all SkColor4f. - flags.setColor(DebugColors::FPSDisplayTextAndGraphColor().toSkColor()); + flags.setColor(DebugColors::FPSDisplayTextAndGraphColor()); DrawText(canvas, flags, value_text, TextAlign::kRight, kFontHeight, text_bounds.right(), text_bounds.bottom()); @@ -790,15 +785,13 @@ SkRect HeadsUpDisplayLayerImpl::DrawFrameThroughputDisplay( flags.setStyle(PaintFlags::kStroke_Style); flags.setStrokeWidth(1); - // TODO(crbug/1308932): Remove all instances of toSkColor below and make all - // SkColor4f. - flags.setColor(DebugColors::FPSDisplaySuccessfulFrame().toSkColor()); + flags.setColor(DebugColors::FPSDisplaySuccessfulFrame()); canvas->drawPath(good_path, flags); - flags.setColor(DebugColors::FPSDisplayDroppedFrame().toSkColor()); + flags.setColor(DebugColors::FPSDisplayDroppedFrame()); canvas->drawPath(dropped_path, flags); - flags.setColor(DebugColors::FPSDisplayMissedFrame().toSkColor()); + flags.setColor(DebugColors::FPSDisplayMissedFrame()); canvas->drawPath(partial_path, flags); return area; @@ -828,13 +821,11 @@ SkRect HeadsUpDisplayLayerImpl::DrawMemoryDisplay(PaintCanvas* canvas, SkPoint stat2_pos = SkPoint::Make(left + width - kPadding - 1, top + 2 * kPadding + 3 * kFontHeight); - // TODO(crbug/1308932): Remove toSkColor and make all SkColor4f. - flags.setColor(DebugColors::HUDTitleColor().toSkColor()); + flags.setColor(DebugColors::HUDTitleColor()); DrawText(canvas, flags, "GPU memory", TextAlign::kLeft, kTitleFontHeight, title_pos); - // TODO(crbug/1308932): Remove toSkColor and make all SkColor4f. - flags.setColor(DebugColors::MemoryDisplayTextColor().toSkColor()); + flags.setColor(DebugColors::MemoryDisplayTextColor()); std::string text = base::StringPrintf( "%6.1f MB used", memory_entry_.total_bytes_used / kMegabyte); DrawText(canvas, flags, text, TextAlign::kRight, kFontHeight, stat1_pos); @@ -864,8 +855,11 @@ SkRect HeadsUpDisplayLayerImpl::DrawMemoryDisplay(PaintCanvas* canvas, memory_entry_.total_budget_in_bytes) * 180; - SkColor colors[] = {SK_ColorRED, SK_ColorGREEN, SK_ColorGREEN, - SkColorSetARGB(255, 255, 140, 0), SK_ColorRED}; + SkColor4f colors[] = {SkColors::kRed, + SkColors::kGreen, + SkColors::kGreen, + {1.0f, 0.55f, 0.0f, 1.0f}, + SkColors::kRed}; const SkScalar pos[] = {SkFloatToScalar(0.2f), SkFloatToScalar(0.4f), SkFloatToScalar(0.6f), SkFloatToScalar(0.8f), SkFloatToScalar(1.0f)}; @@ -939,8 +933,8 @@ void HeadsUpDisplayLayerImpl::DrawDebugRect( PaintCanvas* canvas, PaintFlags* flags, const DebugRect& rect, - SkColor stroke_color, - SkColor fill_color, + SkColor4f stroke_color, + SkColor4f fill_color, float stroke_width, const std::string& label_text) const { DCHECK(typeface_.get()); @@ -998,14 +992,12 @@ void HeadsUpDisplayLayerImpl::DrawDebugRects( std::vector<DebugRect> new_layout_shift_rects; for (size_t i = 0; i < debug_rects.size(); ++i) { - SkColor stroke_color = 0; - SkColor fill_color = 0; + SkColor4f stroke_color = SkColors::kTransparent; + SkColor4f fill_color = SkColors::kTransparent; float stroke_width = 0.f; std::string label_text; switch (debug_rects[i].type) { - // TODO(crbug/1308932): Remove all instances of toSkColor below and make - // all SkColor4f. case LAYOUT_SHIFT_RECT_TYPE: new_layout_shift_rects.push_back(debug_rects[i]); continue; @@ -1013,65 +1005,56 @@ void HeadsUpDisplayLayerImpl::DrawDebugRects( new_paint_rects.push_back(debug_rects[i]); continue; case PROPERTY_CHANGED_RECT_TYPE: - stroke_color = - DebugColors::PropertyChangedRectBorderColor().toSkColor(); - fill_color = DebugColors::PropertyChangedRectFillColor().toSkColor(); + stroke_color = DebugColors::PropertyChangedRectBorderColor(); + fill_color = DebugColors::PropertyChangedRectFillColor(); stroke_width = DebugColors::PropertyChangedRectBorderWidth(); break; case SURFACE_DAMAGE_RECT_TYPE: - stroke_color = DebugColors::SurfaceDamageRectBorderColor().toSkColor(); - fill_color = DebugColors::SurfaceDamageRectFillColor().toSkColor(); + stroke_color = DebugColors::SurfaceDamageRectBorderColor(); + fill_color = DebugColors::SurfaceDamageRectFillColor(); stroke_width = DebugColors::SurfaceDamageRectBorderWidth(); break; case SCREEN_SPACE_RECT_TYPE: - stroke_color = - DebugColors::ScreenSpaceLayerRectBorderColor().toSkColor(); - fill_color = DebugColors::ScreenSpaceLayerRectFillColor().toSkColor(); + stroke_color = DebugColors::ScreenSpaceLayerRectBorderColor(); + fill_color = DebugColors::ScreenSpaceLayerRectFillColor(); stroke_width = DebugColors::ScreenSpaceLayerRectBorderWidth(); break; case TOUCH_EVENT_HANDLER_RECT_TYPE: - stroke_color = - DebugColors::TouchEventHandlerRectBorderColor().toSkColor(); - fill_color = DebugColors::TouchEventHandlerRectFillColor().toSkColor(); + stroke_color = DebugColors::TouchEventHandlerRectBorderColor(); + fill_color = DebugColors::TouchEventHandlerRectFillColor(); stroke_width = DebugColors::TouchEventHandlerRectBorderWidth(); label_text = "touch event listener: "; label_text.append(TouchActionToString(debug_rects[i].touch_action)); break; case WHEEL_EVENT_HANDLER_RECT_TYPE: - stroke_color = - DebugColors::WheelEventHandlerRectBorderColor().toSkColor(); - fill_color = DebugColors::WheelEventHandlerRectFillColor().toSkColor(); + stroke_color = DebugColors::WheelEventHandlerRectBorderColor(); + fill_color = DebugColors::WheelEventHandlerRectFillColor(); stroke_width = DebugColors::WheelEventHandlerRectBorderWidth(); label_text = "mousewheel event listener"; break; case SCROLL_EVENT_HANDLER_RECT_TYPE: - stroke_color = - DebugColors::ScrollEventHandlerRectBorderColor().toSkColor(); - fill_color = DebugColors::ScrollEventHandlerRectFillColor().toSkColor(); + stroke_color = DebugColors::ScrollEventHandlerRectBorderColor(); + fill_color = DebugColors::ScrollEventHandlerRectFillColor(); stroke_width = DebugColors::ScrollEventHandlerRectBorderWidth(); label_text = "scroll event listener"; break; case NON_FAST_SCROLLABLE_RECT_TYPE: - stroke_color = - DebugColors::NonFastScrollableRectBorderColor().toSkColor(); - fill_color = DebugColors::NonFastScrollableRectFillColor().toSkColor(); + stroke_color = DebugColors::NonFastScrollableRectBorderColor(); + fill_color = DebugColors::NonFastScrollableRectFillColor(); stroke_width = DebugColors::NonFastScrollableRectBorderWidth(); label_text = "repaints on scroll"; break; case MAIN_THREAD_SCROLLING_REASON_RECT_TYPE: - stroke_color = - DebugColors::MainThreadScrollingReasonRectBorderColor().toSkColor(); - fill_color = - DebugColors::MainThreadScrollingReasonRectFillColor().toSkColor(); + stroke_color = DebugColors::MainThreadScrollingReasonRectBorderColor(); + fill_color = DebugColors::MainThreadScrollingReasonRectFillColor(); stroke_width = DebugColors::MainThreadScrollingReasonRectBorderWidth(); label_text = "main thread scrolling: "; label_text.append(base::ToLowerASCII(MainThreadScrollingReason::AsText( debug_rects[i].main_thread_scrolling_reasons))); break; case ANIMATION_BOUNDS_RECT_TYPE: - stroke_color = - DebugColors::LayerAnimationBoundsBorderColor().toSkColor(); - fill_color = DebugColors::LayerAnimationBoundsFillColor().toSkColor(); + stroke_color = DebugColors::LayerAnimationBoundsBorderColor(); + fill_color = DebugColors::LayerAnimationBoundsFillColor(); stroke_width = DebugColors::LayerAnimationBoundsBorderWidth(); label_text = "animation bounds"; break; @@ -1088,13 +1071,10 @@ void HeadsUpDisplayLayerImpl::DrawDebugRects( if (paint_rects_fade_step_ > 0) { paint_rects_fade_step_--; for (auto& paint_rect : paint_rects_) { - // TODO(crbug/1308932): Remove all instances of toSkColor below and make - // all SkColor4f. - DrawDebugRect( - canvas, &flags, paint_rect, - DebugColors::PaintRectBorderColor(paint_rects_fade_step_).toSkColor(), - DebugColors::PaintRectFillColor(paint_rects_fade_step_).toSkColor(), - DebugColors::PaintRectBorderWidth(), ""); + DrawDebugRect(canvas, &flags, paint_rect, + DebugColors::PaintRectBorderColor(paint_rects_fade_step_), + DebugColors::PaintRectFillColor(paint_rects_fade_step_), + DebugColors::PaintRectBorderWidth(), ""); } } if (new_layout_shift_rects.size()) { @@ -1108,9 +1088,8 @@ void HeadsUpDisplayLayerImpl::DrawDebugRects( // all SkColor4f. DrawDebugRect( canvas, &flags, layout_shift_debug_rect, - DebugColors::LayoutShiftRectBorderColor().toSkColor(), - DebugColors::LayoutShiftRectFillColor(layout_shift_rects_fade_step_) - .toSkColor(), + DebugColors::LayoutShiftRectBorderColor(), + DebugColors::LayoutShiftRectFillColor(layout_shift_rects_fade_step_), DebugColors::LayoutShiftRectBorderWidth(), ""); } } @@ -1126,19 +1105,18 @@ int HeadsUpDisplayLayerImpl::DrawSingleMetric( bool has_value, double value) const { std::string value_str = "-"; - // TODO(crbug/1308932): Remove toSkColor and make all SkColor4f. - SkColor metrics_color = DebugColors::HUDTitleColor().toSkColor(); - SkColor badge_color = SK_ColorGREEN; + SkColor4f metrics_color = DebugColors::HUDTitleColor(); + SkColor4f badge_color = SkColors::kGreen; if (has_value) { value_str = ToStringTwoDecimalPrecision(value) + info.UnitToString(); if (value < info.green_threshold) { - metrics_color = SK_ColorGREEN; + metrics_color = SkColors::kGreen; } else if (value < info.yellow_threshold) { - metrics_color = SK_ColorYELLOW; - badge_color = SK_ColorYELLOW; + metrics_color = SkColors::kYellow; + badge_color = SkColors::kYellow; } else { - metrics_color = SK_ColorRED; - badge_color = SK_ColorRED; + metrics_color = SkColors::kRed; + badge_color = SkColors::kRed; } } @@ -1147,13 +1125,13 @@ int HeadsUpDisplayLayerImpl::DrawSingleMetric( badge_flags.setColor(badge_color); badge_flags.setStyle(PaintFlags::kFill_Style); badge_flags.setAntiAlias(true); - if (badge_color == SK_ColorGREEN) { + if (badge_color == SkColors::kGreen) { constexpr int kRadius = 6; int x = left + metrics_sizes.kSidePadding + kRadius; int y = top - kRadius - 2; SkPath circle = SkPath::Circle(x, y, kRadius); canvas->drawPath(circle, badge_flags); - } else if (badge_color == SK_ColorYELLOW) { + } else if (badge_color == SkColors::kYellow) { constexpr int kSquareSize = 12; int x = left + metrics_sizes.kSidePadding; int y = top - kSquareSize - 2; @@ -1175,8 +1153,7 @@ int HeadsUpDisplayLayerImpl::DrawSingleMetric( // Draw the label and values of the metric. PaintFlags flags; - // TODO(crbug/1308932): Remove toSkColor and make all SkColor4f. - flags.setColor(DebugColors::HUDTitleColor().toSkColor()); + flags.setColor(DebugColors::HUDTitleColor()); DrawText(canvas, flags, name, TextAlign::kLeft, metrics_sizes.kFontHeight, left + metrics_sizes.kSidePadding + metrics_sizes.kBadgeWidth, top); flags.setColor(metrics_color); diff --git a/chromium/cc/layers/heads_up_display_layer_impl.h b/chromium/cc/layers/heads_up_display_layer_impl.h index 6bc0fff3c4a..7762af57f1a 100644 --- a/chromium/cc/layers/heads_up_display_layer_impl.h +++ b/chromium/cc/layers/heads_up_display_layer_impl.h @@ -130,8 +130,8 @@ class CC_EXPORT HeadsUpDisplayLayerImpl : public LayerImpl { void DrawDebugRect(PaintCanvas* canvas, PaintFlags* flags, const DebugRect& rect, - SkColor stroke_color, - SkColor fill_color, + SkColor4f stroke_color, + SkColor4f fill_color, float stroke_width, const std::string& label_text) const; void DrawDebugRects(PaintCanvas* canvas, diff --git a/chromium/cc/layers/layer.cc b/chromium/cc/layers/layer.cc index 7e9dd9f6e9b..eda9833edb1 100644 --- a/chromium/cc/layers/layer.cc +++ b/chromium/cc/layers/layer.cc @@ -546,10 +546,9 @@ void Layer::SetSafeOpaqueBackgroundColor(SkColor4f background_color) { SetNeedsPushProperties(); } -SkColor4f Layer::SafeOpaqueBackgroundColor( - SkColor4f host_background_color) const { +SkColor4f Layer::SafeOpaqueBackgroundColor() const { if (contents_opaque()) { - if (!IsAttached() || !IsUsingLayerLists()) { + if (!IsUsingLayerLists()) { // In layer tree mode, PropertyTreeBuilder should have calculated the safe // opaque background color and called SetSafeOpaqueBackgroundColor(). DCHECK(layer_tree_inputs()); @@ -558,12 +557,8 @@ SkColor4f Layer::SafeOpaqueBackgroundColor( } // 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. - SkColor4f color = background_color() == SkColors::kTransparent - ? host_background_color - : background_color(); - return color.makeOpaque(); + // background_color() made opaque. + return background_color().makeOpaque(); } if (background_color().isOpaque()) { // The layer is not opaque while the background color is, meaning that the @@ -575,16 +570,6 @@ SkColor4f Layer::SafeOpaqueBackgroundColor( return background_color(); } -SkColor4f Layer::SafeOpaqueBackgroundColor() const { - // TODO(crbug/1308932): Remove FromColor and make all SkColor4f. - SkColor4f host_background_color = - IsAttached() - ? SkColor4f::FromColor( - layer_tree_host()->pending_commit_state()->background_color) - : layer_tree_inputs()->safe_opaque_background_color; - return SafeOpaqueBackgroundColor(host_background_color); -} - void Layer::SetMasksToBounds(bool masks_to_bounds) { DCHECK(IsPropertyChangeAllowed()); auto& inputs = EnsureLayerTreeInputs(); @@ -1469,9 +1454,7 @@ void Layer::PushPropertiesTo(LayerImpl* layer, layer->SetElementId(inputs.element_id); layer->SetHasTransformNode(has_transform_node()); layer->SetBackgroundColor(inputs.background_color); - // TODO(crbug/1308932): Remove FromColor and make all SkColor4f. - layer->SetSafeOpaqueBackgroundColor(SafeOpaqueBackgroundColor( - SkColor4f::FromColor(commit_state.background_color))); + layer->SetSafeOpaqueBackgroundColor(SafeOpaqueBackgroundColor()); layer->SetBounds(inputs.bounds); layer->SetTransformTreeIndex(transform_tree_index(property_trees)); layer->SetEffectTreeIndex(effect_tree_index(property_trees)); diff --git a/chromium/cc/layers/layer.h b/chromium/cc/layers/layer.h index 588cecfa2a0..86870398556 100644 --- a/chromium/cc/layers/layer.h +++ b/chromium/cc/layers/layer.h @@ -169,8 +169,9 @@ class CC_EXPORT Layer : public base::RefCounted<Layer>, return layer_tree_inputs() && !layer_tree_inputs()->copy_requests.empty(); } - // Set and get the background color for the layer. This color is not used by - // basic Layers, but subclasses may make use of it. + // Set and get the background color for the layer. This color is used to + // calculate the safe opaque background color. Subclasses may also use the + // color for other purposes. virtual void SetBackgroundColor(SkColor4f background_color); SkColor4f background_color() const { return inputs_.Read(*this).background_color; @@ -185,17 +186,14 @@ class CC_EXPORT Layer : public base::RefCounted<Layer>, // contents_opaque(). // 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 the argument host_background_color. + // opaque color, and in layer list mode, returns background_color() which + // should be opaque (otherwise SetBackgroundColor() should have set + // contents_opaque to false). // 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 SkColors::kTransparent is returned to avoid // intrusive checkerboard where the layer is not covered by the // background_color(). - SkColor4f SafeOpaqueBackgroundColor(SkColor4f host_background_color) const; - - // Same as the one-argument version, except that host_background_color is - // layer_tree_host()->pending_commit_state()->background_color. SkColor4f SafeOpaqueBackgroundColor() const; // For layer tree mode only. @@ -722,7 +720,7 @@ class CC_EXPORT Layer : public base::RefCounted<Layer>, // Internal method to be overridden by Layer subclasses that need to do work // during a main frame. The method should compute any state that will need to - // propogated to the compositor thread for the next commit, and return true + // propagated to the compositor thread for the next commit, and return true // if there is anything new to commit. If all layers return false, the commit // may be aborted. virtual bool Update(); diff --git a/chromium/cc/layers/layer_impl.cc b/chromium/cc/layers/layer_impl.cc index 1e9e856c2da..138ca367425 100644 --- a/chromium/cc/layers/layer_impl.cc +++ b/chromium/cc/layers/layer_impl.cc @@ -273,15 +273,14 @@ void LayerImpl::AppendDebugBorderQuad( gfx::Rect visible_quad_rect(quad_rect); auto* debug_border_quad = render_pass->CreateAndAppendDrawQuad<viz::DebugBorderDrawQuad>(); - // TODO(crbug/1308932): Remove toSkColor and make all SkColor4f. debug_border_quad->SetNew(shared_quad_state, quad_rect, visible_quad_rect, - color.toSkColor(), width); + color, width); if (contents_opaque()) { // When opaque, draw a second inner border that is thicker than the outer // border, but more transparent. static const float kFillOpacity = 0.3f; SkColor4f fill_color = color; - color.fA *= kFillOpacity; + fill_color.fA *= kFillOpacity; float fill_width = width * 3; gfx::Rect fill_rect = quad_rect; fill_rect.Inset(fill_width / 2.f); @@ -289,11 +288,10 @@ void LayerImpl::AppendDebugBorderQuad( return; gfx::Rect visible_fill_rect = gfx::IntersectRects(visible_quad_rect, fill_rect); - // TODO(crbug/1308932): Remove toSkColor and make all SkColor4f. auto* fill_quad = render_pass->CreateAndAppendDrawQuad<viz::DebugBorderDrawQuad>(); fill_quad->SetNew(shared_quad_state, fill_rect, visible_fill_rect, - fill_color.toSkColor(), fill_width); + fill_color, fill_width); } } diff --git a/chromium/cc/layers/layer_impl_unittest.cc b/chromium/cc/layers/layer_impl_unittest.cc index 095e57d61a4..eb85e3a7313 100644 --- a/chromium/cc/layers/layer_impl_unittest.cc +++ b/chromium/cc/layers/layer_impl_unittest.cc @@ -417,7 +417,8 @@ TEST_F(LayerImplScrollTest, ApplySentScrollsNoListener) { scroll_tree(layer())->GetScrollOffsetBaseForTesting( layer()->element_id())); - scroll_tree(layer())->ApplySentScrollDeltasFromAbortedCommit(); + scroll_tree(layer())->ApplySentScrollDeltasFromAbortedCommit( + /*main_frame_applied_deltas=*/true); EXPECT_POINTF_EQ(scroll_offset + scroll_delta, CurrentScrollOffset(layer())); EXPECT_VECTOR2DF_EQ(scroll_delta - sent_scroll_delta, ScrollDelta(layer())); diff --git a/chromium/cc/layers/layer_unittest.cc b/chromium/cc/layers/layer_unittest.cc index e822609d136..e2798ca1bd1 100644 --- a/chromium/cc/layers/layer_unittest.cc +++ b/chromium/cc/layers/layer_unittest.cc @@ -1402,7 +1402,7 @@ TEST_F(LayerTest, SafeOpaqueBackgroundColor) { layer->SetBackgroundColor(layer_opaque ? SkColors::kRed : SkColors::kTransparent); layer_tree_host->set_background_color( - host_opaque ? SK_ColorRED : SK_ColorTRANSPARENT); + host_opaque ? SkColors::kRed : SkColors::kTransparent); layer_tree_host->property_trees()->set_needs_rebuild(true); layer_tree_host->BuildPropertyTreesForTesting(); diff --git a/chromium/cc/layers/nine_patch_generator.cc b/chromium/cc/layers/nine_patch_generator.cc index 7f8b7e33590..a2b627e563d 100644 --- a/chromium/cc/layers/nine_patch_generator.cc +++ b/chromium/cc/layers/nine_patch_generator.cc @@ -361,7 +361,7 @@ void NinePatchGenerator::AppendQuads(LayerImpl* layer_impl, auto* quad = render_pass->CreateAndAppendDrawQuad<viz::TextureDrawQuad>(); quad->SetNew(shared_quad_state, output_rect, visible_rect, needs_blending, resource, premultiplied_alpha, image_rect.origin(), - image_rect.bottom_right(), SK_ColorTRANSPARENT, + image_rect.bottom_right(), SkColors::kTransparent, vertex_opacity, flipped, nearest_neighbor_, /*secure_output_only=*/false, gfx::ProtectedVideoType::kClear); diff --git a/chromium/cc/layers/painted_overlay_scrollbar_layer.cc b/chromium/cc/layers/painted_overlay_scrollbar_layer.cc index 33ff15fb04b..7fd89fc5386 100644 --- a/chromium/cc/layers/painted_overlay_scrollbar_layer.cc +++ b/chromium/cc/layers/painted_overlay_scrollbar_layer.cc @@ -155,7 +155,7 @@ bool PaintedOverlayScrollbarLayer::PaintThumbIfNeeded() { SkBitmap skbitmap; skbitmap.allocN32Pixels(paint_size.width(), paint_size.height()); SkiaPaintCanvas canvas(skbitmap); - canvas.clear(SK_ColorTRANSPARENT); + canvas.clear(SkColors::kTransparent); scrollbar->PaintPart(&canvas, ScrollbarPart::THUMB, gfx::Rect(paint_size)); // Make sure that the pixels are no longer mutable to unavoid unnecessary @@ -188,7 +188,7 @@ bool PaintedOverlayScrollbarLayer::PaintTickmarks() { SkBitmap skbitmap; skbitmap.allocN32Pixels(paint_size.width(), paint_size.height()); SkiaPaintCanvas canvas(skbitmap); - canvas.clear(SK_ColorTRANSPARENT); + canvas.clear(SkColors::kTransparent); scrollbar_.Write(*this)->PaintPart( &canvas, ScrollbarPart::TRACK_BUTTONS_TICKMARKS, gfx::Rect(paint_size)); diff --git a/chromium/cc/layers/painted_overlay_scrollbar_layer_impl.cc b/chromium/cc/layers/painted_overlay_scrollbar_layer_impl.cc index efa31ec9e4e..0cb95310ac2 100644 --- a/chromium/cc/layers/painted_overlay_scrollbar_layer_impl.cc +++ b/chromium/cc/layers/painted_overlay_scrollbar_layer_impl.cc @@ -162,11 +162,12 @@ void PaintedOverlayScrollbarLayerImpl::AppendTrackQuads( float opacity[] = {1.0f, 1.0f, 1.0f, 1.0f}; viz::TextureDrawQuad* quad = render_pass->CreateAndAppendDrawQuad<viz::TextureDrawQuad>(); - quad->SetNew( - shared_quad_state, scaled_track_quad_rect, scaled_visible_track_quad_rect, - needs_blending, track_resource_id, premultipled_alpha, uv_top_left, - uv_bottom_right, SK_ColorTRANSPARENT, opacity, flipped, nearest_neighbor, - /*secure_output_only=*/false, gfx::ProtectedVideoType::kClear); + quad->SetNew(shared_quad_state, scaled_track_quad_rect, + scaled_visible_track_quad_rect, needs_blending, + track_resource_id, premultipled_alpha, uv_top_left, + uv_bottom_right, SkColors::kTransparent, opacity, flipped, + nearest_neighbor, + /*secure_output_only=*/false, gfx::ProtectedVideoType::kClear); ValidateQuadResources(quad); } diff --git a/chromium/cc/layers/painted_scrollbar_layer.cc b/chromium/cc/layers/painted_scrollbar_layer.cc index fb570bf43a0..ca107ea2712 100644 --- a/chromium/cc/layers/painted_scrollbar_layer.cc +++ b/chromium/cc/layers/painted_scrollbar_layer.cc @@ -256,7 +256,7 @@ UIResourceBitmap PaintedScrollbarLayer::RasterizeScrollbarPart( << "Failed to allocate memory for scrollbar at dimension : " << dimension; SkiaPaintCanvas canvas(skbitmap); - canvas.clear(SK_ColorTRANSPARENT); + canvas.clear(SkColors::kTransparent); float scale_x = content_size.width() / static_cast<float>(size.width()); float scale_y = content_size.height() / static_cast<float>(size.height()); diff --git a/chromium/cc/layers/painted_scrollbar_layer_impl.cc b/chromium/cc/layers/painted_scrollbar_layer_impl.cc index 63ac939b34a..3fffa290a18 100644 --- a/chromium/cc/layers/painted_scrollbar_layer_impl.cc +++ b/chromium/cc/layers/painted_scrollbar_layer_impl.cc @@ -130,7 +130,7 @@ void PaintedScrollbarLayerImpl::AppendQuads( quad->SetNew(shared_quad_state, scaled_thumb_quad_rect, scaled_visible_thumb_quad_rect, needs_blending, thumb_resource_id, premultipled_alpha, uv_top_left, - uv_bottom_right, SK_ColorTRANSPARENT, opacity, flipped, + uv_bottom_right, SkColors::kTransparent, opacity, flipped, nearest_neighbor, /*secure_output_only=*/false, gfx::ProtectedVideoType::kClear); ValidateQuadResources(quad); @@ -150,7 +150,7 @@ void PaintedScrollbarLayerImpl::AppendQuads( quad->SetNew(shared_quad_state, scaled_track_quad_rect, scaled_visible_track_quad_rect, needs_blending, track_resource_id, premultipled_alpha, uv_top_left, - uv_bottom_right, SK_ColorTRANSPARENT, opacity, flipped, + uv_bottom_right, SkColors::kTransparent, opacity, flipped, nearest_neighbor, /*secure_output_only=*/false, gfx::ProtectedVideoType::kClear); ValidateQuadResources(quad); diff --git a/chromium/cc/layers/picture_layer_impl.cc b/chromium/cc/layers/picture_layer_impl.cc index 39e68be28b6..11be1253895 100644 --- a/chromium/cc/layers/picture_layer_impl.cc +++ b/chromium/cc/layers/picture_layer_impl.cc @@ -230,10 +230,9 @@ void PictureLayerImpl::AppendQuads(viz::CompositorRenderPass* render_pass, Occlusion occlusion = draw_properties().occlusion_in_content_space; EffectNode* effect_node = GetEffectTree().Node(effect_tree_index()); - // TODO(crbug/1308932): Remove FromColor and make all SkColor4f. SolidColorLayerImpl::AppendSolidQuads( render_pass, occlusion, shared_quad_state, scaled_visible_layer_rect, - SkColor4f::FromColor(raster_source_->GetSolidColor()), + raster_source_->GetSolidColor(), !layer_tree_impl()->settings().enable_edge_anti_aliasing, effect_node->blend_mode, append_quads_data); return; @@ -400,24 +399,20 @@ void PictureLayerImpl::AppendQuads(viz::CompositorRenderPass* render_pass, gfx::Rect geometry_rect = iter.geometry_rect(); geometry_rect.Offset(quad_offset); gfx::Rect visible_geometry_rect = geometry_rect; - // TODO(crbug/1308932): Remove toSkColor and make all SkColor4f. debug_border_quad->SetNew(shared_quad_state, geometry_rect, - visible_geometry_rect, color.toSkColor(), - width); + visible_geometry_rect, color, width); } } if (layer_tree_impl()->debug_state().highlight_non_lcd_text_layers) { - // TODO(crbug/1308932): Remove all instances of toSkColor below and make all - // SkColor4f. SkColor4f color = DebugColors::NonLCDTextHighlightColor(lcd_text_disallowed_reason()); if (color != SkColors::kTransparent && GetRasterSource()->GetDisplayItemList()->AreaOfDrawText( gfx::Rect(bounds()))) { render_pass->CreateAndAppendDrawQuad<viz::SolidColorDrawQuad>()->SetNew( - shared_quad_state, debug_border_rect, debug_border_rect, - color.toSkColor(), append_quads_data); + shared_quad_state, debug_border_rect, debug_border_rect, color, + append_quads_data); } } @@ -493,9 +488,7 @@ void PictureLayerImpl::AppendQuads(viz::CompositorRenderPass* render_pass, break; } case TileDrawInfo::SOLID_COLOR_MODE: { - float alpha = - (SkColorGetA(draw_info.solid_color()) * (1.0f / 255.0f)) * - shared_quad_state->opacity; + float alpha = draw_info.solid_color().fA * shared_quad_state->opacity; if (alpha >= std::numeric_limits<float>::epsilon()) { auto* quad = render_pass->CreateAndAppendDrawQuad<viz::SolidColorDrawQuad>(); @@ -522,9 +515,8 @@ void PictureLayerImpl::AppendQuads(viz::CompositorRenderPass* render_pass, } auto* quad = render_pass->CreateAndAppendDrawQuad<viz::SolidColorDrawQuad>(); - // TODO(crbug/1308932): Remove toSkColor and make all SkColor4f. quad->SetNew(shared_quad_state, offset_geometry_rect, - offset_visible_geometry_rect, color.toSkColor(), false); + offset_visible_geometry_rect, color, false); ValidateQuadResources(quad); if (geometry_rect.Intersects(scaled_viewport_for_tile_priority)) { @@ -547,6 +539,14 @@ void PictureLayerImpl::AppendQuads(viz::CompositorRenderPass* render_pass, append_quads_data->checkerboarded_no_recording_content_area += visible_geometry_area - checkerboarded_has_recording_area; + // Report data on any missing images that might be the largest + // contentful image. + if (*iter) { + UMA_HISTOGRAM_BOOLEAN( + "Compositing.DecodeLCPCandidateImage.MissedDeadline", + iter->HasMissingLCPCandidateImages()); + } + continue; } @@ -1249,6 +1249,10 @@ bool PictureLayerImpl::CanRecreateHighResTilingForLCDTextAndRasterTransform( // will be deleted. if (layer_tree_impl()->IsSyncTree() && layer_tree_impl()->IsReadyToActivate()) return false; + // To reduce memory usage, don't recreate highres tiling during scroll + if (ScrollInteractionInProgress()) + return false; + return true; } diff --git a/chromium/cc/layers/picture_layer_impl_unittest.cc b/chromium/cc/layers/picture_layer_impl_unittest.cc index ba9a96637b4..2f8c8ee5fe8 100644 --- a/chromium/cc/layers/picture_layer_impl_unittest.cc +++ b/chromium/cc/layers/picture_layer_impl_unittest.cc @@ -1722,7 +1722,7 @@ TEST_F(LegacySWPictureLayerImplTest, FarScrolledSolidColorQuadsShifted) { ASSERT_GT(tiles.size(), 0u); for (auto* tile : tiles) - tile->draw_info().SetSolidColorForTesting(SK_ColorBLUE); + tile->draw_info().SetSolidColorForTesting(SkColors::kBlue); AppendQuadsData data; active_layer()->WillDraw(DRAW_MODE_HARDWARE, nullptr); @@ -1917,7 +1917,7 @@ TEST_F(NoLowResPictureLayerImplTest, num_inside++; // Mark everything in viewport for tile priority as ready to draw. TileDrawInfo& draw_info = tile->draw_info(); - draw_info.SetSolidColorForTesting(SK_ColorRED); + draw_info.SetSolidColorForTesting(SkColors::kRed); } else { num_outside++; EXPECT_FALSE(tile->required_for_activation()); @@ -3371,7 +3371,7 @@ TEST_F(LegacySWPictureLayerImplTest, TilingSetRasterQueue) { ++tile_it) { Tile* tile = *tile_it; TileDrawInfo& draw_info = tile->draw_info(); - draw_info.SetSolidColorForTesting(SK_ColorRED); + draw_info.SetSolidColorForTesting(SkColors::kRed); } queue = std::make_unique<TilingSetRasterQueueAll>( @@ -4960,7 +4960,7 @@ void PictureLayerImplTest::TestQuadsForSolidColor(bool test_for_solid, for (auto it = tiles.begin(); it != tiles.end(); ++it, ++i) { if (i < 5) { TileDrawInfo& draw_info = (*it)->draw_info(); - draw_info.SetSolidColorForTesting(0); + draw_info.SetSolidColorForTesting(SkColors::kTransparent); } else { resource_tiles.push_back(*it); } diff --git a/chromium/cc/layers/picture_layer_unittest.cc b/chromium/cc/layers/picture_layer_unittest.cc index 986784742ad..93474da9d80 100644 --- a/chromium/cc/layers/picture_layer_unittest.cc +++ b/chromium/cc/layers/picture_layer_unittest.cc @@ -401,14 +401,14 @@ TEST(PictureLayerTest, RecordingScaleIsCorrectlySet) { client.set_bounds(layer_bounds); // Fill layer with solid color. PaintFlags solid_flags; - SkColor solid_color = SkColorSetARGB(255, 12, 23, 34); + SkColor4f solid_color{0.1f, 0.15f, 0.2f, 1.0f}; solid_flags.setColor(solid_color); client.add_draw_rect( gfx::ScaleToEnclosingRect(gfx::Rect(layer_bounds), recording_scale), solid_flags); // Add 1 pixel of non solid color. - SkColor non_solid_color = SkColorSetARGB(128, 45, 56, 67); + SkColor4f non_solid_color{0.25f, 0.3f, 0.35f, 0.5f}; PaintFlags non_solid_flags; non_solid_flags.setColor(non_solid_color); client.add_draw_rect(gfx::Rect(std::round(390 * recording_scale), @@ -444,7 +444,7 @@ TEST(PictureLayerTest, RecordingScaleIsCorrectlySet) { layer->SetNeedsDisplayRect(invalidation_bounds); layer->Update(); - // Once the recording scale is set and propogated to the recording source, + // Once the recording scale is set and propagated to the recording source, // the solid color analysis should work as expected and return false. EXPECT_FALSE(recording_source->is_solid_color()); } diff --git a/chromium/cc/layers/recording_source_unittest.cc b/chromium/cc/layers/recording_source_unittest.cc index b3ddae76a0c..11869bb0a08 100644 --- a/chromium/cc/layers/recording_source_unittest.cc +++ b/chromium/cc/layers/recording_source_unittest.cc @@ -336,10 +336,10 @@ TEST(RecordingSourceTest, AnalyzeIsSolid) { recording_source->SetRecordingScaleFactor(recording_scale); PaintFlags solid_flags; - SkColor solid_color = SkColorSetARGB(255, 12, 23, 34); + SkColor4f solid_color{0.1f, 0.2f, 0.3f, 1.0f}; solid_flags.setColor(solid_color); - SkColor non_solid_color = SkColorSetARGB(128, 45, 56, 67); + SkColor4f non_solid_color{0.2f, 0.3f, 0.4f, 0.5f}; PaintFlags non_solid_flags; non_solid_flags.setColor(non_solid_color); diff --git a/chromium/cc/layers/render_surface_impl.cc b/chromium/cc/layers/render_surface_impl.cc index d274baf743c..01d48a8b513 100644 --- a/chromium/cc/layers/render_surface_impl.cc +++ b/chromium/cc/layers/render_surface_impl.cc @@ -110,9 +110,8 @@ SkBlendMode RenderSurfaceImpl::BlendMode() const { return OwningEffectNode()->blend_mode; } -SkColor RenderSurfaceImpl::GetDebugBorderColor() const { - // TODO(crbug/1308932): Remove toSkColor and make all SkColor4f. - return DebugColors::SurfaceBorderColor().toSkColor(); +SkColor4f RenderSurfaceImpl::GetDebugBorderColor() const { + return DebugColors::SurfaceBorderColor(); } float RenderSurfaceImpl::GetDebugBorderWidth() const { diff --git a/chromium/cc/layers/render_surface_impl.h b/chromium/cc/layers/render_surface_impl.h index 057cf802842..f7a26dca966 100644 --- a/chromium/cc/layers/render_surface_impl.h +++ b/chromium/cc/layers/render_surface_impl.h @@ -90,7 +90,7 @@ class CC_EXPORT RenderSurfaceImpl { return nearest_occlusion_immune_ancestor_; } - SkColor GetDebugBorderColor() const; + SkColor4f GetDebugBorderColor() const; float GetDebugBorderWidth() const; void SetDrawTransform(const gfx::Transform& draw_transform) { diff --git a/chromium/cc/layers/solid_color_layer_impl.cc b/chromium/cc/layers/solid_color_layer_impl.cc index 7e6332cf236..d2e8930c832 100644 --- a/chromium/cc/layers/solid_color_layer_impl.cc +++ b/chromium/cc/layers/solid_color_layer_impl.cc @@ -57,9 +57,8 @@ void SolidColorLayerImpl::AppendSolidQuads( return; auto* quad = render_pass->CreateAndAppendDrawQuad<viz::SolidColorDrawQuad>(); - // TODO(crbug/1308932): Remove toSkColor and make all SkColor4f. - quad->SetNew(shared_quad_state, visible_layer_rect, visible_quad_rect, - color.toSkColor(), force_anti_aliasing_off); + quad->SetNew(shared_quad_state, visible_layer_rect, visible_quad_rect, color, + force_anti_aliasing_off); } void SolidColorLayerImpl::AppendQuads(viz::CompositorRenderPass* render_pass, diff --git a/chromium/cc/layers/solid_color_scrollbar_layer_impl.cc b/chromium/cc/layers/solid_color_scrollbar_layer_impl.cc index f1b8b88edf3..7494659d3d9 100644 --- a/chromium/cc/layers/solid_color_scrollbar_layer_impl.cc +++ b/chromium/cc/layers/solid_color_scrollbar_layer_impl.cc @@ -50,9 +50,7 @@ SolidColorScrollbarLayerImpl::SolidColorScrollbarLayerImpl( /*is_overlay*/ true), thumb_thickness_(thumb_thickness), track_start_(track_start), - // TODO(crbug/1308932): Remove FromColor and make all SkColor4f. - color_(SkColor4f::FromColor( - tree_impl->settings().solid_color_scrollbar_color)) {} + color_(tree_impl->settings().solid_color_scrollbar_color) {} void SolidColorScrollbarLayerImpl::PushPropertiesTo(LayerImpl* layer) { ScrollbarLayerImplBase::PushPropertiesTo(layer); @@ -108,9 +106,8 @@ void SolidColorScrollbarLayerImpl::AppendQuads( return; auto* quad = render_pass->CreateAndAppendDrawQuad<viz::SolidColorDrawQuad>(); - // TODO(crbug/1308932): Remove toSkColor and make all SkColor4f. - quad->SetNew(shared_quad_state, thumb_quad_rect, visible_quad_rect, - color_.toSkColor(), false); + quad->SetNew(shared_quad_state, thumb_quad_rect, visible_quad_rect, color_, + false); } const char* SolidColorScrollbarLayerImpl::LayerTypeAsString() const { diff --git a/chromium/cc/layers/surface_layer_impl.cc b/chromium/cc/layers/surface_layer_impl.cc index c3050488efb..6727217d03c 100644 --- a/chromium/cc/layers/surface_layer_impl.cc +++ b/chromium/cc/layers/surface_layer_impl.cc @@ -182,9 +182,8 @@ void SurfaceLayerImpl::AppendQuads(viz::CompositorRenderPass* render_pass, if (surface_range_.IsValid()) { auto* quad = render_pass->CreateAndAppendDrawQuad<viz::SurfaceDrawQuad>(); - // TODO(crbug/1308932): Remove toSkColor and make all SkColor4f. quad->SetNew(shared_quad_state, quad_rect, visible_quad_rect, - surface_range_, background_color().toSkColor(), + surface_range_, background_color(), stretch_content_to_fill_bounds_); quad->is_reflection = is_reflection_; // Add the primary surface ID as a dependency. @@ -200,10 +199,8 @@ void SurfaceLayerImpl::AppendQuads(viz::CompositorRenderPass* render_pass, } else { auto* quad = render_pass->CreateAndAppendDrawQuad<viz::SolidColorDrawQuad>(); - // TODO(crbug/1308932): Remove toSkColor and make all SkColor4f. quad->SetNew(shared_quad_state, quad_rect, visible_quad_rect, - background_color().toSkColor(), - false /* force_anti_aliasing_off */); + background_color(), false /* force_anti_aliasing_off */); } // Unless the client explicitly specifies otherwise, don't block on @@ -222,9 +219,11 @@ gfx::Rect SurfaceLayerImpl::GetEnclosingVisibleRectInTargetSpace() const { void SurfaceLayerImpl::GetDebugBorderProperties(SkColor4f* color, float* width) const { - *color = DebugColors::SurfaceLayerBorderColor(); - *width = DebugColors::SurfaceLayerBorderWidth( - layer_tree_impl() ? layer_tree_impl()->device_scale_factor() : 1); + if (color) + *color = DebugColors::SurfaceLayerBorderColor(); + if (width) + *width = DebugColors::SurfaceLayerBorderWidth( + layer_tree_impl() ? layer_tree_impl()->device_scale_factor() : 1); } void SurfaceLayerImpl::AppendRainbowDebugBorder( @@ -236,17 +235,16 @@ void SurfaceLayerImpl::AppendRainbowDebugBorder( render_pass->CreateAndAppendSharedQuadState(); PopulateSharedQuadState(shared_quad_state, contents_opaque()); - SkColor4f color; - float border_width; - GetDebugBorderProperties(&color, &border_width); - - SkColor colors[] = { - 0x80ff0000, // Red. - 0x80ffa500, // Orange. - 0x80ffff00, // Yellow. - 0x80008000, // Green. - 0x800000ff, // Blue. - 0x80ee82ee, // Violet. + float border_width = DebugColors::SurfaceLayerBorderWidth( + layer_tree_impl() ? layer_tree_impl()->device_scale_factor() : 1); + + SkColor4f colors[] = { + {1.0f, 0.0f, 0.0f, 0.5f}, // Red. + {1.0f, 0.65f, 0.0f, 0.5f}, // Orange. + {1.0f, 1.0f, 0.0f, 0.5f}, // Yellow. + {0.0f, 0.5f, 0.0f, 0.5f}, // Green. + {0.0f, 0.0f, 1.0f, 0.50f}, // Blue. + {0.93f, 0.51f, 0.93f, 0.5f}, // Violet. }; const int kNumColors = std::size(colors); @@ -290,10 +288,8 @@ void SurfaceLayerImpl::AppendRainbowDebugBorder( render_pass->CreateAndAppendDrawQuad<viz::SolidColorDrawQuad>(); // The inner fill is more transparent then the border. static const float kFillOpacity = 0.1f; - SkColor fill_color = SkColorSetA( - colors[i % kNumColors], - static_cast<uint8_t>(SkColorGetA(colors[i % kNumColors]) * - kFillOpacity)); + SkColor4f fill_color = colors[i % kNumColors]; + fill_color.fA *= kFillOpacity; gfx::Rect fill_rect(x, 0, width, bounds().height()); solid_quad->SetNew(shared_quad_state, fill_rect, fill_rect, fill_color, force_anti_aliasing_off); diff --git a/chromium/cc/layers/texture_layer_impl.cc b/chromium/cc/layers/texture_layer_impl.cc index a41414dbaf3..1e60781504a 100644 --- a/chromium/cc/layers/texture_layer_impl.cc +++ b/chromium/cc/layers/texture_layer_impl.cc @@ -147,11 +147,10 @@ void TextureLayerImpl::AppendQuads(viz::CompositorRenderPass* render_pass, return; float vertex_opacity[] = {1.0f, 1.0f, 1.0f, 1.0f}; - // TODO(crbug/1308932): Remove toSkColor and make all SkColor4f. auto* quad = render_pass->CreateAndAppendDrawQuad<viz::TextureDrawQuad>(); quad->SetNew(shared_quad_state, quad_rect, visible_quad_rect, needs_blending, resource_id_, premultiplied_alpha_, uv_top_left_, - uv_bottom_right_, bg_color.toSkColor(), vertex_opacity, flipped_, + uv_bottom_right_, bg_color, vertex_opacity, flipped_, nearest_neighbor_, /*secure_output=*/false, gfx::ProtectedVideoType::kClear); quad->set_resource_size_in_pixels(transferable_resource_.size); diff --git a/chromium/cc/layers/ui_resource_layer_impl.cc b/chromium/cc/layers/ui_resource_layer_impl.cc index 9688bb7a45f..557bf8a0b9c 100644 --- a/chromium/cc/layers/ui_resource_layer_impl.cc +++ b/chromium/cc/layers/ui_resource_layer_impl.cc @@ -130,7 +130,8 @@ void UIResourceLayerImpl::AppendQuads(viz::CompositorRenderPass* render_pass, auto* quad = render_pass->CreateAndAppendDrawQuad<viz::TextureDrawQuad>(); quad->SetNew(shared_quad_state, quad_rect, visible_quad_rect, needs_blending, resource, premultiplied_alpha, uv_top_left_, uv_bottom_right_, - SK_ColorTRANSPARENT, vertex_opacity_, flipped, nearest_neighbor, + SkColors::kTransparent, vertex_opacity_, flipped, + nearest_neighbor, /*secure_output_only=*/false, gfx::ProtectedVideoType::kClear); ValidateQuadResources(quad); } diff --git a/chromium/cc/layers/ui_resource_layer_unittest.cc b/chromium/cc/layers/ui_resource_layer_unittest.cc index 88569c18c47..bb9f09ecad1 100644 --- a/chromium/cc/layers/ui_resource_layer_unittest.cc +++ b/chromium/cc/layers/ui_resource_layer_unittest.cc @@ -7,6 +7,7 @@ #include "base/threading/thread_task_runner_handle.h" #include "cc/animation/animation_host.h" #include "cc/resources/scoped_ui_resource.h" +#include "cc/resources/ui_resource_manager.h" #include "cc/test/fake_layer_tree_host.h" #include "cc/test/layer_tree_impl_test_base.h" #include "cc/test/stub_layer_tree_host_single_thread_client.h" @@ -192,5 +193,37 @@ TEST_F(UIResourceLayerTest, SharedBitmap) { EXPECT_EQ(other_lth_resource_id, layer1->resource_id()); } +TEST_F(UIResourceLayerTest, SharedBitmapCacheSizeLimit) { + scoped_refptr<TestUIResourceLayer> layer = TestUIResourceLayer::Create(); + layer_tree_host()->SetRootLayer(layer); + + // Number of bitmaps that are created then get their references dropped. + constexpr size_t kDroppedResources = 100u; + + // Populate the shared bitmap cache. + auto* manager = layer_tree_host()->GetUIResourceManager(); + while (manager->owned_shared_resources_size_for_test() < kDroppedResources) { + SkBitmap bitmap; + bitmap.allocN32Pixels(10, 10); + bitmap.setImmutable(); + layer->SetBitmap(bitmap); + } + + // No eviction because bitmaps are references by UIResourcesRequests. + EXPECT_EQ(manager->owned_shared_resources_size_for_test(), kDroppedResources); + + // Pretend UIResourcesRequests are processed to drop bitmap references. + manager->TakeUIResourcesRequests(); + + // Create one more shared bitmap resource and the eviction happens. + SkBitmap bitmap; + bitmap.allocN32Pixels(10, 10); + bitmap.setImmutable(); + layer->SetBitmap(bitmap); + + // The cache should trimmed down. + EXPECT_EQ(manager->owned_shared_resources_size_for_test(), 1u); +} + } // namespace } // namespace cc diff --git a/chromium/cc/layers/video_layer_impl.cc b/chromium/cc/layers/video_layer_impl.cc index 91872c8cee7..c0b9ce25c85 100644 --- a/chromium/cc/layers/video_layer_impl.cc +++ b/chromium/cc/layers/video_layer_impl.cc @@ -19,7 +19,6 @@ #include "cc/trees/occlusion.h" #include "cc/trees/task_runner_provider.h" #include "components/viz/client/client_resource_provider.h" -#include "components/viz/common/quads/stream_video_draw_quad.h" #include "components/viz/common/quads/texture_draw_quad.h" #include "components/viz/common/quads/yuv_video_draw_quad.h" #include "media/base/video_frame.h" diff --git a/chromium/cc/layers/video_layer_impl_unittest.cc b/chromium/cc/layers/video_layer_impl_unittest.cc index 6a205ea443b..dd0e14e4db0 100644 --- a/chromium/cc/layers/video_layer_impl_unittest.cc +++ b/chromium/cc/layers/video_layer_impl_unittest.cc @@ -13,7 +13,6 @@ #include "cc/trees/single_thread_proxy.h" #include "components/viz/common/gpu/context_provider.h" #include "components/viz/common/quads/draw_quad.h" -#include "components/viz/common/quads/stream_video_draw_quad.h" #include "components/viz/common/quads/texture_draw_quad.h" #include "components/viz/common/quads/yuv_video_draw_quad.h" #include "components/viz/service/display/output_surface.h" diff --git a/chromium/cc/metrics/compositor_frame_reporter.cc b/chromium/cc/metrics/compositor_frame_reporter.cc index fb5bdb10ff0..f1545f64b96 100644 --- a/chromium/cc/metrics/compositor_frame_reporter.cc +++ b/chromium/cc/metrics/compositor_frame_reporter.cc @@ -92,6 +92,26 @@ constexpr int kMaxEventLatencyHistogramIndex = constexpr base::TimeDelta kEventLatencyHistogramMin = base::Microseconds(1); constexpr base::TimeDelta kEventLatencyHistogramMax = base::Seconds(5); constexpr int kEventLatencyHistogramBucketCount = 100; +constexpr base::TimeDelta kHighLatencyMin = base::Milliseconds(75); + +// Number of breakdown stages of the current PipelineReporter +constexpr int kNumOfCompositorStages = + static_cast<int>(StageType::kStageTypeCount) - 1; +// Number of breakdown stages of the blink +constexpr int kNumOfBlinkStages = + static_cast<int>(BlinkBreakdown::kBreakdownCount); +// Number of breakdown stages of the viz +constexpr int kNumOfVizStages = static_cast<int>(VizBreakdown::kBreakdownCount); +// Number of dispatch stages of the current EventLatency +constexpr int kNumDispatchStages = + static_cast<int>(EventMetrics::DispatchStage::kMaxValue); +// Stores the weight of the most recent data point used in percentage when +// predicting substages' latency. (It is stored and calculated in percentage +// since TimeDelta calculate based on microseconds instead of nanoseconds, +// therefore, decimals of stage durations in microseconds may be lost.) +constexpr double kWeightOfCurStageInPercent = 25; +// Used for comparing doubles +constexpr double kEpsilon = 0.001; std::string GetCompositorLatencyHistogramName( FrameReportType report_type, @@ -116,12 +136,11 @@ std::string GetEventLatencyHistogramBaseName( const EventMetrics& event_metrics) { auto* scroll_metrics = event_metrics.AsScroll(); auto* pinch_metrics = event_metrics.AsPinch(); - return base::StrCat( - {"EventLatency.", event_metrics.GetTypeName(), - scroll_metrics || pinch_metrics ? "." : "", - scroll_metrics - ? scroll_metrics->GetScrollTypeName() - : pinch_metrics ? pinch_metrics->GetPinchTypeName() : ""}); + return base::StrCat({"EventLatency.", event_metrics.GetTypeName(), + scroll_metrics || pinch_metrics ? "." : "", + scroll_metrics ? scroll_metrics->GetScrollTypeName() + : pinch_metrics ? pinch_metrics->GetPinchTypeName() + : ""}); } constexpr char kTraceCategory[] = @@ -131,6 +150,40 @@ base::TimeTicks ComputeSafeDeadlineForFrame(const viz::BeginFrameArgs& args) { return args.frame_time + (args.interval * 1.5); } +// Returns the value of the exponentially weighted average for +// SetEventLatencyPredictions. +base::TimeDelta CalculateWeightedAverage(base::TimeDelta previous_value, + base::TimeDelta current_value) { + if (previous_value.is_negative()) + return current_value; + return (kWeightOfCurStageInPercent * current_value + + (100 - kWeightOfCurStageInPercent) * previous_value) / + 100; +} + +// Calculate new prediction of latency based on old prediction and current +// latency +base::TimeDelta PredictLatency(base::TimeDelta previous_prediction, + base::TimeDelta current_latency) { + return (kWeightOfCurStageInPercent * current_latency + + (100 - kWeightOfCurStageInPercent) * previous_prediction) / + 100; +} + +double DetermineHighestContribution( + double contribution_change, + double highest_contribution_change, + const std::string& stage_name, + std::vector<std::string>& high_latency_stages) { + if (std::abs(contribution_change - highest_contribution_change) < kEpsilon) { + high_latency_stages.push_back(stage_name); + } else if (contribution_change > highest_contribution_change) { + highest_contribution_change = contribution_change; + high_latency_stages = {stage_name}; + } + return highest_contribution_change; +} + } // namespace // CompositorFrameReporter::ProcessedBlinkBreakdown::Iterator ================== @@ -318,6 +371,21 @@ CompositorFrameReporter::ProcessedVizBreakdown::CreateIterator( buffer_ready_available_); } +// CompositorFrameReporter::CompositorLatencyInfo ============================== + +CompositorFrameReporter::CompositorLatencyInfo::CompositorLatencyInfo() = + default; +CompositorFrameReporter::CompositorLatencyInfo::CompositorLatencyInfo( + base::TimeDelta init_value) + : top_level_stages(kNumOfCompositorStages, init_value), + blink_breakdown_stages(kNumOfBlinkStages, init_value), + viz_breakdown_stages(kNumOfVizStages, init_value), + total_latency(init_value), + total_blink_latency(init_value), + total_viz_latency(init_value) {} +CompositorFrameReporter::CompositorLatencyInfo::~CompositorLatencyInfo() = + default; + // CompositorFrameReporter ===================================================== CompositorFrameReporter::CompositorFrameReporter( @@ -357,6 +425,8 @@ CompositorFrameReporter::CompositorFrameReporter( FrameSequenceTrackerType::kSETMainThreadAnimation)) || active_trackers_.test(static_cast<size_t>( FrameSequenceTrackerType::kMainThreadAnimation))); + is_forked_ = false; + is_backfill_ = false; } // static @@ -508,6 +578,7 @@ CompositorFrameReporter::CopyReporterAtBeginImplStage() { StageType::kBeginImplFrameToSendBeginMainFrame; new_reporter->current_stage_.start_time = stage_history_.front().start_time; new_reporter->set_tick_clock(tick_clock_); + new_reporter->set_is_forked(true); // Set up the new reporter so that it depends on |this| for partial update // information. @@ -528,6 +599,16 @@ CompositorFrameReporter::StageData::StageData(StageType stage_type, CompositorFrameReporter::StageData::StageData(const StageData&) = default; CompositorFrameReporter::StageData::~StageData() = default; +CompositorFrameReporter::EventLatencyInfo::EventLatencyInfo( + const int num_dispatch_stages, + const int num_compositor_stages) + : dispatch_durations(num_dispatch_stages, base::Microseconds(-1)), + transition_duration(base::Microseconds(-1)), + compositor_durations(num_compositor_stages, base::Microseconds(-1)), + total_duration(base::Microseconds(-1)), + transition_name("") {} +CompositorFrameReporter::EventLatencyInfo::~EventLatencyInfo() = default; + void CompositorFrameReporter::StartStage( CompositorFrameReporter::StageType stage_type, base::TimeTicks start_time) { @@ -639,10 +720,12 @@ void CompositorFrameReporter::TerminateReporter() { if (frame_termination_status_ == FrameTerminationStatus::kUnknown) TerminateFrame(FrameTerminationStatus::kUnknown, Now()); - 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_); + if (!processed_blink_breakdown_) + processed_blink_breakdown_ = std::make_unique<ProcessedBlinkBreakdown>( + blink_start_time_, begin_main_frame_start_, blink_breakdown_); + if (!processed_viz_breakdown_) + processed_viz_breakdown_ = std::make_unique<ProcessedVizBreakdown>( + viz_start_time_, viz_breakdown_); DCHECK_EQ(current_stage_.start_time, base::TimeTicks()); const FrameInfo frame_info = GenerateFrameInfo(); @@ -890,9 +973,9 @@ void CompositorFrameReporter::ReportCompositorLatencyHistogram( const int stage_type_index = blink_breakdown ? kBlinkBreakdownInitialIndex + static_cast<int>(*blink_breakdown) - : viz_breakdown - ? kVizBreakdownInitialIndex + static_cast<int>(*viz_breakdown) - : static_cast<int>(stage_type); + : viz_breakdown + ? kVizBreakdownInitialIndex + static_cast<int>(*viz_breakdown) + : static_cast<int>(stage_type); const int histogram_index = (stage_type_index == static_cast<int>(StageType::kTotalLatency) ? kStagesWithBreakdownCount + frame_sequence_tracker_type_index @@ -1075,6 +1158,18 @@ void CompositorFrameReporter::ReportCompositorLatencyTraceEvents( has_smooth_input_main |= event_metrics->HasSmoothInputEvent(); } reporter->set_has_smooth_input_main(has_smooth_input_main); + reporter->set_has_high_latency( + (frame_termination_time_ - args_.frame_time) > kHighLatencyMin); + + if (is_forked_) { + reporter->set_frame_type(ChromeFrameReporter::FORKED); + } else if (is_backfill_) { + reporter->set_frame_type(ChromeFrameReporter::BACKFILL); + } + + for (auto stage : high_latency_substages_) { + reporter->add_high_latency_contribution_stage(stage); + } // TODO(crbug.com/1086974): Set 'drop reason' if applicable. }); @@ -1205,6 +1300,279 @@ void CompositorFrameReporter::AdoptReporter( DiscardOldPartialUpdateReporters(); } +void CompositorFrameReporter::CalculateCompositorLatencyPrediction( + CompositorLatencyInfo& previous_predictions, + base::TimeDelta prediction_deviation_threshold) { + // `stage_history_` should not be empty since we are calling this function + // from DidPresentCompositorFrame(), which means there has to be some sort of + // stage data. + DCHECK(!stage_history_.empty()); + + // If the bad case of having `total_latency` of `previous_predictions` happens + // then it would mess up the prediction calculation, therefore, we want to + // reset the prediction by setting everything back to -1. + if (previous_predictions.total_latency.is_zero()) + previous_predictions = CompositorLatencyInfo(base::Microseconds(-1)); + + base::TimeDelta total_pipeline_latency = + stage_history_.back().end_time - stage_history_[0].start_time; + + // Do not record current breakdown stages' duration if the total latency of + // the current PipelineReporter is 0s. And no further predictions could be + // made in this case. + if (total_pipeline_latency.is_zero()) + return; + + 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_); + + // Note that `current_stage_durations` would always have the same length as + // `previous_predictions`, since each index represent the breakdown stages of + // the PipelineReporter listed at enum class, StageType. + CompositorLatencyInfo current_stage_durations(base::Microseconds(0)); + current_stage_durations.total_latency = total_pipeline_latency; + + for (auto stage : stage_history_) { + if (stage.stage_type == StageType::kTotalLatency) + continue; + base::TimeDelta substageLatency = stage.end_time - stage.start_time; + current_stage_durations + .top_level_stages[static_cast<int>(stage.stage_type)] = substageLatency; + } + + for (auto it = processed_blink_breakdown_->CreateIterator(); it.IsValid(); + it.Advance()) { + current_stage_durations + .blink_breakdown_stages[static_cast<int>(it.GetBreakdown())] = + it.GetLatency(); + current_stage_durations.total_blink_latency += it.GetLatency(); + } + + for (auto it = processed_viz_breakdown_->CreateIterator(true); it.IsValid(); + it.Advance()) { + current_stage_durations + .viz_breakdown_stages[static_cast<int>(it.GetBreakdown())] = + it.GetDuration(); + current_stage_durations.total_viz_latency += it.GetDuration(); + } + + // Do not record current pipeline details or update predictions if no frame + // is submitted. + if (current_stage_durations + .top_level_stages[static_cast<int>( + StageType::kSubmitCompositorFrameToPresentationCompositorFrame)] + .is_zero()) + return; + + // The previous prediction is initialized to be -1, so check if the current + // PipelineReporter is the first reporter ever to be calculated. + if (previous_predictions.total_latency == base::Microseconds(-1)) { + previous_predictions = current_stage_durations; + } else { + if ((current_stage_durations.total_latency - + previous_predictions.total_latency) >= prediction_deviation_threshold) + FindHighLatencyAttribution(previous_predictions, current_stage_durations); + + for (int i = 0; i < kNumOfCompositorStages; i++) { + previous_predictions.top_level_stages[i] = + PredictLatency(previous_predictions.top_level_stages[i], + current_stage_durations.top_level_stages[i]); + } + previous_predictions.total_latency = + PredictLatency(previous_predictions.total_latency, + current_stage_durations.total_latency); + + if (!current_stage_durations.total_blink_latency.is_zero()) { + for (int i = 0; i < kNumOfBlinkStages; i++) { + previous_predictions.blink_breakdown_stages[i] = + previous_predictions.total_blink_latency.is_zero() + ? current_stage_durations.blink_breakdown_stages[i] + : PredictLatency( + previous_predictions.blink_breakdown_stages[i], + current_stage_durations.blink_breakdown_stages[i]); + } + previous_predictions.total_blink_latency = + previous_predictions.total_blink_latency.is_zero() + ? current_stage_durations.total_blink_latency + : PredictLatency(previous_predictions.total_blink_latency, + current_stage_durations.total_blink_latency); + } + + // TODO(crbug.com/1349930): implement check that ensure the prediction is + // correct by checking if platform supports breakdown of the stage + // SubmitCompositorFrameToPresentationCompositorFrame.SwapStartToSwapEnd, + // then SwapStartToSwapEnd should always be 0s and data for breakdown of it + // should always be available. (See enum class `VizBreakdown` for stage + // details.) + if (!current_stage_durations.total_viz_latency.is_zero()) { + for (int i = 0; i < kNumOfVizStages; i++) { + previous_predictions.viz_breakdown_stages[i] = + previous_predictions.total_viz_latency.is_zero() + ? current_stage_durations.viz_breakdown_stages[i] + : PredictLatency( + previous_predictions.viz_breakdown_stages[i], + current_stage_durations.viz_breakdown_stages[i]); + } + previous_predictions.total_viz_latency = + previous_predictions.total_viz_latency.is_zero() + ? current_stage_durations.total_viz_latency + : PredictLatency(previous_predictions.total_viz_latency, + current_stage_durations.total_viz_latency); + } + } +} + +void CompositorFrameReporter::CalculateEventLatencyPrediction( + CompositorFrameReporter::EventLatencyInfo& predicted_event_latency, + base::TimeDelta prediction_deviation_threshold) { + if (events_metrics_.empty()) + return; + + // TODO(crbug.com/1334827): Explore calculating predictions for multiple + // events. Currently only kGestureScrollUpdate event predictions + // are being calculated, consider including other stages in future changes. + auto event_it = std::find_if( + events_metrics_.begin(), events_metrics_.end(), + [](const std::unique_ptr<EventMetrics>& event) { + return event && + event->type() == EventMetrics::EventType::kGestureScrollUpdate; + }); + if (event_it == events_metrics_.end()) + return; + auto& event_metrics = *event_it; + + base::TimeTicks dispatch_start_time = + event_metrics->GetDispatchStageTimestamp( + EventMetrics::DispatchStage::kGenerated); + + // Determine the last valid stage in case kRendererMainFinished or + // kRendererCompositorFinished stages do not exist, otherwise there is not + // enough information for the prediction. + EventMetrics::DispatchStage last_valid_stage = + EventMetrics::DispatchStage::kGenerated; + if (event_metrics->GetDispatchStageTimestamp( + EventMetrics::DispatchStage::kRendererMainFinished) > + dispatch_start_time) { + last_valid_stage = EventMetrics::DispatchStage::kRendererMainFinished; + } else if (event_metrics->GetDispatchStageTimestamp( + EventMetrics::DispatchStage::kRendererCompositorFinished) > + dispatch_start_time) { + last_valid_stage = EventMetrics::DispatchStage::kRendererCompositorFinished; + } + + base::TimeTicks dispatch_end_time = + event_metrics->GetDispatchStageTimestamp(last_valid_stage); + base::TimeDelta total_dispatch_duration = + dispatch_end_time - dispatch_start_time; + if (total_dispatch_duration.is_negative()) + return; + + CompositorFrameReporter::EventLatencyInfo actual_event_latency( + kNumDispatchStages, kNumOfCompositorStages); + actual_event_latency.total_duration = base::Microseconds(0); + + // Determine dispatch stage durations. + base::TimeTicks previous_timetick = dispatch_start_time; + for (auto stage = EventMetrics::DispatchStage::kGenerated; + stage <= last_valid_stage; + stage = static_cast<EventMetrics::DispatchStage>( + static_cast<int>(stage) + 1)) { + if (stage != EventMetrics::DispatchStage::kGenerated) { + base::TimeTicks current_timetick = + event_metrics->GetDispatchStageTimestamp(stage); + // Only update stage if the current_timetick is later than the + // previous_timetick. + if (current_timetick > previous_timetick) { + base::TimeDelta stage_duration = current_timetick - previous_timetick; + actual_event_latency.dispatch_durations[static_cast<int>(stage) - 1] = + stage_duration; + actual_event_latency.total_duration += stage_duration; + previous_timetick = current_timetick; + } + } + } + + // Determine dispatch-to-compositor transition stage duration. + auto stage_it = std::find_if( + stage_history_.begin(), stage_history_.end(), + [dispatch_end_time](const CompositorFrameReporter::StageData& stage) { + return stage.start_time >= dispatch_end_time; + }); + if (stage_it != stage_history_.end()) { + if (dispatch_end_time < stage_it->start_time) { + base::TimeDelta stage_duration = stage_it->start_time - dispatch_end_time; + actual_event_latency.transition_duration = stage_duration; + actual_event_latency.total_duration += stage_duration; + actual_event_latency.transition_name = + EventLatencyTracingRecorder::GetDispatchToCompositorBreakdownName( + last_valid_stage, stage_it->stage_type); + } + } + + // Determine compositor stage durations, which start from stage_it for + // EventLatency. + for (auto stage = stage_it; stage != stage_history_.end(); stage++) { + base::TimeDelta stage_duration = stage->end_time - stage->start_time; + if (stage_duration.is_positive()) { + actual_event_latency + .compositor_durations[static_cast<int>(stage->stage_type)] = + stage_duration; + actual_event_latency.total_duration += stage_duration; + } + } + + // High latency attribution. + if (predicted_event_latency.total_duration.is_positive() && + actual_event_latency.total_duration - + predicted_event_latency.total_duration >= + prediction_deviation_threshold) { + FindEventLatencyAttribution(event_metrics.get(), predicted_event_latency, + actual_event_latency); + } + + // Calculate new dispatch stage predictions. + base::TimeDelta predicted_total_duration = base::Microseconds(0); + for (int i = 0; i < kNumDispatchStages; i++) { + if (actual_event_latency.dispatch_durations[i].is_positive()) { + predicted_event_latency.dispatch_durations[i] = CalculateWeightedAverage( + predicted_event_latency.dispatch_durations[i], + actual_event_latency.dispatch_durations[i]); + } + if (predicted_event_latency.dispatch_durations[i].is_positive()) { + predicted_total_duration += predicted_event_latency.dispatch_durations[i]; + } + } + + // Calculate new dispatch-to-compositor transition stage predictions. + if (actual_event_latency.transition_duration.is_positive()) { + predicted_event_latency.transition_duration = + CalculateWeightedAverage(predicted_event_latency.transition_duration, + actual_event_latency.transition_duration); + if (predicted_event_latency.transition_duration.is_positive()) + predicted_total_duration += predicted_event_latency.transition_duration; + } + + // Calculate new compositor stage predictions. + // TODO(crbug.com/1334827): Explore using existing PipelineReporter + // predictions for the compositor stage. + for (int i = 0; i < kNumOfCompositorStages; i++) { + if (actual_event_latency.compositor_durations[i].is_positive()) { + predicted_event_latency.compositor_durations[i] = + CalculateWeightedAverage( + predicted_event_latency.compositor_durations[i], + actual_event_latency.compositor_durations[i]); + } + if (predicted_event_latency.compositor_durations[i].is_positive()) { + predicted_total_duration += + predicted_event_latency.compositor_durations[i]; + } + } + + predicted_event_latency.total_duration = predicted_total_duration; +} + void CompositorFrameReporter::SetPartialUpdateDecider( CompositorFrameReporter* decider) { DCHECK(decider); @@ -1345,4 +1713,182 @@ FrameInfo CompositorFrameReporter::GenerateFrameInfo() const { return info; } +void CompositorFrameReporter::FindHighLatencyAttribution( + CompositorLatencyInfo& previous_predictions, + CompositorLatencyInfo& current_stage_durations) { + if (previous_predictions.total_latency.is_zero() || + current_stage_durations.total_latency.is_zero()) + return; + + double contribution_change = -1; + double highest_contribution_change = -1; + std::vector<int> highest_contribution_change_index; + std::vector<int> highest_blink_contribution_change_index; + std::vector<int> highest_viz_contribution_change_index; + + for (int i = 0; i < kNumOfCompositorStages; i++) { + switch (i) { + case static_cast<int>(StageType::kSendBeginMainFrameToCommit): + if (current_stage_durations.top_level_stages[i].is_zero() || + previous_predictions.total_blink_latency.is_zero() || + current_stage_durations.total_blink_latency.is_zero()) + continue; + + for (int j = 0; j < kNumOfBlinkStages; j++) { + contribution_change = + (current_stage_durations.blink_breakdown_stages[j] / + current_stage_durations.total_latency) - + (previous_predictions.blink_breakdown_stages[j] / + previous_predictions.total_latency); + + if (contribution_change > highest_contribution_change) { + highest_contribution_change = contribution_change; + highest_contribution_change_index.clear(); + highest_viz_contribution_change_index.clear(); + highest_blink_contribution_change_index = {j}; + } else if (std::abs(contribution_change - + highest_contribution_change) < kEpsilon) { + highest_blink_contribution_change_index.push_back(j); + } + } + break; + + case static_cast<int>( + StageType::kSubmitCompositorFrameToPresentationCompositorFrame): + if (current_stage_durations.top_level_stages[i].is_zero() || + previous_predictions.total_viz_latency.is_zero() || + current_stage_durations.total_viz_latency.is_zero()) + continue; + + for (int j = 0; j < kNumOfVizStages; j++) { + contribution_change = + (current_stage_durations.viz_breakdown_stages[j] / + current_stage_durations.total_latency) - + (previous_predictions.viz_breakdown_stages[j] / + previous_predictions.total_latency); + + if (contribution_change > highest_contribution_change) { + highest_contribution_change = contribution_change; + highest_contribution_change_index.clear(); + highest_blink_contribution_change_index.clear(); + highest_viz_contribution_change_index = {j}; + } else if (std::abs(contribution_change - + highest_contribution_change) < kEpsilon) { + highest_viz_contribution_change_index.push_back(j); + } + } + break; + + default: + contribution_change = (current_stage_durations.top_level_stages[i] / + current_stage_durations.total_latency) - + (previous_predictions.top_level_stages[i] / + previous_predictions.total_latency); + + if (contribution_change > highest_contribution_change) { + highest_contribution_change = contribution_change; + highest_blink_contribution_change_index.clear(); + highest_viz_contribution_change_index.clear(); + highest_contribution_change_index = {i}; + } else if (std::abs(contribution_change - highest_contribution_change) < + kEpsilon) { + highest_contribution_change_index.push_back(i); + } + break; + } + } + + // It is not expensive to go through vector of indexes again since it is + // usually very small (possibilities of breakdown stages having the same + // change contribution is small). + for (auto index : highest_contribution_change_index) { + high_latency_substages_.push_back( + GetStageName(static_cast<StageType>(index))); + } + for (auto index : highest_blink_contribution_change_index) { + high_latency_substages_.push_back( + GetStageName(StageType::kSendBeginMainFrameToCommit, absl::nullopt, + static_cast<BlinkBreakdown>(index))); + } + for (auto index : highest_viz_contribution_change_index) { + high_latency_substages_.push_back(GetStageName( + StageType::kSubmitCompositorFrameToPresentationCompositorFrame, + static_cast<VizBreakdown>(index))); + } +} + +void CompositorFrameReporter::FindEventLatencyAttribution( + EventMetrics* event_metrics, + CompositorFrameReporter::EventLatencyInfo& predicted_event_latency, + CompositorFrameReporter::EventLatencyInfo& actual_event_latency) { + if (!event_metrics) + return; + + std::vector<std::string> high_latency_stages; + double contribution_change = -1; + double highest_contribution_change = -1; + + // Check dispatch stage change + 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()); + 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; + + contribution_change = + (actual_event_latency + .dispatch_durations[static_cast<int>(end_stage) - 1] / + actual_event_latency.total_duration) - + (predicted_event_latency + .dispatch_durations[static_cast<int>(end_stage) - 1] / + predicted_event_latency.total_duration); + std::string dispatch_stage_name = + EventLatencyTracingRecorder::GetDispatchBreakdownName(dispatch_stage, + end_stage); + highest_contribution_change = DetermineHighestContribution( + contribution_change, highest_contribution_change, dispatch_stage_name, + high_latency_stages); + + dispatch_stage = end_stage; + dispatch_timestamp = end_timestamp; + } + + // Check dispatch-to-compositor stage change + contribution_change = (actual_event_latency.transition_duration / + actual_event_latency.total_duration) - + (predicted_event_latency.transition_duration / + predicted_event_latency.total_duration); + highest_contribution_change = DetermineHighestContribution( + contribution_change, highest_contribution_change, + actual_event_latency.transition_name, high_latency_stages); + + // Check compositor stage change + for (int i = 0; i < kNumOfCompositorStages; i++) { + contribution_change = (actual_event_latency.compositor_durations[i] / + actual_event_latency.total_duration) - + (predicted_event_latency.compositor_durations[i] / + predicted_event_latency.total_duration); + highest_contribution_change = DetermineHighestContribution( + contribution_change, highest_contribution_change, + GetStageName(static_cast<StageType>(i)), high_latency_stages); + } + + for (auto stage : high_latency_stages) { + event_metrics->SetHighLatencyStage(stage); + } +} + } // namespace cc diff --git a/chromium/cc/metrics/compositor_frame_reporter.h b/chromium/cc/metrics/compositor_frame_reporter.h index ad0b1084a34..c04a7cff6cc 100644 --- a/chromium/cc/metrics/compositor_frame_reporter.h +++ b/chromium/cc/metrics/compositor_frame_reporter.h @@ -133,6 +133,9 @@ class CC_EXPORT CompositorFrameReporter { kBreakdownCount }; + // To distinguish between impl and main reporter + enum class ReporterType { kImpl = 0, kMain = 1 }; + struct CC_EXPORT StageData { StageType stage_type; base::TimeTicks start_time; @@ -145,6 +148,18 @@ class CC_EXPORT CompositorFrameReporter { ~StageData(); }; + struct CC_EXPORT EventLatencyInfo { + std::vector<base::TimeDelta> dispatch_durations; + base::TimeDelta transition_duration; + std::vector<base::TimeDelta> compositor_durations; + base::TimeDelta total_duration; + std::string transition_name; + EventLatencyInfo(const int num_dispatch_stages, + const int num_compositor_stages); + EventLatencyInfo(const EventLatencyInfo&); + ~EventLatencyInfo(); + }; + using SmoothThread = FrameInfo::SmoothThread; // Holds a processed list of Blink breakdowns with an `Iterator` class to @@ -229,6 +244,21 @@ class CC_EXPORT CompositorFrameReporter { base::TimeTicks swap_start_; }; + // Wrapper for all level of breakdown stages' prediction + struct CC_EXPORT CompositorLatencyInfo { + CompositorLatencyInfo(); + explicit CompositorLatencyInfo(base::TimeDelta init_value); + ~CompositorLatencyInfo(); + + std::vector<base::TimeDelta> top_level_stages; + std::vector<base::TimeDelta> blink_breakdown_stages; + std::vector<base::TimeDelta> viz_breakdown_stages; + + base::TimeDelta total_latency; + base::TimeDelta total_blink_latency; + base::TimeDelta total_viz_latency; + }; + CompositorFrameReporter(const ActiveTrackers& active_trackers, const viz::BeginFrameArgs& args, bool should_report_histograms, @@ -330,6 +360,9 @@ class CC_EXPORT CompositorFrameReporter { is_accompanied_by_main_thread_update; } + void set_is_forked(bool is_forked) { is_forked_ = is_forked; } + void set_is_backfill(bool is_backfill) { is_backfill_ = is_backfill; } + const viz::BeginFrameId& frame_id() const { return args_.frame_id; } // Adopts |cloned_reporter|, i.e. keeps |cloned_reporter| alive until after @@ -346,6 +379,36 @@ class CC_EXPORT CompositorFrameReporter { using FrameReportTypes = std::bitset<static_cast<size_t>(FrameReportType::kMaxValue) + 1>; + // This function is called to calculate breakdown stage duration's prediction + // based on the `previous_predictions` and update the `previous_predictions` + // to the new prediction calculated. + void CalculateCompositorLatencyPrediction( + CompositorLatencyInfo& previous_predictions, + base::TimeDelta prediction_deviation_threshold); + + // Sets EventLatency stage duration predictions based on previous trace + // durations using exponentially weighted averages. + void CalculateEventLatencyPrediction( + CompositorFrameReporter::EventLatencyInfo& predicted_event_latency, + base::TimeDelta prediction_deviation_threshold); + + ReporterType get_reporter_type() { return reporter_type_; } + + void set_reporter_type_to_impl() { reporter_type_ = ReporterType::kImpl; } + void set_reporter_type_to_main() { reporter_type_ = ReporterType::kMain; } + + const std::vector<std::string>& high_latency_substages_for_testing() { + return high_latency_substages_; + } + + void ClearHighLatencySubstagesForTesting() { + high_latency_substages_.clear(); + } + + std::vector<std::unique_ptr<EventMetrics>>& events_metrics_for_testing() { + return events_metrics_; + } + protected: void set_has_partial_update(bool has_partial_update) { has_partial_update_ = has_partial_update; @@ -398,6 +461,15 @@ class CC_EXPORT CompositorFrameReporter { // updates (see comments on EventMetrics::requires_main_thread_update_). EventMetrics::List TakeMainBlockedEventsMetrics(); + void FindHighLatencyAttribution( + CompositorLatencyInfo& previous_predictions, + CompositorLatencyInfo& current_stage_durations); + + void FindEventLatencyAttribution( + EventMetrics* event_metrics, + CompositorFrameReporter::EventLatencyInfo& predicted_event_latency, + CompositorFrameReporter::EventLatencyInfo& actual_event_latency); + // Whether UMA histograms should be reported or not. const bool should_report_histograms_; @@ -458,6 +530,14 @@ class CC_EXPORT CompositorFrameReporter { // with checkerboarding). bool has_missing_content_ = false; + // Indicates whether the frame is forked (i.e. a PipelineReporter event starts + // at the same frame sequence as another PipelineReporter). + bool is_forked_ = false; + + // Indicates whether the frame is backfill (i.e. dropped frames when there are + // no partial compositor updates). + bool is_backfill_ = false; + // 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 @@ -479,6 +559,10 @@ class CC_EXPORT CompositorFrameReporter { const GlobalMetricsTrackers global_trackers_; + std::vector<std::string> high_latency_substages_; + + ReporterType reporter_type_; + 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 014a1a4a3f3..105435c5696 100644 --- a/chromium/cc/metrics/compositor_frame_reporter_unittest.cc +++ b/chromium/cc/metrics/compositor_frame_reporter_unittest.cc @@ -32,12 +32,12 @@ using ::testing::NotNull; class CompositorFrameReporterTest : public testing::Test { public: CompositorFrameReporterTest() : pipeline_reporter_(CreatePipelineReporter()) { - AdvanceNowByMs(1); + AdvanceNowByUs(1); dropped_frame_counter_.set_total_counter(&total_frame_counter_); } protected: - base::TimeTicks AdvanceNowByMs(int advance_ms) { + base::TimeTicks AdvanceNowByUs(int advance_ms) { test_tick_clock_.Advance(base::Microseconds(advance_ms)); return test_tick_clock_.NowTicks(); } @@ -57,44 +57,86 @@ class CompositorFrameReporterTest : public testing::Test { breakdown->update_layers = base::Microseconds(1); // Advance now by the sum of the breakdowns. - AdvanceNowByMs(10 + 9 + 8 + 7 + 6 + 5 + 3 + 2 + 1); + AdvanceNowByUs(10 + 9 + 8 + 7 + 6 + 5 + 3 + 2 + 1); return breakdown; } viz::FrameTimingDetails BuildVizBreakdown() { viz::FrameTimingDetails viz_breakdown; - viz_breakdown.received_compositor_frame_timestamp = AdvanceNowByMs(1); - viz_breakdown.draw_start_timestamp = AdvanceNowByMs(2); - viz_breakdown.swap_timings.swap_start = AdvanceNowByMs(3); - viz_breakdown.swap_timings.swap_end = AdvanceNowByMs(4); - viz_breakdown.presentation_feedback.timestamp = AdvanceNowByMs(5); + viz_breakdown.received_compositor_frame_timestamp = AdvanceNowByUs(1); + viz_breakdown.draw_start_timestamp = AdvanceNowByUs(2); + viz_breakdown.swap_timings.swap_start = AdvanceNowByUs(3); + viz_breakdown.swap_timings.swap_end = AdvanceNowByUs(4); + viz_breakdown.presentation_feedback.timestamp = AdvanceNowByUs(5); return viz_breakdown; } std::unique_ptr<EventMetrics> SetupEventMetrics( std::unique_ptr<EventMetrics> metrics) { if (metrics) { - AdvanceNowByMs(3); + AdvanceNowByUs(3); metrics->SetDispatchStageTimestamp( EventMetrics::DispatchStage::kRendererCompositorStarted); - AdvanceNowByMs(3); + AdvanceNowByUs(3); metrics->SetDispatchStageTimestamp( EventMetrics::DispatchStage::kRendererCompositorFinished); } return metrics; } + // Sets up the dispatch durations of each EventMetrics according to + // stage_durations. Stages with a duration of -1 will be skipped. + std::unique_ptr<EventMetrics> SetupEventMetricsWithDispatchTimes( + std::unique_ptr<EventMetrics> metrics, + const std::vector<int>& stage_durations) { + if (metrics) { + int num_stages = stage_durations.size(); + // Start indexing from 1 because the 0th index held duration from + // kGenerated to kArrivedInRendererCompositor, which was already used in + // when the EventMetrics was created. + for (int i = 1; i < num_stages; i++) { + if (stage_durations[i] >= 0) { + AdvanceNowByUs(stage_durations[i]); + metrics->SetDispatchStageTimestamp( + EventMetrics::DispatchStage(i + 1)); + } + } + } + return metrics; + } + std::unique_ptr<EventMetrics> CreateEventMetrics(ui::EventType type) { - const base::TimeTicks event_time = AdvanceNowByMs(3); - AdvanceNowByMs(3); + const base::TimeTicks event_time = AdvanceNowByUs(3); + AdvanceNowByUs(3); return SetupEventMetrics( EventMetrics::CreateForTesting(type, event_time, &test_tick_clock_)); } + // Creates EventMetrics with elements in stage_durations representing each + // dispatch stage's desired duration respectively, with the 0th index + // representing the duration from kGenerated to kArrivedInRendererCompositor. + // stage_durations must have at least 1 element for the first required stage + // Use -1 for stages that want to be skipped. + std::unique_ptr<EventMetrics> CreateScrollUpdateEventMetricsWithDispatchTimes( + bool is_inertial, + ScrollUpdateEventMetrics::ScrollUpdateType scroll_update_type, + const std::vector<int>& stage_durations) { + DCHECK_GT((int)stage_durations.size(), 0); + const base::TimeTicks event_time = AdvanceNowByUs(3); + AdvanceNowByUs(stage_durations[0]); + // Creates a kGestureScrollUpdate event. + return SetupEventMetricsWithDispatchTimes( + ScrollUpdateEventMetrics::CreateForTesting( + ui::ET_GESTURE_SCROLL_UPDATE, ui::ScrollInputType::kWheel, + is_inertial, scroll_update_type, /*delta=*/10.0f, event_time, + &test_tick_clock_), + stage_durations); + } + std::unique_ptr<EventMetrics> CreateScrollBeginMetrics() { - const base::TimeTicks event_time = AdvanceNowByMs(3); - AdvanceNowByMs(3); + const base::TimeTicks event_time = AdvanceNowByUs(3); + AdvanceNowByUs(3); return SetupEventMetrics(ScrollEventMetrics::CreateForTesting( ui::ET_GESTURE_SCROLL_BEGIN, ui::ScrollInputType::kWheel, /*is_inertial=*/false, event_time, &test_tick_clock_)); @@ -103,8 +145,8 @@ class CompositorFrameReporterTest : public testing::Test { std::unique_ptr<EventMetrics> CreateScrollUpdateEventMetrics( bool is_inertial, ScrollUpdateEventMetrics::ScrollUpdateType scroll_update_type) { - const base::TimeTicks event_time = AdvanceNowByMs(3); - AdvanceNowByMs(3); + const base::TimeTicks event_time = AdvanceNowByUs(3); + AdvanceNowByUs(3); return SetupEventMetrics(ScrollUpdateEventMetrics::CreateForTesting( ui::ET_GESTURE_SCROLL_UPDATE, ui::ScrollInputType::kWheel, is_inertial, scroll_update_type, /*delta=*/10.0f, event_time, &test_tick_clock_)); @@ -113,8 +155,8 @@ class CompositorFrameReporterTest : public testing::Test { std::unique_ptr<EventMetrics> CreatePinchEventMetrics( ui::EventType type, ui::ScrollInputType input_type) { - const base::TimeTicks event_time = AdvanceNowByMs(3); - AdvanceNowByMs(3); + const base::TimeTicks event_time = AdvanceNowByUs(3); + AdvanceNowByUs(3); return SetupEventMetrics(PinchEventMetrics::CreateForTesting( type, input_type, event_time, &test_tick_clock_)); } @@ -144,6 +186,28 @@ class CompositorFrameReporterTest : public testing::Test { return reporter; } + void IntToTimeDeltaVector(std::vector<base::TimeDelta>& timedelta_vector, + std::vector<int> int_vector) { + size_t vector_size = int_vector.size(); + for (size_t i = 0; i < vector_size; i++) { + timedelta_vector[i] = base::Microseconds(int_vector[i]); + } + } + + void VerifyLatencyInfo( + CompositorFrameReporter::CompositorLatencyInfo& expected_info, + CompositorFrameReporter::CompositorLatencyInfo& actual_info) { + EXPECT_EQ(expected_info.top_level_stages, actual_info.top_level_stages); + EXPECT_EQ(expected_info.blink_breakdown_stages, + actual_info.blink_breakdown_stages); + EXPECT_EQ(expected_info.viz_breakdown_stages, + actual_info.viz_breakdown_stages); + EXPECT_EQ(expected_info.total_latency, actual_info.total_latency); + EXPECT_EQ(expected_info.total_blink_latency, + actual_info.total_blink_latency); + EXPECT_EQ(expected_info.total_viz_latency, actual_info.total_viz_latency); + } + // This should be defined before |pipeline_reporter_| so it is created before // and destroyed after that. base::SimpleTestTickClock test_tick_clock_; @@ -151,6 +215,14 @@ class CompositorFrameReporterTest : public testing::Test { DroppedFrameCounter dropped_frame_counter_; TotalFrameCounter total_frame_counter_; std::unique_ptr<CompositorFrameReporter> pipeline_reporter_; + + // Number of breakdown stages of the current PipelineReporter + const int kNumOfCompositorStages = + static_cast<int>(CompositorFrameReporter::StageType::kStageTypeCount) - 1; + const int kNumDispatchStages = + static_cast<int>(EventMetrics::DispatchStage::kMaxValue); + const base::TimeDelta kLatencyPredictionDeviationThreshold = + base::Milliseconds(8.33); }; TEST_F(CompositorFrameReporterTest, MainFrameAbortedReportingTest) { @@ -161,25 +233,25 @@ TEST_F(CompositorFrameReporterTest, MainFrameAbortedReportingTest) { Now()); EXPECT_EQ(0u, pipeline_reporter_->stage_history_size_for_testing()); - AdvanceNowByMs(3); + AdvanceNowByUs(3); pipeline_reporter_->StartStage( CompositorFrameReporter::StageType::kSendBeginMainFrameToCommit, Now()); EXPECT_EQ(1u, pipeline_reporter_->stage_history_size_for_testing()); - AdvanceNowByMs(3); + AdvanceNowByUs(3); pipeline_reporter_->StartStage( CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame, Now()); EXPECT_EQ(2u, pipeline_reporter_->stage_history_size_for_testing()); - AdvanceNowByMs(3); + AdvanceNowByUs(3); pipeline_reporter_->StartStage( CompositorFrameReporter::StageType:: kSubmitCompositorFrameToPresentationCompositorFrame, Now()); EXPECT_EQ(3u, pipeline_reporter_->stage_history_size_for_testing()); - AdvanceNowByMs(3); + AdvanceNowByUs(3); pipeline_reporter_->TerminateFrame( CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame, Now()); EXPECT_EQ(4u, pipeline_reporter_->stage_history_size_for_testing()); @@ -206,12 +278,12 @@ TEST_F(CompositorFrameReporterTest, ReplacedByNewReporterReportingTest) { Now()); EXPECT_EQ(0u, pipeline_reporter_->stage_history_size_for_testing()); - AdvanceNowByMs(3); + AdvanceNowByUs(3); pipeline_reporter_->StartStage( CompositorFrameReporter::StageType::kEndCommitToActivation, Now()); EXPECT_EQ(1u, pipeline_reporter_->stage_history_size_for_testing()); - AdvanceNowByMs(2); + AdvanceNowByUs(2); pipeline_reporter_->TerminateFrame( CompositorFrameReporter::FrameTerminationStatus::kReplacedByNewReporter, Now()); @@ -230,13 +302,13 @@ TEST_F(CompositorFrameReporterTest, SubmittedFrameReportingTest) { CompositorFrameReporter::StageType::kActivation, Now()); EXPECT_EQ(0u, pipeline_reporter_->stage_history_size_for_testing()); - AdvanceNowByMs(3); + AdvanceNowByUs(3); pipeline_reporter_->StartStage( CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame, Now()); EXPECT_EQ(1u, pipeline_reporter_->stage_history_size_for_testing()); - AdvanceNowByMs(2); + AdvanceNowByUs(2); pipeline_reporter_->TerminateFrame( CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame, Now()); EXPECT_EQ(2u, pipeline_reporter_->stage_history_size_for_testing()); @@ -266,12 +338,12 @@ TEST_F(CompositorFrameReporterTest, SubmittedDroppedFrameReportingTest) { CompositorFrameReporter::StageType::kSendBeginMainFrameToCommit, Now()); EXPECT_EQ(0u, pipeline_reporter_->stage_history_size_for_testing()); - AdvanceNowByMs(3); + AdvanceNowByUs(3); pipeline_reporter_->StartStage(CompositorFrameReporter::StageType::kCommit, Now()); EXPECT_EQ(1u, pipeline_reporter_->stage_history_size_for_testing()); - AdvanceNowByMs(2); + AdvanceNowByUs(2); pipeline_reporter_->TerminateFrame( CompositorFrameReporter::FrameTerminationStatus::kDidNotPresentFrame, Now()); @@ -313,24 +385,24 @@ TEST_F(CompositorFrameReporterTest, std::make_move_iterator(std::end(event_metrics_ptrs))); std::vector<base::TimeTicks> event_times = GetEventTimestamps(events_metrics); - AdvanceNowByMs(3); + AdvanceNowByUs(3); pipeline_reporter_->StartStage( CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame, Now()); - AdvanceNowByMs(3); + AdvanceNowByUs(3); pipeline_reporter_->StartStage( CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame, Now()); - AdvanceNowByMs(3); + AdvanceNowByUs(3); pipeline_reporter_->StartStage( CompositorFrameReporter::StageType:: kSubmitCompositorFrameToPresentationCompositorFrame, Now()); pipeline_reporter_->AddEventsMetrics(std::move(events_metrics)); - const base::TimeTicks presentation_time = AdvanceNowByMs(3); + const base::TimeTicks presentation_time = AdvanceNowByUs(3); pipeline_reporter_->TerminateFrame( CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame, presentation_time); @@ -405,24 +477,24 @@ TEST_F(CompositorFrameReporterTest, std::make_move_iterator(std::end(event_metrics_ptrs))); std::vector<base::TimeTicks> event_times = GetEventTimestamps(events_metrics); - AdvanceNowByMs(3); + AdvanceNowByUs(3); pipeline_reporter_->StartStage( CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame, Now()); - AdvanceNowByMs(3); + AdvanceNowByUs(3); pipeline_reporter_->StartStage( CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame, Now()); - AdvanceNowByMs(3); + AdvanceNowByUs(3); pipeline_reporter_->StartStage( CompositorFrameReporter::StageType:: kSubmitCompositorFrameToPresentationCompositorFrame, Now()); pipeline_reporter_->AddEventsMetrics(std::move(events_metrics)); - AdvanceNowByMs(3); + AdvanceNowByUs(3); viz::FrameTimingDetails viz_breakdown = BuildVizBreakdown(); pipeline_reporter_->SetVizBreakdown(viz_breakdown); pipeline_reporter_->TerminateFrame( @@ -493,24 +565,24 @@ TEST_F(CompositorFrameReporterTest, std::make_move_iterator(std::end(event_metrics_ptrs))); std::vector<base::TimeTicks> event_times = GetEventTimestamps(events_metrics); - AdvanceNowByMs(3); + AdvanceNowByUs(3); pipeline_reporter_->StartStage( CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame, Now()); - AdvanceNowByMs(3); + AdvanceNowByUs(3); pipeline_reporter_->StartStage( CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame, Now()); - AdvanceNowByMs(3); + AdvanceNowByUs(3); pipeline_reporter_->StartStage( CompositorFrameReporter::StageType:: kSubmitCompositorFrameToPresentationCompositorFrame, Now()); pipeline_reporter_->AddEventsMetrics(std::move(events_metrics)); - AdvanceNowByMs(3); + AdvanceNowByUs(3); viz::FrameTimingDetails viz_breakdown = BuildVizBreakdown(); pipeline_reporter_->SetVizBreakdown(viz_breakdown); pipeline_reporter_->TerminateFrame( @@ -575,24 +647,24 @@ TEST_F(CompositorFrameReporterTest, std::make_move_iterator(std::begin(event_metrics_ptrs)), std::make_move_iterator(std::end(event_metrics_ptrs))); - AdvanceNowByMs(3); + AdvanceNowByUs(3); pipeline_reporter_->StartStage( CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame, Now()); - AdvanceNowByMs(3); + AdvanceNowByUs(3); pipeline_reporter_->StartStage( CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame, Now()); - AdvanceNowByMs(3); + AdvanceNowByUs(3); pipeline_reporter_->StartStage( CompositorFrameReporter::StageType:: kSubmitCompositorFrameToPresentationCompositorFrame, Now()); pipeline_reporter_->AddEventsMetrics(std::move(events_metrics)); - AdvanceNowByMs(3); + AdvanceNowByUs(3); pipeline_reporter_->TerminateFrame( CompositorFrameReporter::FrameTerminationStatus::kDidNotPresentFrame, Now()); @@ -720,5 +792,1367 @@ TEST_F(CompositorFrameReporterTest, PartialUpdateDependentQueues) { pipeline_reporter_->owned_partial_update_dependents_size_for_testing()); } +TEST_F(CompositorFrameReporterTest, StageLatencyGeneralPrediction) { + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame, + Now()); + + AdvanceNowByUs(3); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kSendBeginMainFrameToCommit, Now()); + AdvanceNowByUs(4); + base::TimeTicks begin_main_frame_start_time = Now(); + std::unique_ptr<BeginMainFrameMetrics> blink_breakdown = + BuildBlinkBreakdown(); + pipeline_reporter_->SetBlinkBreakdown(std::move(blink_breakdown), + begin_main_frame_start_time); + + pipeline_reporter_->StartStage(CompositorFrameReporter::StageType::kCommit, + Now()); + + AdvanceNowByUs(3); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kEndCommitToActivation, Now()); + + AdvanceNowByUs(3); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kActivation, Now()); + + AdvanceNowByUs(3); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame, + Now()); + + AdvanceNowByUs(3); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType:: + kSubmitCompositorFrameToPresentationCompositorFrame, + Now()); + viz::FrameTimingDetails viz_breakdown = BuildVizBreakdown(); + pipeline_reporter_->SetVizBreakdown(viz_breakdown); + + pipeline_reporter_->TerminateFrame( + CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame, + viz_breakdown.presentation_feedback.timestamp); + + // predictions when this is the very first prediction + CompositorFrameReporter::CompositorLatencyInfo expected_latency_predictions1; + expected_latency_predictions1.top_level_stages = { + base::Microseconds(3), base::Microseconds(55), base::Microseconds(3), + base::Microseconds(3), base::Microseconds(3), base::Microseconds(3), + base::Microseconds(15)}; + expected_latency_predictions1.blink_breakdown_stages = { + base::Microseconds(10), base::Microseconds(9), base::Microseconds(8), + base::Microseconds(7), base::Microseconds(0), base::Microseconds(5), + base::Microseconds(6), base::Microseconds(3), base::Microseconds(2), + base::Microseconds(1), base::Microseconds(4)}; + expected_latency_predictions1.viz_breakdown_stages = { + base::Microseconds(1), base::Microseconds(2), base::Microseconds(3), + base::Microseconds(4), base::Microseconds(5), base::Microseconds(0), + base::Microseconds(0), base::Microseconds(0), base::Microseconds(0)}; + expected_latency_predictions1.total_latency = base::Microseconds(85); + expected_latency_predictions1.total_blink_latency = base::Microseconds(55); + expected_latency_predictions1.total_viz_latency = base::Microseconds(15); + + // predictions when there exists a previous prediction + CompositorFrameReporter::CompositorLatencyInfo expected_latency_predictions2( + base::Microseconds(0)); + expected_latency_predictions2.top_level_stages = { + base::Microseconds(1), base::Microseconds(13), base::Microseconds(3), + base::Microseconds(0), base::Microseconds(2), base::Microseconds(3), + base::Microseconds(3)}; + expected_latency_predictions2.blink_breakdown_stages = { + base::Microseconds(10), base::Microseconds(9), base::Microseconds(8), + base::Microseconds(7), base::Microseconds(0), base::Microseconds(5), + base::Microseconds(6), base::Microseconds(3), base::Microseconds(2), + base::Microseconds(1), base::Microseconds(4)}; + expected_latency_predictions2.viz_breakdown_stages = { + base::Microseconds(1), base::Microseconds(2), base::Microseconds(3), + base::Microseconds(4), base::Microseconds(5), base::Microseconds(0), + base::Microseconds(0), base::Microseconds(0), base::Microseconds(0)}; + expected_latency_predictions2.total_latency = base::Microseconds(28); + expected_latency_predictions2.total_blink_latency = base::Microseconds(55); + expected_latency_predictions2.total_viz_latency = base::Microseconds(15); + + // expected attribution for all 3 cases above + std::vector<std::string> expected_latency_attributions = {}; + + CompositorFrameReporter::CompositorLatencyInfo actual_latency_predictions1( + base::Microseconds(-1)); + pipeline_reporter_->CalculateCompositorLatencyPrediction( + actual_latency_predictions1, kLatencyPredictionDeviationThreshold); + std::vector<std::string> actual_latency_attributions1 = + pipeline_reporter_->high_latency_substages_for_testing(); + pipeline_reporter_->ClearHighLatencySubstagesForTesting(); + + CompositorFrameReporter::CompositorLatencyInfo actual_latency_predictions2( + base::Microseconds(0)); + actual_latency_predictions2.top_level_stages = { + base::Microseconds(1), base::Microseconds(0), base::Microseconds(4), + base::Microseconds(0), base::Microseconds(2), base::Microseconds(3), + base::Microseconds(0)}; + actual_latency_predictions2.total_latency = base::Microseconds(10); + pipeline_reporter_->CalculateCompositorLatencyPrediction( + actual_latency_predictions2, kLatencyPredictionDeviationThreshold); + std::vector<std::string> actual_latency_attributions2 = + pipeline_reporter_->high_latency_substages_for_testing(); + pipeline_reporter_->ClearHighLatencySubstagesForTesting(); + + VerifyLatencyInfo(expected_latency_predictions1, actual_latency_predictions1); + VerifyLatencyInfo(expected_latency_predictions2, actual_latency_predictions2); + + EXPECT_EQ(expected_latency_attributions, actual_latency_attributions1); + EXPECT_EQ(expected_latency_attributions, actual_latency_attributions2); + + pipeline_reporter_ = nullptr; +} + +TEST_F(CompositorFrameReporterTest, StageLatencyAllZeroPrediction) { + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame, + Now()); + + AdvanceNowByUs(0); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kSendBeginMainFrameToCommit, Now()); + + AdvanceNowByUs(0); + pipeline_reporter_->StartStage(CompositorFrameReporter::StageType::kCommit, + Now()); + + AdvanceNowByUs(0); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kEndCommitToActivation, Now()); + + AdvanceNowByUs(0); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kActivation, Now()); + + AdvanceNowByUs(0); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame, + Now()); + + AdvanceNowByUs(0); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType:: + kSubmitCompositorFrameToPresentationCompositorFrame, + Now()); + + AdvanceNowByUs(0); + pipeline_reporter_->TerminateFrame( + CompositorFrameReporter::FrameTerminationStatus::kDidNotProduceFrame, + Now()); + + // predictions when this is the very first prediction + CompositorFrameReporter::CompositorLatencyInfo expected_latency_predictions1( + base::Microseconds(-1)); + + // predictions when there exists a previous prediction + CompositorFrameReporter::CompositorLatencyInfo expected_latency_predictions2( + base::Microseconds(0)); + expected_latency_predictions2.top_level_stages = { + base::Microseconds(1), base::Microseconds(0), base::Microseconds(4), + base::Microseconds(0), base::Microseconds(2), base::Microseconds(3), + base::Microseconds(0)}; + expected_latency_predictions2.total_latency = base::Microseconds(10); + + // expected attribution for all 3 cases above + std::vector<std::string> expected_latency_attributions = {}; + + CompositorFrameReporter::CompositorLatencyInfo actual_latency_predictions1( + base::Microseconds(-1)); + pipeline_reporter_->CalculateCompositorLatencyPrediction( + actual_latency_predictions1, kLatencyPredictionDeviationThreshold); + std::vector<std::string> actual_latency_attributions1 = + pipeline_reporter_->high_latency_substages_for_testing(); + pipeline_reporter_->ClearHighLatencySubstagesForTesting(); + + CompositorFrameReporter::CompositorLatencyInfo actual_latency_predictions2( + base::Microseconds(0)); + actual_latency_predictions2.top_level_stages = { + base::Microseconds(1), base::Microseconds(0), base::Microseconds(4), + base::Microseconds(0), base::Microseconds(2), base::Microseconds(3), + base::Microseconds(0)}; + actual_latency_predictions2.total_latency = base::Microseconds(10); + + pipeline_reporter_->CalculateCompositorLatencyPrediction( + actual_latency_predictions2, kLatencyPredictionDeviationThreshold); + std::vector<std::string> actual_latency_attributions2 = + pipeline_reporter_->high_latency_substages_for_testing(); + pipeline_reporter_->ClearHighLatencySubstagesForTesting(); + + VerifyLatencyInfo(expected_latency_predictions1, actual_latency_predictions1); + VerifyLatencyInfo(expected_latency_predictions2, actual_latency_predictions2); + + EXPECT_EQ(expected_latency_attributions, actual_latency_attributions1); + EXPECT_EQ(expected_latency_attributions, actual_latency_attributions2); + + pipeline_reporter_ = nullptr; +} + +TEST_F(CompositorFrameReporterTest, StageLatencyLargeDurationPrediction) { + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame, + Now()); + + AdvanceNowByUs(10000000); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kSendBeginMainFrameToCommit, Now()); + AdvanceNowByUs(400000); + base::TimeTicks begin_main_frame_start_time = Now(); + + auto blink_breakdown = std::make_unique<BeginMainFrameMetrics>(); + blink_breakdown->handle_input_events = base::Microseconds(1000000); + blink_breakdown->animate = base::Microseconds(900000); + blink_breakdown->style_update = base::Microseconds(800000); + blink_breakdown->layout_update = base::Microseconds(300000); + blink_breakdown->accessibility = base::Microseconds(400000); + blink_breakdown->prepaint = base::Microseconds(500000); + blink_breakdown->compositing_inputs = base::Microseconds(600000); + blink_breakdown->paint = base::Microseconds(300000); + blink_breakdown->composite_commit = base::Microseconds(200000); + blink_breakdown->update_layers = base::Microseconds(100000); + AdvanceNowByUs(5100000); + + pipeline_reporter_->SetBlinkBreakdown(std::move(blink_breakdown), + begin_main_frame_start_time); + + pipeline_reporter_->StartStage(CompositorFrameReporter::StageType::kCommit, + Now()); + + AdvanceNowByUs(6000000); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kEndCommitToActivation, Now()); + + AdvanceNowByUs(10000000); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kActivation, Now()); + + AdvanceNowByUs(0); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame, + Now()); + + AdvanceNowByUs(2000000); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType:: + kSubmitCompositorFrameToPresentationCompositorFrame, + Now()); + + viz::FrameTimingDetails viz_breakdown; + viz_breakdown.received_compositor_frame_timestamp = AdvanceNowByUs(1000000); + viz_breakdown.draw_start_timestamp = AdvanceNowByUs(2000000); + viz_breakdown.swap_timings.swap_start = AdvanceNowByUs(3000000); + viz_breakdown.presentation_feedback.available_timestamp = + AdvanceNowByUs(15000000); + viz_breakdown.presentation_feedback.ready_timestamp = AdvanceNowByUs(700000); + viz_breakdown.presentation_feedback.latch_timestamp = AdvanceNowByUs(800000); + viz_breakdown.swap_timings.swap_end = AdvanceNowByUs(1000000); + viz_breakdown.presentation_feedback.timestamp = AdvanceNowByUs(5000000); + + pipeline_reporter_->SetVizBreakdown(viz_breakdown); + + pipeline_reporter_->TerminateFrame( + CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame, Now()); + + // predictions when this is the very first prediction + CompositorFrameReporter::CompositorLatencyInfo expected_latency_predictions1; + expected_latency_predictions1.top_level_stages = { + base::Microseconds(10000000), base::Microseconds(5500000), + base::Microseconds(6000000), base::Microseconds(10000000), + base::Microseconds(0), base::Microseconds(2000000), + base::Microseconds(28500000)}; + expected_latency_predictions1.blink_breakdown_stages = { + base::Microseconds(1000000), base::Microseconds(900000), + base::Microseconds(800000), base::Microseconds(300000), + base::Microseconds(400000), base::Microseconds(500000), + base::Microseconds(600000), base::Microseconds(300000), + base::Microseconds(200000), base::Microseconds(100000), + base::Microseconds(400000)}; + expected_latency_predictions1.viz_breakdown_stages = { + base::Microseconds(1000000), base::Microseconds(2000000), + base::Microseconds(3000000), base::Microseconds(0), + base::Microseconds(5000000), base::Microseconds(15000000), + base::Microseconds(700000), base::Microseconds(800000), + base::Microseconds(1000000)}; + expected_latency_predictions1.total_latency = base::Microseconds(62000000); + expected_latency_predictions1.total_blink_latency = + base::Microseconds(5500000); + expected_latency_predictions1.total_viz_latency = + base::Microseconds(28500000); + + // predictions when there exists a previous prediction + CompositorFrameReporter::CompositorLatencyInfo expected_latency_predictions2; + expected_latency_predictions2.top_level_stages = { + base::Microseconds(8500000), base::Microseconds(4375000), + base::Microseconds(4875000), base::Microseconds(5252650), + base::Microseconds(750000), base::Microseconds(1850000), + base::Microseconds(18375000)}; + expected_latency_predictions2.blink_breakdown_stages = { + base::Microseconds(1000000), base::Microseconds(225000), + base::Microseconds(500000), base::Microseconds(75000), + base::Microseconds(250000), base::Microseconds(350000), + base::Microseconds(150000), base::Microseconds(750000), + base::Microseconds(650000), base::Microseconds(250000), + base::Microseconds(175000)}; + expected_latency_predictions2.viz_breakdown_stages = { + base::Microseconds(625000), base::Microseconds(875000), + base::Microseconds(1500000), base::Microseconds(0), + base::Microseconds(1706075), base::Microseconds(9750000), + base::Microseconds(925000), base::Microseconds(1100000), + base::Microseconds(1893925)}; + expected_latency_predictions2.total_latency = base::Microseconds(43977650); + expected_latency_predictions2.total_blink_latency = + base::Microseconds(4375000); + expected_latency_predictions2.total_viz_latency = + base::Microseconds(18375000); + + // expected attribution for cases 1 above + std::vector<std::string> expected_latency_attributions1 = {}; + + // expected attribution for case 2 above + std::vector<std::string> expected_latency_attributions2 = { + "EndCommitToActivation", + "SubmitCompositorFrameToPresentationCompositorFrame." + "SwapEndToPresentationCompositorFrame"}; + + CompositorFrameReporter::CompositorLatencyInfo actual_latency_predictions1( + base::Microseconds(-1)); + pipeline_reporter_->CalculateCompositorLatencyPrediction( + actual_latency_predictions1, kLatencyPredictionDeviationThreshold); + std::vector<std::string> actual_latency_attributions1 = + pipeline_reporter_->high_latency_substages_for_testing(); + pipeline_reporter_->ClearHighLatencySubstagesForTesting(); + + CompositorFrameReporter::CompositorLatencyInfo actual_latency_predictions2; + actual_latency_predictions2.top_level_stages = { + base::Microseconds(8000000), base::Microseconds(4000000), + base::Microseconds(4500000), base::Microseconds(3670200), + base::Microseconds(1000000), base::Microseconds(1800000), + base::Microseconds(15000000)}; + actual_latency_predictions2.blink_breakdown_stages = { + base::Microseconds(1000000), base::Microseconds(0), + base::Microseconds(400000), base::Microseconds(0), + base::Microseconds(200000), base::Microseconds(300000), + base::Microseconds(0), base::Microseconds(900000), + base::Microseconds(800000), base::Microseconds(300000), + base::Microseconds(100000)}; + actual_latency_predictions2.viz_breakdown_stages = { + base::Microseconds(500000), base::Microseconds(500000), + base::Microseconds(1000000), base::Microseconds(0), + base::Microseconds(608100), base::Microseconds(8000000), + base::Microseconds(1000000), base::Microseconds(1200000), + base::Microseconds(2191900)}; + actual_latency_predictions2.total_latency = base::Microseconds(37970200); + actual_latency_predictions2.total_blink_latency = base::Microseconds(4000000); + actual_latency_predictions2.total_viz_latency = base::Microseconds(15000000); + + pipeline_reporter_->CalculateCompositorLatencyPrediction( + actual_latency_predictions2, kLatencyPredictionDeviationThreshold); + std::vector<std::string> actual_latency_attributions2 = + pipeline_reporter_->high_latency_substages_for_testing(); + pipeline_reporter_->ClearHighLatencySubstagesForTesting(); + + VerifyLatencyInfo(expected_latency_predictions1, actual_latency_predictions1); + VerifyLatencyInfo(expected_latency_predictions2, actual_latency_predictions2); + + EXPECT_EQ(expected_latency_attributions1, actual_latency_attributions1); + EXPECT_EQ(expected_latency_attributions2, actual_latency_attributions2); + + pipeline_reporter_ = nullptr; +} + +TEST_F(CompositorFrameReporterTest, StageLatencyMultiplePrediction) { + CompositorFrameReporter::CompositorLatencyInfo actual_latency_predictions( + base::Microseconds(-1)); + CompositorFrameReporter::CompositorLatencyInfo expected_latency_predictions( + base::Microseconds(-1)); + + // First compositor reporter (general) + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame, + Now()); + + AdvanceNowByUs(16000); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame, + Now()); + + AdvanceNowByUs(1500); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType:: + kSubmitCompositorFrameToPresentationCompositorFrame, + Now()); + + viz::FrameTimingDetails viz_breakdown; + viz_breakdown.received_compositor_frame_timestamp = AdvanceNowByUs(330000); + viz_breakdown.draw_start_timestamp = AdvanceNowByUs(23000); + viz_breakdown.swap_timings.swap_start = AdvanceNowByUs(170000); + viz_breakdown.swap_timings.swap_end = AdvanceNowByUs(280000); + viz_breakdown.presentation_feedback.timestamp = AdvanceNowByUs(30000); + + pipeline_reporter_->SetVizBreakdown(viz_breakdown); + + pipeline_reporter_->TerminateFrame( + CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame, Now()); + + expected_latency_predictions.top_level_stages = { + base::Microseconds(16000), base::Microseconds(0), + base::Microseconds(0), base::Microseconds(0), + base::Microseconds(0), base::Microseconds(1500), + base::Microseconds(833000)}; + expected_latency_predictions.blink_breakdown_stages = { + base::Microseconds(0), base::Microseconds(0), base::Microseconds(0), + base::Microseconds(0), base::Microseconds(0), base::Microseconds(0), + base::Microseconds(0), base::Microseconds(0), base::Microseconds(0), + base::Microseconds(0), base::Microseconds(0)}; + expected_latency_predictions.viz_breakdown_stages = { + base::Microseconds(330000), base::Microseconds(23000), + base::Microseconds(170000), base::Microseconds(280000), + base::Microseconds(30000), base::Microseconds(0), + base::Microseconds(0), base::Microseconds(0), + base::Microseconds(0)}; + expected_latency_predictions.total_latency = base::Microseconds(850500); + expected_latency_predictions.total_blink_latency = base::Microseconds(0); + expected_latency_predictions.total_viz_latency = base::Microseconds(833000); + + pipeline_reporter_->CalculateCompositorLatencyPrediction( + actual_latency_predictions, kLatencyPredictionDeviationThreshold); + + VerifyLatencyInfo(expected_latency_predictions, actual_latency_predictions); + + // Second compositor reporter (without subtmit stage) + pipeline_reporter_ = CreatePipelineReporter(); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame, + Now()); + + AdvanceNowByUs(16000); + pipeline_reporter_->TerminateFrame( + CompositorFrameReporter::FrameTerminationStatus::kDidNotProduceFrame, + Now()); + + expected_latency_predictions.top_level_stages = { + base::Microseconds(16000), base::Microseconds(0), + base::Microseconds(0), base::Microseconds(0), + base::Microseconds(0), base::Microseconds(1500), + base::Microseconds(833000)}; + expected_latency_predictions.blink_breakdown_stages = { + base::Microseconds(0), base::Microseconds(0), base::Microseconds(0), + base::Microseconds(0), base::Microseconds(0), base::Microseconds(0), + base::Microseconds(0), base::Microseconds(0), base::Microseconds(0), + base::Microseconds(0), base::Microseconds(0)}; + expected_latency_predictions.viz_breakdown_stages = { + base::Microseconds(330000), base::Microseconds(23000), + base::Microseconds(170000), base::Microseconds(280000), + base::Microseconds(30000), base::Microseconds(0), + base::Microseconds(0), base::Microseconds(0), + base::Microseconds(0)}; + expected_latency_predictions.total_latency = base::Microseconds(850500); + expected_latency_predictions.total_blink_latency = base::Microseconds(0); + expected_latency_predictions.total_viz_latency = base::Microseconds(833000); + + pipeline_reporter_->CalculateCompositorLatencyPrediction( + actual_latency_predictions, kLatencyPredictionDeviationThreshold); + + VerifyLatencyInfo(expected_latency_predictions, actual_latency_predictions); + + // Third compositor reporter (prediction and actual latency does not differ + // by 8) + pipeline_reporter_ = CreatePipelineReporter(); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame, + Now()); + + AdvanceNowByUs(16500); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame, + Now()); + + AdvanceNowByUs(2000); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType:: + kSubmitCompositorFrameToPresentationCompositorFrame, + Now()); + + viz_breakdown.received_compositor_frame_timestamp = AdvanceNowByUs(330000); + viz_breakdown.draw_start_timestamp = AdvanceNowByUs(23000); + viz_breakdown.swap_timings.swap_start = AdvanceNowByUs(170000); + viz_breakdown.swap_timings.swap_end = AdvanceNowByUs(280000); + viz_breakdown.presentation_feedback.timestamp = AdvanceNowByUs(30000); + + pipeline_reporter_->SetVizBreakdown(viz_breakdown); + + pipeline_reporter_->TerminateFrame( + CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame, Now()); + + expected_latency_predictions.top_level_stages = { + base::Microseconds(16125), base::Microseconds(0), + base::Microseconds(0), base::Microseconds(0), + base::Microseconds(0), base::Microseconds(1625), + base::Microseconds(833000)}; + expected_latency_predictions.blink_breakdown_stages = { + base::Microseconds(0), base::Microseconds(0), base::Microseconds(0), + base::Microseconds(0), base::Microseconds(0), base::Microseconds(0), + base::Microseconds(0), base::Microseconds(0), base::Microseconds(0), + base::Microseconds(0), base::Microseconds(0)}; + expected_latency_predictions.viz_breakdown_stages = { + base::Microseconds(330000), base::Microseconds(23000), + base::Microseconds(170000), base::Microseconds(280000), + base::Microseconds(30000), base::Microseconds(0), + base::Microseconds(0), base::Microseconds(0), + base::Microseconds(0)}; + expected_latency_predictions.total_latency = base::Microseconds(850750); + expected_latency_predictions.total_blink_latency = base::Microseconds(0); + expected_latency_predictions.total_viz_latency = base::Microseconds(833000); + + pipeline_reporter_->CalculateCompositorLatencyPrediction( + actual_latency_predictions, kLatencyPredictionDeviationThreshold); + + VerifyLatencyInfo(expected_latency_predictions, actual_latency_predictions); + + // Fourth compositor reporter (total duration is 0) + pipeline_reporter_ = CreatePipelineReporter(); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame, + Now()); + + AdvanceNowByUs(0); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kSendBeginMainFrameToCommit, Now()); + + AdvanceNowByUs(0); + pipeline_reporter_->StartStage(CompositorFrameReporter::StageType::kCommit, + Now()); + + AdvanceNowByUs(0); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kEndCommitToActivation, Now()); + + AdvanceNowByUs(0); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType:: + kSubmitCompositorFrameToPresentationCompositorFrame, + Now()); + + AdvanceNowByUs(0); + pipeline_reporter_->TerminateFrame( + CompositorFrameReporter::FrameTerminationStatus::kDidNotProduceFrame, + Now()); + + expected_latency_predictions.top_level_stages = { + base::Microseconds(16125), base::Microseconds(0), + base::Microseconds(0), base::Microseconds(0), + base::Microseconds(0), base::Microseconds(1625), + base::Microseconds(833000)}; + expected_latency_predictions.blink_breakdown_stages = { + base::Microseconds(0), base::Microseconds(0), base::Microseconds(0), + base::Microseconds(0), base::Microseconds(0), base::Microseconds(0), + base::Microseconds(0), base::Microseconds(0), base::Microseconds(0), + base::Microseconds(0), base::Microseconds(0)}; + expected_latency_predictions.viz_breakdown_stages = { + base::Microseconds(330000), base::Microseconds(23000), + base::Microseconds(170000), base::Microseconds(280000), + base::Microseconds(30000), base::Microseconds(0), + base::Microseconds(0), base::Microseconds(0), + base::Microseconds(0)}; + expected_latency_predictions.total_latency = base::Microseconds(850750); + expected_latency_predictions.total_blink_latency = base::Microseconds(0); + expected_latency_predictions.total_viz_latency = base::Microseconds(833000); + + pipeline_reporter_->CalculateCompositorLatencyPrediction( + actual_latency_predictions, kLatencyPredictionDeviationThreshold); + + VerifyLatencyInfo(expected_latency_predictions, actual_latency_predictions); + + // Fifth compositor reporter (prediction and actual latency differ by a lot) + pipeline_reporter_ = CreatePipelineReporter(); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame, + Now()); + + AdvanceNowByUs(16000); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kSendBeginMainFrameToCommit, Now()); + + AdvanceNowByUs(4000); + base::TimeTicks begin_main_frame_start_time = Now(); + + auto blink_breakdown = std::make_unique<BeginMainFrameMetrics>(); + blink_breakdown->handle_input_events = base::Microseconds(12000); + blink_breakdown->animate = base::Microseconds(3000); + blink_breakdown->style_update = base::Microseconds(7000); + blink_breakdown->layout_update = base::Microseconds(19000); + blink_breakdown->accessibility = base::Microseconds(800); + blink_breakdown->prepaint = base::Microseconds(4100); + blink_breakdown->compositing_inputs = base::Microseconds(5100); + blink_breakdown->paint = base::Microseconds(1500); + blink_breakdown->composite_commit = base::Microseconds(1500); + blink_breakdown->update_layers = base::Microseconds(2000); + AdvanceNowByUs(56000); + + pipeline_reporter_->SetBlinkBreakdown(std::move(blink_breakdown), + begin_main_frame_start_time); + + pipeline_reporter_->StartStage(CompositorFrameReporter::StageType::kCommit, + Now()); + + AdvanceNowByUs(6000); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kEndCommitToActivation, Now()); + + AdvanceNowByUs(3000); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kActivation, Now()); + + AdvanceNowByUs(300); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame, + Now()); + + AdvanceNowByUs(39000); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType:: + kSubmitCompositorFrameToPresentationCompositorFrame, + Now()); + + viz_breakdown.received_compositor_frame_timestamp = AdvanceNowByUs(340000); + viz_breakdown.draw_start_timestamp = AdvanceNowByUs(20000); + viz_breakdown.swap_timings.swap_start = AdvanceNowByUs(160000); + viz_breakdown.swap_timings.swap_end = AdvanceNowByUs(283000); + viz_breakdown.presentation_feedback.timestamp = AdvanceNowByUs(30000); + + pipeline_reporter_->SetVizBreakdown(viz_breakdown); + + pipeline_reporter_->TerminateFrame( + CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame, Now()); + + expected_latency_predictions.top_level_stages = { + base::Microseconds(16093), base::Microseconds(15000), + base::Microseconds(1500), base::Microseconds(750), + base::Microseconds(75), base::Microseconds(10968), + base::Microseconds(833000)}; + expected_latency_predictions.blink_breakdown_stages = { + base::Microseconds(12000), base::Microseconds(3000), + base::Microseconds(7000), base::Microseconds(19000), + base::Microseconds(800), base::Microseconds(4100), + base::Microseconds(5100), base::Microseconds(1500), + base::Microseconds(1500), base::Microseconds(2000), + base::Microseconds(4000)}; + expected_latency_predictions.viz_breakdown_stages = { + base::Microseconds(332500), base::Microseconds(22250), + base::Microseconds(167500), base::Microseconds(280750), + base::Microseconds(30000), base::Microseconds(0), + base::Microseconds(0), base::Microseconds(0), + base::Microseconds(0)}; + expected_latency_predictions.total_latency = base::Microseconds(877387); + expected_latency_predictions.total_blink_latency = base::Microseconds(60000); + expected_latency_predictions.total_viz_latency = base::Microseconds(833000); + + std::vector<std::string> expected_latency_attributions = { + "EndActivateToSubmitCompositorFrame"}; + + pipeline_reporter_->CalculateCompositorLatencyPrediction( + actual_latency_predictions, kLatencyPredictionDeviationThreshold); + std::vector<std::string> actual_latency_attributions = + pipeline_reporter_->high_latency_substages_for_testing(); + + VerifyLatencyInfo(expected_latency_predictions, actual_latency_predictions); + EXPECT_EQ(expected_latency_attributions, actual_latency_attributions); + + pipeline_reporter_ = nullptr; +} + +// Tests that when a frame is presented to the user, event latency predictions +// are reported properly. +TEST_F(CompositorFrameReporterTest, EventLatencyDispatchPredictions) { + std::vector<int> dispatch_times = {300, 300, 300, 300, 300}; + std::unique_ptr<EventMetrics> event_metrics_ptrs[] = { + CreateScrollUpdateEventMetricsWithDispatchTimes( + false, ScrollUpdateEventMetrics::ScrollUpdateType::kContinued, + dispatch_times)}; + 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))}; + + AdvanceNowByUs(300); // Transition time + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame, + Now()); + + AdvanceNowByUs(300); // kEndActivateToSubmitCompositorFrame stage duration + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType:: + kSubmitCompositorFrameToPresentationCompositorFrame, + Now()); + + pipeline_reporter_->AddEventsMetrics(std::move(events_metrics)); + + // Test with no previous stage predictions. + std::vector<base::TimeDelta> expected_predictions1(kNumDispatchStages, + base::Microseconds(-1)); + IntToTimeDeltaVector(expected_predictions1, + std::vector<int>{300, 300, 300, 300, 300}); + base::TimeDelta expected_transition1 = base::Microseconds(300); + base::TimeDelta expected_total1 = base::Microseconds(2100); + CompositorFrameReporter::EventLatencyInfo actual_predictions1 = + CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages, + kNumOfCompositorStages); + pipeline_reporter_->CalculateEventLatencyPrediction( + actual_predictions1, kLatencyPredictionDeviationThreshold); + + // Test with all previous stage predictions. + std::vector<base::TimeDelta> expected_predictions2(kNumDispatchStages, + base::Microseconds(-1)); + IntToTimeDeltaVector(expected_predictions2, + std::vector<int>{262, 300, 412, 225, 450}); + base::TimeDelta expected_transition2 = base::Microseconds(390); + base::TimeDelta expected_total2 = base::Microseconds(2339); + CompositorFrameReporter::EventLatencyInfo actual_predictions2 = + CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages, + kNumOfCompositorStages); + IntToTimeDeltaVector(actual_predictions2.dispatch_durations, + std::vector<int>{250, 300, 450, 200, 500}); + actual_predictions2.transition_duration = base::Microseconds(420); + pipeline_reporter_->CalculateEventLatencyPrediction( + actual_predictions2, kLatencyPredictionDeviationThreshold); + + // Test with some previous stage predictions. + std::vector<base::TimeDelta> expected_predictions3(kNumDispatchStages, + base::Microseconds(-1)); + IntToTimeDeltaVector(expected_predictions3, + std::vector<int>{375, 450, 300, 300, 300}); + base::TimeDelta expected_transition3 = base::Microseconds(270); + base::TimeDelta expected_total3 = base::Microseconds(2295); + CompositorFrameReporter::EventLatencyInfo actual_predictions3 = + CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages, + kNumOfCompositorStages); + IntToTimeDeltaVector(actual_predictions3.dispatch_durations, + std::vector<int>{400, 500, 300, -1, -1}); + actual_predictions3.transition_duration = base::Microseconds(260); + pipeline_reporter_->CalculateEventLatencyPrediction( + actual_predictions3, kLatencyPredictionDeviationThreshold); + + for (int i = 0; i < kNumDispatchStages; i++) { + EXPECT_EQ(expected_predictions1[i], + actual_predictions1.dispatch_durations[i]); + EXPECT_EQ(expected_predictions2[i], + actual_predictions2.dispatch_durations[i]); + EXPECT_EQ(expected_predictions3[i], + actual_predictions3.dispatch_durations[i]); + } + EXPECT_EQ(expected_transition1, actual_predictions1.transition_duration); + EXPECT_EQ(expected_total1, actual_predictions1.total_duration); + EXPECT_EQ(expected_transition2, actual_predictions2.transition_duration); + EXPECT_EQ(expected_total2, actual_predictions2.total_duration); + EXPECT_EQ(expected_transition3, actual_predictions3.transition_duration); + EXPECT_EQ(expected_total3, actual_predictions3.total_duration); + + pipeline_reporter_ = nullptr; +} + +// Tests that when a new frame with missing dispatch stages is presented to +// the user, event latency predictions are reported properly. +TEST_F(CompositorFrameReporterTest, + EventLatencyDispatchPredictionsWithMissingStages) { + // Invalid EventLatency stage durations will cause program to crash, + // validity checked in event_latency_tracing_recorder.cc. + std::vector<int> dispatch_times = {400, 600, 700, -1, -1}; + std::unique_ptr<EventMetrics> event_metrics_ptrs[] = { + CreateScrollUpdateEventMetricsWithDispatchTimes( + false, ScrollUpdateEventMetrics::ScrollUpdateType::kContinued, + dispatch_times)}; + 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))}; + + AdvanceNowByUs(470); // Transition time + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame, + Now()); + + AdvanceNowByUs(300); // kEndActivateToSubmitCompositorFrame stage duration + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType:: + kSubmitCompositorFrameToPresentationCompositorFrame, + Now()); + + pipeline_reporter_->AddEventsMetrics(std::move(events_metrics)); + + // Test with no previous stage predictions. + std::vector<base::TimeDelta> expected_predictions1(kNumDispatchStages, + base::Microseconds(-1)); + IntToTimeDeltaVector(expected_predictions1, + std::vector<int>{400, 600, 700, -1, -1}); + base::TimeDelta expected_transition1 = base::Microseconds(470); + base::TimeDelta expected_total1 = base::Microseconds(2470); + CompositorFrameReporter::EventLatencyInfo actual_predictions1 = + CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages, + kNumOfCompositorStages); + pipeline_reporter_->CalculateEventLatencyPrediction( + actual_predictions1, kLatencyPredictionDeviationThreshold); + + // Test with all previous stage predictions. + std::vector<base::TimeDelta> expected_predictions2(kNumDispatchStages, + base::Microseconds(-1)); + IntToTimeDeltaVector(expected_predictions2, + std::vector<int>{250, 375, 475, 200, 500}); + base::TimeDelta expected_transition2 = base::Microseconds(402); + base::TimeDelta expected_total2 = base::Microseconds(2502); + CompositorFrameReporter::EventLatencyInfo actual_predictions2 = + CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages, + kNumOfCompositorStages); + IntToTimeDeltaVector(actual_predictions2.dispatch_durations, + std::vector<int>{200, 300, 400, 200, 500}); + actual_predictions2.transition_duration = base::Microseconds(380); + pipeline_reporter_->CalculateEventLatencyPrediction( + actual_predictions2, kLatencyPredictionDeviationThreshold); + + // Test with some previous stage predictions. + std::vector<base::TimeDelta> expected_predictions3(kNumDispatchStages, + base::Microseconds(-1)); + IntToTimeDeltaVector(expected_predictions3, + std::vector<int>{400, 525, 745, -1, -1}); + base::TimeDelta expected_transition3 = base::Microseconds(492); + base::TimeDelta expected_total3 = base::Microseconds(2462); + CompositorFrameReporter::EventLatencyInfo actual_predictions3 = + CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages, + kNumOfCompositorStages); + IntToTimeDeltaVector(actual_predictions3.dispatch_durations, + std::vector<int>{400, 500, 760, -1, -1}); + actual_predictions3.transition_duration = base::Microseconds(500); + pipeline_reporter_->CalculateEventLatencyPrediction( + actual_predictions3, kLatencyPredictionDeviationThreshold); + + for (int i = 0; i < kNumDispatchStages; i++) { + EXPECT_EQ(expected_predictions1[i], + actual_predictions1.dispatch_durations[i]); + EXPECT_EQ(expected_predictions2[i], + actual_predictions2.dispatch_durations[i]); + EXPECT_EQ(expected_predictions3[i], + actual_predictions3.dispatch_durations[i]); + } + EXPECT_EQ(expected_transition1, actual_predictions1.transition_duration); + EXPECT_EQ(expected_total1, actual_predictions1.total_duration); + EXPECT_EQ(expected_transition2, actual_predictions2.transition_duration); + EXPECT_EQ(expected_total2, actual_predictions2.total_duration); + EXPECT_EQ(expected_transition3, actual_predictions3.transition_duration); + EXPECT_EQ(expected_total3, actual_predictions3.total_duration); + + pipeline_reporter_ = nullptr; +} + +// Tests that when a frame is presented to the user, event latency predictions +// are reported properly. +TEST_F(CompositorFrameReporterTest, EventLatencyCompositorPredictions) { + std::vector<int> dispatch_times = {300, 300, 300, 300, 300}; + std::unique_ptr<EventMetrics> event_metrics_ptrs[] = { + CreateScrollUpdateEventMetricsWithDispatchTimes( + false, ScrollUpdateEventMetrics::ScrollUpdateType::kContinued, + dispatch_times)}; + 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))}; + + AdvanceNowByUs(300); // Transition time + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame, + Now()); + + // For this test there are only 3 compositor substages updated which reflects + // a more realistic scenario. + + AdvanceNowByUs(300); // kBeginImplFrameToSendBeginMainFrame duration + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame, + Now()); + + AdvanceNowByUs(300); // kEndActivateToSubmitCompositorFrame duration + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType:: + kSubmitCompositorFrameToPresentationCompositorFrame, + Now()); + + // kSubmitCompositorFrameToPresentationCompositorFrame duration + AdvanceNowByUs(300); + pipeline_reporter_->TerminateFrame( + CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame, Now()); + + pipeline_reporter_->AddEventsMetrics(std::move(events_metrics)); + + // Test with no previous stage predictions. + std::vector<base::TimeDelta> expected_dispatch1(kNumDispatchStages, + base::Microseconds(-1)); + IntToTimeDeltaVector(expected_dispatch1, + std::vector<int>{300, 300, 300, 300, 300}); + base::TimeDelta expected_transition1 = base::Microseconds(300); + std::vector<base::TimeDelta> expected_compositor1(kNumOfCompositorStages, + base::Microseconds(-1)); + IntToTimeDeltaVector(expected_compositor1, + std::vector<int>{300, -1, -1, -1, -1, 300, 300}); + base::TimeDelta expected_total1 = base::Microseconds(2700); + CompositorFrameReporter::EventLatencyInfo actual_predictions1 = + CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages, + kNumOfCompositorStages); + pipeline_reporter_->CalculateEventLatencyPrediction( + actual_predictions1, kLatencyPredictionDeviationThreshold); + + // Test with all previous stage predictions. + std::vector<base::TimeDelta> expected_dispatch2(kNumDispatchStages, + base::Microseconds(-1)); + IntToTimeDeltaVector(expected_dispatch2, + std::vector<int>{262, 300, 412, 225, 450}); + base::TimeDelta expected_transition2 = base::Microseconds(390); + std::vector<base::TimeDelta> expected_compositor2(kNumOfCompositorStages, + base::Microseconds(-1)); + IntToTimeDeltaVector(expected_compositor2, + std::vector<int>{465, 500, 90, 720, 410, 742, 390}); + base::TimeDelta expected_total2 = base::Microseconds(5356); + CompositorFrameReporter::EventLatencyInfo actual_predictions2 = + CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages, + kNumOfCompositorStages); + IntToTimeDeltaVector(actual_predictions2.dispatch_durations, + std::vector<int>{250, 300, 450, 200, 500}); + actual_predictions2.transition_duration = base::Microseconds(420); + IntToTimeDeltaVector(actual_predictions2.compositor_durations, + std::vector<int>{520, 500, 90, 720, 410, 890, 420}); + pipeline_reporter_->CalculateEventLatencyPrediction( + actual_predictions2, kLatencyPredictionDeviationThreshold); + + // Test with some previous stage predictions. + std::vector<base::TimeDelta> expected_dispatch3(kNumDispatchStages, + base::Microseconds(-1)); + IntToTimeDeltaVector(expected_dispatch3, + std::vector<int>{375, 450, 300, 300, 300}); + base::TimeDelta expected_transition3 = base::Microseconds(270); + std::vector<base::TimeDelta> expected_compositor3(kNumOfCompositorStages, + base::Microseconds(-1)); + IntToTimeDeltaVector(expected_compositor3, + std::vector<int>{300, 500, -1, -1, 410, 742, 390}); + base::TimeDelta expected_total3 = base::Microseconds(4337); + CompositorFrameReporter::EventLatencyInfo actual_predictions3 = + CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages, + kNumOfCompositorStages); + IntToTimeDeltaVector(actual_predictions3.dispatch_durations, + std::vector<int>{400, 500, 300, -1, -1}); + actual_predictions3.transition_duration = base::Microseconds(260); + IntToTimeDeltaVector(actual_predictions3.compositor_durations, + std::vector<int>{-1, 500, -1, -1, 410, 890, 420}); + pipeline_reporter_->CalculateEventLatencyPrediction( + actual_predictions3, kLatencyPredictionDeviationThreshold); + + for (int i = 0; i < kNumDispatchStages; i++) { + EXPECT_EQ(expected_dispatch1[i], actual_predictions1.dispatch_durations[i]); + EXPECT_EQ(expected_dispatch2[i], actual_predictions2.dispatch_durations[i]); + EXPECT_EQ(expected_dispatch3[i], actual_predictions3.dispatch_durations[i]); + } + for (int i = 0; i < kNumOfCompositorStages; i++) { + EXPECT_EQ(expected_compositor1[i], + actual_predictions1.compositor_durations[i]); + EXPECT_EQ(expected_compositor2[i], + actual_predictions2.compositor_durations[i]); + EXPECT_EQ(expected_compositor3[i], + actual_predictions3.compositor_durations[i]); + } + EXPECT_EQ(expected_transition1, actual_predictions1.transition_duration); + EXPECT_EQ(expected_total1, actual_predictions1.total_duration); + EXPECT_EQ(expected_transition2, actual_predictions2.transition_duration); + EXPECT_EQ(expected_total2, actual_predictions2.total_duration); + EXPECT_EQ(expected_transition3, actual_predictions3.transition_duration); + EXPECT_EQ(expected_total3, actual_predictions3.total_duration); + + pipeline_reporter_ = nullptr; +} + +// Tests that when a frame is presented to the user, event latency predictions +// are reported properly for filtered EventTypes. +TEST_F(CompositorFrameReporterTest, EventLatencyMultipleEventTypePredictions) { + std::vector<int> dispatch_times = {300, 300, 300, 300, 300}; + // The prediction should only be updated with the ScrollUpdateType event, + // other EventType metrics should be ignored. + std::unique_ptr<EventMetrics> event_metrics_ptrs[] = { + CreateEventMetrics(ui::ET_TOUCH_PRESSED), + CreateEventMetrics(ui::ET_TOUCH_MOVED), + CreateScrollUpdateEventMetricsWithDispatchTimes( + false, ScrollUpdateEventMetrics::ScrollUpdateType::kContinued, + dispatch_times), + CreateEventMetrics(ui::ET_TOUCH_MOVED)}; + // The last ET_TOUCH_MOVED event above adds 12 us to transition time. + const base::TimeDelta kTouchEventTransition = base::Microseconds(12); + 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))}; + + AdvanceNowByUs(300); + // Total transition time is 312 us because of the extra 3 us from the + // ET_TOUCH_MOVED event. + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame, + Now()); + + // For this test there are only 3 compositor substages updated which reflects + // a more realistic scenario. + + AdvanceNowByUs(300); // kBeginImplFrameToSendBeginMainFrame duration + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame, + Now()); + + AdvanceNowByUs(300); // kEndActivateToSubmitCompositorFrame duration + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType:: + kSubmitCompositorFrameToPresentationCompositorFrame, + Now()); + + // kSubmitCompositorFrameToPresentationCompositorFrame duration + AdvanceNowByUs(300); + pipeline_reporter_->TerminateFrame( + CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame, Now()); + + pipeline_reporter_->AddEventsMetrics(std::move(events_metrics)); + + // Test with no previous stage predictions. + std::vector<base::TimeDelta> expected_dispatch1(kNumDispatchStages, + base::Microseconds(-1)); + IntToTimeDeltaVector(expected_dispatch1, + std::vector<int>{300, 300, 300, 300, 300}); + base::TimeDelta expected_transition1 = + base::Microseconds(300) + kTouchEventTransition; + std::vector<base::TimeDelta> expected_compositor1(kNumOfCompositorStages, + base::Microseconds(-1)); + IntToTimeDeltaVector(expected_compositor1, + std::vector<int>{300, -1, -1, -1, -1, 300, 300}); + base::TimeDelta expected_total1 = + base::Microseconds(2700) + kTouchEventTransition; + CompositorFrameReporter::EventLatencyInfo actual_predictions1 = + CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages, + kNumOfCompositorStages); + pipeline_reporter_->CalculateEventLatencyPrediction( + actual_predictions1, kLatencyPredictionDeviationThreshold); + + // Test with all previous stage predictions. + std::vector<base::TimeDelta> expected_dispatch2(kNumDispatchStages, + base::Microseconds(-1)); + IntToTimeDeltaVector(expected_dispatch2, + std::vector<int>{262, 300, 412, 225, 450}); + base::TimeDelta expected_transition2 = base::Microseconds(393); + std::vector<base::TimeDelta> expected_compositor2(kNumOfCompositorStages, + base::Microseconds(-1)); + IntToTimeDeltaVector(expected_compositor2, + std::vector<int>{465, 500, 90, 720, 410, 742, 390}); + base::TimeDelta expected_total2 = base::Microseconds(5359); + CompositorFrameReporter::EventLatencyInfo actual_predictions2 = + CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages, + kNumOfCompositorStages); + IntToTimeDeltaVector(actual_predictions2.dispatch_durations, + std::vector<int>{250, 300, 450, 200, 500}); + actual_predictions2.transition_duration = base::Microseconds(420); + IntToTimeDeltaVector(actual_predictions2.compositor_durations, + std::vector<int>{520, 500, 90, 720, 410, 890, 420}); + pipeline_reporter_->CalculateEventLatencyPrediction( + actual_predictions2, kLatencyPredictionDeviationThreshold); + + for (int i = 0; i < kNumDispatchStages; i++) { + EXPECT_EQ(expected_dispatch1[i], actual_predictions1.dispatch_durations[i]); + EXPECT_EQ(expected_dispatch2[i], actual_predictions2.dispatch_durations[i]); + } + for (int i = 0; i < kNumOfCompositorStages; i++) { + EXPECT_EQ(expected_compositor1[i], + actual_predictions1.compositor_durations[i]); + EXPECT_EQ(expected_compositor2[i], + actual_predictions2.compositor_durations[i]); + } + EXPECT_EQ(expected_transition1, actual_predictions1.transition_duration); + EXPECT_EQ(expected_total1, actual_predictions1.total_duration); + EXPECT_EQ(expected_transition2, actual_predictions2.transition_duration); + EXPECT_EQ(expected_total2, actual_predictions2.total_duration); + + pipeline_reporter_ = nullptr; +} + +// Tests that when a frame is presented to the user, high latency attribution +// for EventLatency is reported properly for filtered EventTypes. +TEST_F(CompositorFrameReporterTest, EventLatencyAttributionPredictions) { + std::vector<int> dispatch_times = {300, 300, 300, 50000, 300}; + // The prediction should only be updated with the ScrollUpdateType event, + // other EventType metrics should be ignored. + std::unique_ptr<EventMetrics> event_metrics_ptrs[] = { + CreateScrollUpdateEventMetricsWithDispatchTimes( + false, ScrollUpdateEventMetrics::ScrollUpdateType::kContinued, + dispatch_times)}; + 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))}; + + AdvanceNowByUs(300); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame, + Now()); + + // For this test there are only 3 compositor substages updated which reflects + // a more realistic scenario. + + AdvanceNowByUs(300); // kBeginImplFrameToSendBeginMainFrame duration + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame, + Now()); + + AdvanceNowByUs(50000); // kEndActivateToSubmitCompositorFrame duration + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType:: + kSubmitCompositorFrameToPresentationCompositorFrame, + Now()); + + // kSubmitCompositorFrameToPresentationCompositorFrame duration + AdvanceNowByUs(300); + pipeline_reporter_->TerminateFrame( + CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame, Now()); + + pipeline_reporter_->AddEventsMetrics(std::move(events_metrics)); + + // Test with no high latency attribution. + CompositorFrameReporter::EventLatencyInfo expected_predictions1 = + CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages, + kNumOfCompositorStages); + IntToTimeDeltaVector(expected_predictions1.dispatch_durations, + std::vector<int>{300, 300, 300, 50000, 300}); + expected_predictions1.transition_duration = base::Microseconds(300); + IntToTimeDeltaVector(expected_predictions1.compositor_durations, + std::vector<int>{300, -1, -1, -1, -1, 50000, 300}); + expected_predictions1.total_duration = base::Microseconds(102100); + + CompositorFrameReporter::EventLatencyInfo actual_predictions1 = + CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages, + kNumOfCompositorStages); + pipeline_reporter_->CalculateEventLatencyPrediction( + actual_predictions1, kLatencyPredictionDeviationThreshold); + + std::unique_ptr<EventMetrics>& event_metrics = + pipeline_reporter_->events_metrics_for_testing()[0]; + std::vector<std::string> attribution = event_metrics->GetHighLatencyStages(); + EXPECT_EQ(0, (int)attribution.size()); + event_metrics->ClearHighLatencyStagesForTesting(); + + // Test with 1 high latency stage attributed. + CompositorFrameReporter::EventLatencyInfo expected_predictions2 = + CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages, + kNumOfCompositorStages); + IntToTimeDeltaVector(expected_predictions2.dispatch_durations, + std::vector<int>{300, 300, 300, 12725, 300}); + expected_predictions2.transition_duration = base::Microseconds(300); + IntToTimeDeltaVector(expected_predictions2.compositor_durations, + std::vector<int>{300, -1, -1, -1, -1, 50000, 300}); + expected_predictions2.total_duration = base::Microseconds(64825); + + CompositorFrameReporter::EventLatencyInfo actual_predictions2 = + CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages, + kNumOfCompositorStages); + IntToTimeDeltaVector(actual_predictions2.dispatch_durations, + std::vector<int>{300, 300, 300, 300, 300}); + actual_predictions2.transition_duration = base::Microseconds(300); + IntToTimeDeltaVector(actual_predictions2.compositor_durations, + std::vector<int>{300, -1, -1, -1, -1, 50000, 300}); + actual_predictions2.total_duration = base::Microseconds(52400); + pipeline_reporter_->CalculateEventLatencyPrediction( + actual_predictions2, kLatencyPredictionDeviationThreshold); + + attribution = event_metrics->GetHighLatencyStages(); + EXPECT_EQ(1, (int)attribution.size()); + EXPECT_EQ("RendererCompositorToMain", attribution[0]); + event_metrics->ClearHighLatencyStagesForTesting(); + + // Test with more than 1 high latency stage attributed. + CompositorFrameReporter::EventLatencyInfo expected_predictions3 = + CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages, + kNumOfCompositorStages); + IntToTimeDeltaVector(expected_predictions3.dispatch_durations, + std::vector<int>{300, 300, 300, 12725, 300}); + expected_predictions3.transition_duration = base::Microseconds(300); + IntToTimeDeltaVector(expected_predictions3.compositor_durations, + std::vector<int>{300, -1, -1, -1, -1, 12725, 300}); + expected_predictions3.total_duration = base::Microseconds(27550); + + CompositorFrameReporter::EventLatencyInfo actual_predictions3 = + CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages, + kNumOfCompositorStages); + IntToTimeDeltaVector(actual_predictions3.dispatch_durations, + std::vector<int>{300, 300, 300, 300, 300}); + actual_predictions3.transition_duration = base::Microseconds(300); + IntToTimeDeltaVector(actual_predictions3.compositor_durations, + std::vector<int>{300, -1, -1, -1, -1, 300, 300}); + actual_predictions3.total_duration = base::Microseconds(2700); + pipeline_reporter_->CalculateEventLatencyPrediction( + actual_predictions3, kLatencyPredictionDeviationThreshold); + + attribution = event_metrics->GetHighLatencyStages(); + EXPECT_EQ(2, (int)attribution.size()); + EXPECT_EQ("RendererCompositorToMain", attribution[0]); + EXPECT_EQ("EndActivateToSubmitCompositorFrame", attribution[1]); + + // Check that all prediction values are accurate. + for (int i = 0; i < kNumDispatchStages; i++) { + EXPECT_EQ(expected_predictions1.dispatch_durations[i], + actual_predictions1.dispatch_durations[i]); + EXPECT_EQ(expected_predictions2.dispatch_durations[i], + actual_predictions2.dispatch_durations[i]); + EXPECT_EQ(expected_predictions3.dispatch_durations[i], + actual_predictions3.dispatch_durations[i]); + } + for (int i = 0; i < kNumOfCompositorStages; i++) { + EXPECT_EQ(expected_predictions1.compositor_durations[i], + actual_predictions1.compositor_durations[i]); + EXPECT_EQ(expected_predictions2.compositor_durations[i], + actual_predictions2.compositor_durations[i]); + EXPECT_EQ(expected_predictions3.compositor_durations[i], + actual_predictions3.compositor_durations[i]); + } + EXPECT_EQ(expected_predictions1.transition_duration, + actual_predictions1.transition_duration); + EXPECT_EQ(expected_predictions1.total_duration, + actual_predictions1.total_duration); + EXPECT_EQ(expected_predictions2.transition_duration, + actual_predictions2.transition_duration); + EXPECT_EQ(expected_predictions2.total_duration, + actual_predictions2.total_duration); + EXPECT_EQ(expected_predictions3.transition_duration, + actual_predictions3.transition_duration); + EXPECT_EQ(expected_predictions3.total_duration, + actual_predictions3.total_duration); + + pipeline_reporter_ = nullptr; +} + +// Tests that when a frame is presented to the user, high latency attribution +// for EventLatency is reported properly for filtered EventTypes. +TEST_F(CompositorFrameReporterTest, EventLatencyAttributionChangePredictions) { + std::vector<int> dispatch_times = {40000, -1, -1, 300, 50000}; + // The prediction should only be updated with the ScrollUpdateType event, + // other EventType metrics should be ignored. + std::unique_ptr<EventMetrics> event_metrics_ptrs[] = { + CreateScrollUpdateEventMetricsWithDispatchTimes( + false, ScrollUpdateEventMetrics::ScrollUpdateType::kContinued, + dispatch_times)}; + 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))}; + + AdvanceNowByUs(300); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame, + Now()); + + // For this test there are only 3 compositor substages updated which reflects + // a more realistic scenario. + + AdvanceNowByUs(300); // kBeginImplFrameToSendBeginMainFrame duration + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame, + Now()); + + AdvanceNowByUs(50000); // kEndActivateToSubmitCompositorFrame duration + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType:: + kSubmitCompositorFrameToPresentationCompositorFrame, + Now()); + + // kSubmitCompositorFrameToPresentationCompositorFrame duration + AdvanceNowByUs(300); + pipeline_reporter_->TerminateFrame( + CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame, Now()); + + pipeline_reporter_->AddEventsMetrics(std::move(events_metrics)); + + // Test 1 + CompositorFrameReporter::EventLatencyInfo expected_predictions1 = + CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages, + kNumOfCompositorStages); + IntToTimeDeltaVector(expected_predictions1.dispatch_durations, + std::vector<int>{10300, -1, -1, 300, 42500}); + expected_predictions1.transition_duration = base::Microseconds(300); + IntToTimeDeltaVector(expected_predictions1.compositor_durations, + std::vector<int>{300, -1, -1, -1, -1, 15200, 300}); + expected_predictions1.total_duration = base::Microseconds(69200); + + CompositorFrameReporter::EventLatencyInfo actual_predictions1 = + CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages, + kNumOfCompositorStages); + IntToTimeDeltaVector(actual_predictions1.dispatch_durations, + std::vector<int>{400, -1, -1, 300, 40000}); + actual_predictions1.transition_duration = base::Microseconds(300); + IntToTimeDeltaVector(actual_predictions1.compositor_durations, + std::vector<int>{300, -1, -1, -1, -1, 3600, 300}); + actual_predictions1.total_duration = base::Microseconds(45200); + pipeline_reporter_->CalculateEventLatencyPrediction( + actual_predictions1, kLatencyPredictionDeviationThreshold); + + std::unique_ptr<EventMetrics>& event_metrics = + pipeline_reporter_->events_metrics_for_testing()[0]; + std::vector<std::string> attribution = event_metrics->GetHighLatencyStages(); + EXPECT_EQ(2, (int)attribution.size()); + EXPECT_EQ("GenerationToRendererCompositor", attribution[0]); + EXPECT_EQ("EndActivateToSubmitCompositorFrame", attribution[1]); + event_metrics->ClearHighLatencyStagesForTesting(); + + // Test 2 + CompositorFrameReporter::EventLatencyInfo expected_predictions2 = + CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages, + kNumOfCompositorStages); + IntToTimeDeltaVector(expected_predictions2.dispatch_durations, + std::vector<int>{10225, -1, -1, 300, 12725}); + expected_predictions2.transition_duration = base::Microseconds(300); + + IntToTimeDeltaVector(expected_predictions2.compositor_durations, + std::vector<int>{300, -1, -1, -1, -1, 12725, 300}); + expected_predictions2.total_duration = base::Microseconds(36875); + + CompositorFrameReporter::EventLatencyInfo actual_predictions2 = + CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages, + kNumOfCompositorStages); + IntToTimeDeltaVector(actual_predictions2.dispatch_durations, + std::vector<int>{300, -1, -1, 300, 300}); + actual_predictions2.transition_duration = base::Microseconds(300); + IntToTimeDeltaVector(actual_predictions2.compositor_durations, + std::vector<int>{300, -1, -1, -1, -1, 300, 300}); + actual_predictions2.total_duration = base::Microseconds(2100); + pipeline_reporter_->CalculateEventLatencyPrediction( + actual_predictions2, kLatencyPredictionDeviationThreshold); + + attribution = event_metrics->GetHighLatencyStages(); + EXPECT_EQ(2, (int)attribution.size()); + EXPECT_EQ("RendererMainProcessing", attribution[0]); + EXPECT_EQ("EndActivateToSubmitCompositorFrame", attribution[1]); + + // Check that all prediction values are accurate. + for (int i = 0; i < kNumDispatchStages; i++) { + EXPECT_EQ(expected_predictions1.dispatch_durations[i], + actual_predictions1.dispatch_durations[i]); + EXPECT_EQ(expected_predictions2.dispatch_durations[i], + actual_predictions2.dispatch_durations[i]); + } + for (int i = 0; i < kNumOfCompositorStages; i++) { + EXPECT_EQ(expected_predictions1.compositor_durations[i], + actual_predictions1.compositor_durations[i]); + EXPECT_EQ(expected_predictions2.compositor_durations[i], + actual_predictions2.compositor_durations[i]); + } + EXPECT_EQ(expected_predictions1.transition_duration, + actual_predictions1.transition_duration); + EXPECT_EQ(expected_predictions1.total_duration, + actual_predictions1.total_duration); + EXPECT_EQ(expected_predictions2.transition_duration, + actual_predictions2.transition_duration); + EXPECT_EQ(expected_predictions2.total_duration, + actual_predictions2.total_duration); + + pipeline_reporter_ = nullptr; +} + } // namespace } // namespace cc diff --git a/chromium/cc/metrics/compositor_frame_reporting_controller.cc b/chromium/cc/metrics/compositor_frame_reporting_controller.cc index c40ffcee3b5..b05992c7363 100644 --- a/chromium/cc/metrics/compositor_frame_reporting_controller.cc +++ b/chromium/cc/metrics/compositor_frame_reporting_controller.cc @@ -23,6 +23,12 @@ using StageType = CompositorFrameReporter::StageType; using FrameTerminationStatus = CompositorFrameReporter::FrameTerminationStatus; constexpr char kTraceCategory[] = "cc,benchmark"; +constexpr int kNumOfCompositorStages = + static_cast<int>(StageType::kStageTypeCount) - 1; +constexpr int kNumDispatchStages = + static_cast<int>(EventMetrics::DispatchStage::kMaxValue); +constexpr base::TimeDelta kDefaultLatencyPredictionDeviationThreshold = + viz::BeginFrameArgs::DefaultInterval() / 2; } // namespace CompositorFrameReportingController::CompositorFrameReportingController( @@ -31,7 +37,12 @@ CompositorFrameReportingController::CompositorFrameReportingController( int layer_tree_host_id) : should_report_histograms_(should_report_histograms), layer_tree_host_id_(layer_tree_host_id), - latency_ukm_reporter_(std::make_unique<LatencyUkmReporter>()) { + latency_ukm_reporter_(std::make_unique<LatencyUkmReporter>()), + previous_latency_predictions_main_(base::Microseconds(-1)), + previous_latency_predictions_impl_(base::Microseconds(-1)), + event_latency_predictions_( + CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages, + kNumOfCompositorStages)) { if (should_report_ukm) { // UKM metrics should be reported if and only if `latency_ukm_reporter` is // set on `global_trackers_`. @@ -316,6 +327,7 @@ void CompositorFrameReportingController::DidSubmitCompositorFrame( main_reporter->AddEventsMetrics( std::move(events_metrics.main_event_metrics)); main_reporter->set_has_missing_content(has_missing_content); + main_reporter->set_reporter_type_to_main(); submitted_compositor_frames_.emplace_back(frame_token, std::move(main_reporter)); } @@ -330,6 +342,7 @@ void CompositorFrameReportingController::DidSubmitCompositorFrame( impl_reporter->set_has_missing_content(has_missing_content); impl_reporter->set_is_accompanied_by_main_thread_update( is_activated_frame_new); + impl_reporter->set_reporter_type_to_impl(); submitted_compositor_frames_.emplace_back(frame_token, std::move(impl_reporter)); } @@ -471,6 +484,23 @@ void CompositorFrameReportingController::DidPresentCompositorFrame( reporter->TerminateFrame(termination_status, details.presentation_feedback.timestamp); + base::TimeDelta latency_prediction_deviation_threshold = + details.presentation_feedback.interval.is_zero() + ? kDefaultLatencyPredictionDeviationThreshold + : (details.presentation_feedback.interval) / 2; + switch (reporter->get_reporter_type()) { + case CompositorFrameReporter::ReporterType::kImpl: + reporter->CalculateCompositorLatencyPrediction( + previous_latency_predictions_impl_, + latency_prediction_deviation_threshold); + break; + case CompositorFrameReporter::ReporterType::kMain: + reporter->CalculateCompositorLatencyPrediction( + previous_latency_predictions_main_, + latency_prediction_deviation_threshold); + break; + } + if (termination_status == FrameTerminationStatus::kPresentedFrame) { // If there are outstanding metrics from dropped frames older than this // frame, this frame would be the first frame presented after those @@ -487,6 +517,11 @@ void CompositorFrameReportingController::DidPresentCompositorFrame( reporter->AddEventsMetrics(std::move(it->second)); } + // TODO(crbug.com/1334827): Consider using a separate container to + // differentiate event predictions with and without a main dispatch stage. + reporter->CalculateEventLatencyPrediction( + event_latency_predictions_, latency_prediction_deviation_threshold); + // For presented frames, if `reporter` was cloned from another reporter, // and the original reporter is still alive, then check whether the cloned // reporter has a 'partial update decider'. It is still possible for the @@ -743,6 +778,7 @@ void CompositorFrameReportingController::CreateReportersForDroppedFrames( timestamp); reporter->TerminateFrame(FrameTerminationStatus::kDidNotPresentFrame, args.deadline); + reporter->set_is_backfill(true); } } diff --git a/chromium/cc/metrics/compositor_frame_reporting_controller.h b/chromium/cc/metrics/compositor_frame_reporting_controller.h index 3d1640c395c..321e4922095 100644 --- a/chromium/cc/metrics/compositor_frame_reporting_controller.h +++ b/chromium/cc/metrics/compositor_frame_reporting_controller.h @@ -236,6 +236,15 @@ class CC_EXPORT CompositorFrameReportingController { // interval of last begin frame args. base::TimeDelta last_interval_; + + CompositorFrameReporter::CompositorLatencyInfo + previous_latency_predictions_main_; + CompositorFrameReporter::CompositorLatencyInfo + previous_latency_predictions_impl_; + + // Container that stores the EventLatency stage latency predictions based on + // previous event traces. + CompositorFrameReporter::EventLatencyInfo event_latency_predictions_; }; } // namespace cc diff --git a/chromium/cc/metrics/compositor_timing_history.cc b/chromium/cc/metrics/compositor_timing_history.cc index a9c16c24319..b949c6937da 100644 --- a/chromium/cc/metrics/compositor_timing_history.cc +++ b/chromium/cc/metrics/compositor_timing_history.cc @@ -30,11 +30,6 @@ class CompositorTimingHistory::UMAReporter { // Latency measurements virtual void AddBeginImplFrameLatency(base::TimeDelta delta) = 0; - virtual void AddCommitToReadyToActivateDuration(base::TimeDelta duration, - TreePriority priority) = 0; - virtual void AddInvalidationToReadyToActivateDuration( - base::TimeDelta duration, - TreePriority priority) = 0; virtual void AddDrawDuration(base::TimeDelta duration) = 0; // crbug.com/758439: the following functions are used to report timing in @@ -346,21 +341,6 @@ class RendererUMAReporter : public CompositorTimingHistory::UMAReporter { "Scheduling.Renderer.BeginImplFrameLatency", delta); } - void AddCommitToReadyToActivateDuration(base::TimeDelta duration, - TreePriority priority) override { - UMA_HISTOGRAM_READY_TO_ACTIVATE( - "Scheduling.Renderer.CommitToReadyToActivateDuration", duration, - priority); - } - - void AddInvalidationToReadyToActivateDuration( - base::TimeDelta duration, - TreePriority priority) override { - UMA_HISTOGRAM_READY_TO_ACTIVATE( - "Scheduling.Renderer.InvalidationToReadyToActivateDuration", duration, - priority); - } - void AddDrawDuration(base::TimeDelta duration) override { UMA_HISTOGRAM_CUSTOM_TIMES_DURATION("Scheduling.Renderer.DrawDuration", duration); @@ -389,21 +369,6 @@ class BrowserUMAReporter : public CompositorTimingHistory::UMAReporter { "Scheduling.Browser.BeginImplFrameLatency", delta); } - void AddCommitToReadyToActivateDuration(base::TimeDelta duration, - TreePriority priority) override { - UMA_HISTOGRAM_READY_TO_ACTIVATE( - "Scheduling.Browser.CommitToReadyToActivateDuration", duration, - priority); - } - - void AddInvalidationToReadyToActivateDuration( - base::TimeDelta duration, - TreePriority priority) override { - UMA_HISTOGRAM_READY_TO_ACTIVATE( - "Scheduling.Browser.InvalidationToReadyToActivateDuration", duration, - priority); - } - void AddDrawDuration(base::TimeDelta duration) override { UMA_HISTOGRAM_CUSTOM_TIMES_DURATION("Scheduling.Browser.DrawDuration", duration); @@ -423,11 +388,6 @@ class NullUMAReporter : public CompositorTimingHistory::UMAReporter { void AddDrawIntervalWithCustomPropertyAnimations( base::TimeDelta inverval) override {} void AddBeginImplFrameLatency(base::TimeDelta delta) override {} - void AddCommitToReadyToActivateDuration(base::TimeDelta duration, - TreePriority priority) override {} - void AddInvalidationToReadyToActivateDuration( - base::TimeDelta duration, - TreePriority priority) override {} void AddDrawDuration(base::TimeDelta duration) override {} void AddImplFrameDeadlineType( CompositorTimingHistory::DeadlineMode deadline_mode) override {} @@ -740,12 +700,7 @@ void CompositorTimingHistory::ReadyToActivate() { DCHECK_EQ(pending_tree_ready_to_activate_time_, base::TimeTicks()); pending_tree_ready_to_activate_time_ = Now(); - if (pending_tree_is_impl_side_) { - base::TimeDelta time_since_invalidation = - pending_tree_ready_to_activate_time_ - pending_tree_creation_time_; - uma_reporter_->AddInvalidationToReadyToActivateDuration( - time_since_invalidation, tree_priority_); - } else { + if (!pending_tree_is_impl_side_) { base::TimeDelta time_since_commit = pending_tree_ready_to_activate_time_ - pending_tree_creation_time_; @@ -755,8 +710,6 @@ void CompositorTimingHistory::ReadyToActivate() { base::TimeDelta commit_to_ready_to_activate_estimate = CommitToReadyToActivateDurationEstimate(); - uma_reporter_->AddCommitToReadyToActivateDuration(time_since_commit, - tree_priority_); rendering_stats_instrumentation_->AddCommitToActivateDuration( time_since_commit, commit_to_ready_to_activate_estimate); diff --git a/chromium/cc/metrics/dropped_frame_counter.cc b/chromium/cc/metrics/dropped_frame_counter.cc index 3ad8364c7a0..980fe517478 100644 --- a/chromium/cc/metrics/dropped_frame_counter.cc +++ b/chromium/cc/metrics/dropped_frame_counter.cc @@ -391,7 +391,6 @@ void DroppedFrameCounter::ReportFrames() { if (sliding_window_max_percent_dropped_After_5_sec_.has_value()) smoothness_data.worst_smoothness_after5sec = sliding_window_max_percent_dropped_After_5_sec_.value(); - smoothness_data.time_max_delta = time_max_delta_; ukm_smoothness_data_->Write(smoothness_data); } } @@ -447,7 +446,6 @@ void DroppedFrameCounter::Reset() { sliding_window_histogram_[SmoothnessStrategy::kCompositorFocusedStrategy] .Clear(); ring_buffer_.Clear(); - time_max_delta_ = {}; last_reported_metrics_ = {}; } @@ -569,10 +567,9 @@ void DroppedFrameCounter::PopSlidingWindow() { sliding_window_histogram_[SmoothnessStrategy::kScrollFocusedStrategy] .AddPercentDroppedFrame(percent_dropped_frame_scroll, count); - if (percent_dropped_frame > sliding_window_max_percent_dropped_) { - time_max_delta_ = newest_args.frame_time - time_fcp_received_; + if (percent_dropped_frame > sliding_window_max_percent_dropped_) sliding_window_max_percent_dropped_ = percent_dropped_frame; - } + sliding_window_current_percent_dropped_ = percent_dropped_frame; latest_sliding_window_start_ = last_timestamp; diff --git a/chromium/cc/metrics/dropped_frame_counter.h b/chromium/cc/metrics/dropped_frame_counter.h index 430f5100b80..52251305302 100644 --- a/chromium/cc/metrics/dropped_frame_counter.h +++ b/chromium/cc/metrics/dropped_frame_counter.h @@ -161,6 +161,10 @@ class CC_EXPORT DroppedFrameCounter { return &sliding_window_histogram_[strategy]; } + double sliding_window_current_percent_dropped() const { + return sliding_window_current_percent_dropped_; + } + private: void NotifyFrameResult(const viz::BeginFrameArgs& args, const FrameInfo& frame_info); @@ -194,7 +198,6 @@ class CC_EXPORT DroppedFrameCounter { absl::optional<double> sliding_window_max_percent_dropped_After_2_sec_; absl::optional<double> sliding_window_max_percent_dropped_After_5_sec_; base::TimeTicks time_fcp_received_; - base::TimeDelta time_max_delta_; raw_ptr<UkmSmoothnessDataShared> ukm_smoothness_data_ = nullptr; FrameSorter frame_sorter_; raw_ptr<TotalFrameCounter> total_counter_ = nullptr; diff --git a/chromium/cc/metrics/event_latency_tracing_recorder.cc b/chromium/cc/metrics/event_latency_tracing_recorder.cc index 57dce582e1f..138dac53796 100644 --- a/chromium/cc/metrics/event_latency_tracing_recorder.cc +++ b/chromium/cc/metrics/event_latency_tracing_recorder.cc @@ -16,10 +16,47 @@ namespace cc { namespace { constexpr char kTracingCategory[] = "cc,benchmark,input"; +constexpr base::TimeDelta high_latency_threshold = base::Milliseconds(90); -// Returns the name of the event dispatch breakdown of EventLatency trace events -// between `start_stage` and `end_stage`. -constexpr const char* GetDispatchBreakdownName( +constexpr perfetto::protos::pbzero::EventLatency::EventType ToProtoEnum( + EventMetrics::EventType event_type) { +#define CASE(event_type, proto_event_type) \ + case EventMetrics::EventType::event_type: \ + return perfetto::protos::pbzero::EventLatency::proto_event_type + switch (event_type) { + CASE(kMousePressed, MOUSE_PRESSED); + CASE(kMouseReleased, MOUSE_RELEASED); + CASE(kMouseWheel, MOUSE_WHEEL); + CASE(kKeyPressed, KEY_PRESSED); + CASE(kKeyReleased, KEY_RELEASED); + CASE(kTouchPressed, TOUCH_PRESSED); + CASE(kTouchReleased, TOUCH_RELEASED); + CASE(kTouchMoved, TOUCH_MOVED); + CASE(kGestureScrollBegin, GESTURE_SCROLL_BEGIN); + CASE(kGestureScrollUpdate, GESTURE_SCROLL_UPDATE); + CASE(kGestureScrollEnd, GESTURE_SCROLL_END); + CASE(kGestureDoubleTap, GESTURE_DOUBLE_TAP); + CASE(kGestureLongPress, GESTURE_LONG_PRESS); + CASE(kGestureLongTap, GESTURE_LONG_TAP); + CASE(kGestureShowPress, GESTURE_SHOW_PRESS); + CASE(kGestureTap, GESTURE_TAP); + CASE(kGestureTapCancel, GESTURE_TAP_CANCEL); + CASE(kGestureTapDown, GESTURE_TAP_DOWN); + CASE(kGestureTapUnconfirmed, GESTURE_TAP_UNCONFIRMED); + CASE(kGestureTwoFingerTap, GESTURE_TWO_FINGER_TAP); + CASE(kFirstGestureScrollUpdate, FIRST_GESTURE_SCROLL_UPDATE); + CASE(kMouseDragged, MOUSE_DRAGGED); + CASE(kGesturePinchBegin, GESTURE_PINCH_BEGIN); + CASE(kGesturePinchEnd, GESTURE_PINCH_END); + CASE(kGesturePinchUpdate, GESTURE_PINCH_UPDATE); + CASE(kInertialGestureScrollUpdate, INERTIAL_GESTURE_SCROLL_UPDATE); + } +} + +} // namespace + +// static +const char* EventLatencyTracingRecorder::GetDispatchBreakdownName( EventMetrics::DispatchStage start_stage, EventMetrics::DispatchStage end_stage) { switch (start_stage) { @@ -53,9 +90,8 @@ constexpr const char* GetDispatchBreakdownName( } } -// Returns the name of EventLatency breakdown between `dispatch_stage` and -// `compositor_stage`. -constexpr const char* GetDispatchToCompositorBreakdownName( +// static +const char* EventLatencyTracingRecorder::GetDispatchToCompositorBreakdownName( EventMetrics::DispatchStage dispatch_stage, CompositorFrameReporter::StageType compositor_stage) { switch (dispatch_stage) { @@ -111,9 +147,8 @@ constexpr const char* GetDispatchToCompositorBreakdownName( } } -// Returns the name of EventLatency breakdown between `dispatch_stage` and -// termination for events not associated with a frame update. -constexpr const char* GetDispatchToTerminationBreakdownName( +// static +const char* EventLatencyTracingRecorder::GetDispatchToTerminationBreakdownName( EventMetrics::DispatchStage dispatch_stage) { switch (dispatch_stage) { case EventMetrics::DispatchStage::kArrivedInRendererCompositor: @@ -132,43 +167,6 @@ constexpr const char* GetDispatchToTerminationBreakdownName( } } -constexpr perfetto::protos::pbzero::EventLatency::EventType ToProtoEnum( - EventMetrics::EventType event_type) { -#define CASE(event_type, proto_event_type) \ - case EventMetrics::EventType::event_type: \ - return perfetto::protos::pbzero::EventLatency::proto_event_type - switch (event_type) { - CASE(kMousePressed, MOUSE_PRESSED); - CASE(kMouseReleased, MOUSE_RELEASED); - CASE(kMouseWheel, MOUSE_WHEEL); - CASE(kKeyPressed, KEY_PRESSED); - CASE(kKeyReleased, KEY_RELEASED); - CASE(kTouchPressed, TOUCH_PRESSED); - CASE(kTouchReleased, TOUCH_RELEASED); - CASE(kTouchMoved, TOUCH_MOVED); - CASE(kGestureScrollBegin, GESTURE_SCROLL_BEGIN); - CASE(kGestureScrollUpdate, GESTURE_SCROLL_UPDATE); - CASE(kGestureScrollEnd, GESTURE_SCROLL_END); - CASE(kGestureDoubleTap, GESTURE_DOUBLE_TAP); - CASE(kGestureLongPress, GESTURE_LONG_PRESS); - CASE(kGestureLongTap, GESTURE_LONG_TAP); - CASE(kGestureShowPress, GESTURE_SHOW_PRESS); - CASE(kGestureTap, GESTURE_TAP); - CASE(kGestureTapCancel, GESTURE_TAP_CANCEL); - CASE(kGestureTapDown, GESTURE_TAP_DOWN); - CASE(kGestureTapUnconfirmed, GESTURE_TAP_UNCONFIRMED); - CASE(kGestureTwoFingerTap, GESTURE_TWO_FINGER_TAP); - CASE(kFirstGestureScrollUpdate, FIRST_GESTURE_SCROLL_UPDATE); - CASE(kMouseDragged, MOUSE_DRAGGED); - CASE(kGesturePinchBegin, GESTURE_PINCH_BEGIN); - CASE(kGesturePinchEnd, GESTURE_PINCH_END); - CASE(kGesturePinchUpdate, GESTURE_PINCH_UPDATE); - CASE(kInertialGestureScrollUpdate, INERTIAL_GESTURE_SCROLL_UPDATE); - } -} - -} // namespace - // static void EventLatencyTracingRecorder::RecordEventLatencyTraceEvent( EventMetrics* event_metrics, @@ -191,6 +189,15 @@ void EventLatencyTracingRecorder::RecordEventLatencyTraceEvent( context.event<perfetto::protos::pbzero::ChromeTrackEvent>(); auto* event_latency = event->set_event_latency(); event_latency->set_event_type(ToProtoEnum(event_metrics->type())); + bool has_high_latency = + (termination_time - generated_timestamp) > high_latency_threshold; + event_latency->set_has_high_latency(has_high_latency); + for (auto stage : event_metrics->GetHighLatencyStages()) { + // TODO(crbug.com/1334827): Consider changing the high_latency_stage + // type from a string to enum type in chrome_track_event.proto, + // similar to event_type. + event_latency->add_high_latency_stage(stage); + } }); // Event dispatch stages. diff --git a/chromium/cc/metrics/event_latency_tracing_recorder.h b/chromium/cc/metrics/event_latency_tracing_recorder.h index 67202ca3d4e..755037195df 100644 --- a/chromium/cc/metrics/event_latency_tracing_recorder.h +++ b/chromium/cc/metrics/event_latency_tracing_recorder.h @@ -15,6 +15,23 @@ class EventMetrics; class EventLatencyTracingRecorder { public: + // Returns the name of the event dispatch breakdown of EventLatency trace + // events between `start_stage` and `end_stage`. + static const char* GetDispatchBreakdownName( + EventMetrics::DispatchStage start_stage, + EventMetrics::DispatchStage end_stage); + + // Returns the name of EventLatency breakdown between `dispatch_stage` and + // `compositor_stage`. + static const char* GetDispatchToCompositorBreakdownName( + EventMetrics::DispatchStage dispatch_stage, + CompositorFrameReporter::StageType compositor_stage); + + // Returns the name of EventLatency breakdown between `dispatch_stage` and + // termination for events not associated with a frame update. + static const char* GetDispatchToTerminationBreakdownName( + EventMetrics::DispatchStage dispatch_stage); + static void RecordEventLatencyTraceEvent( EventMetrics* event_metrics, base::TimeTicks termination_time, diff --git a/chromium/cc/metrics/event_metrics.cc b/chromium/cc/metrics/event_metrics.cc index aff7043c15f..62306ee30a5 100644 --- a/chromium/cc/metrics/event_metrics.cc +++ b/chromium/cc/metrics/event_metrics.cc @@ -6,6 +6,7 @@ #include <algorithm> #include <ostream> +#include <string> #include <utility> #include "base/check.h" @@ -272,6 +273,10 @@ const char* EventMetrics::GetTypeName() const { return kInterestingEvents[static_cast<int>(type_)].name; } +void EventMetrics::SetHighLatencyStage(const std::string& stage) { + high_latency_stages_.push_back(stage); +} + void EventMetrics::SetDispatchStageTimestamp(DispatchStage stage) { DCHECK(dispatch_stage_timestamps_[static_cast<size_t>(stage)].is_null()); diff --git a/chromium/cc/metrics/event_metrics.h b/chromium/cc/metrics/event_metrics.h index b8c43eab201..ce133874abf 100644 --- a/chromium/cc/metrics/event_metrics.h +++ b/chromium/cc/metrics/event_metrics.h @@ -6,6 +6,7 @@ #define CC_METRICS_EVENT_METRICS_H_ #include <memory> +#include <string> #include <vector> #include "base/memory/raw_ptr.h" @@ -106,6 +107,12 @@ class CC_EXPORT EventMetrics { // Returns a string representing event type. const char* GetTypeName() const; + void SetHighLatencyStage(const std::string& stage); + const std::vector<std::string>& GetHighLatencyStages() const { + return high_latency_stages_; + } + void ClearHighLatencyStagesForTesting() { high_latency_stages_.clear(); } + void SetDispatchStageTimestamp(DispatchStage stage); base::TimeTicks GetDispatchStageTimestamp(DispatchStage stage) const; @@ -167,6 +174,8 @@ class CC_EXPORT EventMetrics { EventType type_; + std::vector<std::string> high_latency_stages_; + const raw_ptr<const base::TickClock> tick_clock_; // Timestamps of different stages of event dispatch. Timestamps are set as the diff --git a/chromium/cc/metrics/frame_info.cc b/chromium/cc/metrics/frame_info.cc index 003cc67a9cb..383c7ed69e4 100644 --- a/chromium/cc/metrics/frame_info.cc +++ b/chromium/cc/metrics/frame_info.cc @@ -55,6 +55,10 @@ void FrameInfo::MergeWith(const FrameInfo& other) { // the same BeginFrameArgs. This can trip the DCHECK()s in this function. if (was_merged) return; + if (main_thread_response == MainThreadResponse::kIncluded && + other.main_thread_response == MainThreadResponse::kIncluded) { + return; + } #endif DCHECK(!was_merged); DCHECK(!other.was_merged); diff --git a/chromium/cc/metrics/frame_sequence_metrics.cc b/chromium/cc/metrics/frame_sequence_metrics.cc index 5d3938153e7..ef19c68621a 100644 --- a/chromium/cc/metrics/frame_sequence_metrics.cc +++ b/chromium/cc/metrics/frame_sequence_metrics.cc @@ -451,6 +451,8 @@ void FrameSequenceMetrics::ReportMetrics() { base::LinearHistogram::FactoryGet( GetCheckerboardingHistogramName(type_), 1, 100, 101, base::HistogramBase::kUmaTargetedHistogramFlag)); + ThroughputData::ReportCheckerboardingHistogram( + this, SmoothEffectDrivingThread::kCompositor, checkerboarding_percent); frames_checkerboarded_ = 0; } @@ -680,6 +682,34 @@ int FrameSequenceMetrics::ThroughputData:: return percent; } +void FrameSequenceMetrics::ThroughputData::ReportCheckerboardingHistogram( + FrameSequenceMetrics* metrics, + SmoothEffectDrivingThread thread_type, + int percent) { + const auto sequence_type = metrics->type(); + DCHECK_LT(sequence_type, FrameSequenceTrackerType::kMaxType); + + const bool is_animation = + ShouldReportForAnimation(sequence_type, thread_type); + const bool is_interaction = ShouldReportForInteraction( + metrics->type(), thread_type, metrics->GetEffectiveThread()); + + if (is_animation) { + UMA_HISTOGRAM_PERCENTAGE( + "Graphics.Smoothness.Checkerboarding.AllAnimations", percent); + } + + if (is_interaction) { + UMA_HISTOGRAM_PERCENTAGE( + "Graphics.Smoothness.Checkerboarding.AllInteractions", percent); + } + + if (is_animation || is_interaction) { + UMA_HISTOGRAM_PERCENTAGE("Graphics.Smoothness.Checkerboarding.AllSequences", + percent); + } +} + std::unique_ptr<base::trace_event::TracedValue> FrameSequenceMetrics::ThroughputData::ToTracedValue( const ThroughputData& impl, diff --git a/chromium/cc/metrics/frame_sequence_metrics.h b/chromium/cc/metrics/frame_sequence_metrics.h index 33fa2b8ae30..31759277ec7 100644 --- a/chromium/cc/metrics/frame_sequence_metrics.h +++ b/chromium/cc/metrics/frame_sequence_metrics.h @@ -106,6 +106,11 @@ class CC_EXPORT FrameSequenceMetrics { int metric_index, const ThroughputData& data); + static void ReportCheckerboardingHistogram( + FrameSequenceMetrics* metrics, + FrameInfo::SmoothEffectDrivingThread thread_type, + int percent); + void Merge(const ThroughputData& data) { frames_expected += data.frames_expected; frames_produced += data.frames_produced; diff --git a/chromium/cc/metrics/frame_sequence_tracker_unittest.cc b/chromium/cc/metrics/frame_sequence_tracker_unittest.cc index e1f7b45cad7..106224668c7 100644 --- a/chromium/cc/metrics/frame_sequence_tracker_unittest.cc +++ b/chromium/cc/metrics/frame_sequence_tracker_unittest.cc @@ -433,6 +433,7 @@ TEST_F(FrameSequenceTrackerTest, TestJankWithZeroIntervalInFeedback) { // followed by a non-checkerboard frame. TEST_F(FrameSequenceTrackerTest, CheckerboardingSimple) { CreateNewTracker(); + base::HistogramTester histogram_tester; const uint64_t source_1 = 1; uint64_t sequence_1 = 0; @@ -458,12 +459,21 @@ TEST_F(FrameSequenceTrackerTest, CheckerboardingSimple) { collection_.NotifyFramePresented(frame_token, feedback); EXPECT_EQ(1u, NumberOfFramesCheckerboarded()); + + // ImplThroughput().frames_expected is set to 100 since in ReportMetrics(), + // in order to report checkerboarding histogram, the minimum frames for + // ThroughputMetric is 100. + ImplThroughput().frames_expected = 100u; + ReportMetrics(); + histogram_tester.ExpectTotalCount( + "Graphics.Smoothness.Checkerboarding.AllSequences", 1u); } // Present a single frame with checkerboarding, followed by a non-checkerboard // frame after a few vsyncs. TEST_F(FrameSequenceTrackerTest, CheckerboardingMultipleFrames) { CreateNewTracker(); + base::HistogramTester histogram_tester; const uint64_t source_1 = 1; uint64_t sequence_1 = 0; @@ -489,12 +499,21 @@ TEST_F(FrameSequenceTrackerTest, CheckerboardingMultipleFrames) { collection_.NotifyFramePresented(frame_token, feedback); EXPECT_EQ(3u, NumberOfFramesCheckerboarded()); + + // ImplThroughput().frames_expected is set to 100 since in ReportMetrics(), + // in order to report checkerboarding histogram, the minimum frames for + // ThroughputMetric is 100. + ImplThroughput().frames_expected = 100u; + ReportMetrics(); + histogram_tester.ExpectTotalCount( + "Graphics.Smoothness.Checkerboarding.AllSequences", 1u); } // Present multiple checkerboarded frames, followed by a non-checkerboard // frame. TEST_F(FrameSequenceTrackerTest, MultipleCheckerboardingFrames) { CreateNewTracker(); + base::HistogramTester histogram_tester; const uint32_t kFrames = 3; const uint64_t source_1 = 1; @@ -527,6 +546,14 @@ TEST_F(FrameSequenceTrackerTest, MultipleCheckerboardingFrames) { collection_.NotifyFramePresented(frame_token, feedback); EXPECT_EQ(kFrames, NumberOfFramesCheckerboarded()); + + // ImplThroughput().frames_expected is set to 100 since in ReportMetrics(), + // in order to report checkerboarding histogram, the minimum frames for + // ThroughputMetric is 100. + ImplThroughput().frames_expected = 100u; + ReportMetrics(); + histogram_tester.ExpectTotalCount( + "Graphics.Smoothness.Checkerboarding.AllSequences", 1u); } TEST_F(FrameSequenceTrackerTest, ReportMetrics) { diff --git a/chromium/cc/metrics/ukm_smoothness_data.h b/chromium/cc/metrics/ukm_smoothness_data.h index 8aaedcb22b1..7d43fa20421 100644 --- a/chromium/cc/metrics/ukm_smoothness_data.h +++ b/chromium/cc/metrics/ukm_smoothness_data.h @@ -31,7 +31,6 @@ struct CC_EXPORT UkmSmoothnessData { double percentile_95 = 0.0; double variance = 0.0; double buckets[7] = {0}; - base::TimeDelta time_max_delta = base::Milliseconds(1); double scroll_focused_median = 0.0; double scroll_focused_percentile_95 = 0.0; diff --git a/chromium/cc/metrics/video_playback_roughness_reporter.cc b/chromium/cc/metrics/video_playback_roughness_reporter.cc index 07d3232d1e1..fe8f3d950e4 100644 --- a/chromium/cc/metrics/video_playback_roughness_reporter.cc +++ b/chromium/cc/metrics/video_playback_roughness_reporter.cc @@ -7,6 +7,7 @@ #include <algorithm> #include "base/callback_helpers.h" +#include "base/containers/adapters.h" #include "base/cxx17_backports.h" #include "base/metrics/histogram_macros.h" #include "base/numerics/safe_conversions.h" @@ -84,19 +85,18 @@ void VideoPlaybackRoughnessReporter::FrameSubmitted( void VideoPlaybackRoughnessReporter::FramePresented(TokenType token, base::TimeTicks timestamp, bool reliable_timestamp) { - for (auto it = frames_.rbegin(); it != frames_.rend(); it++) { - FrameInfo& info = *it; - if (token == it->token) { - if (info.decode_time.has_value()) { - auto time_since_decode = timestamp - info.decode_time.value(); + for (auto& frame : base::Reversed(frames_)) { + if (token == frame.token) { + if (frame.decode_time.has_value()) { + auto time_since_decode = timestamp - frame.decode_time.value(); UMA_HISTOGRAM_TIMES("Media.VideoFrameSubmitter", time_since_decode); } if (reliable_timestamp) - info.presentation_time = timestamp; + frame.presentation_time = timestamp; break; } - if (viz::FrameTokenGT(token, it->token)) + if (viz::FrameTokenGT(token, frame.token)) break; } } 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 522c1a6f1b8..2fa65d484fb 100644 --- a/chromium/cc/mojo_embedder/async_layer_tree_frame_sink.cc +++ b/chromium/cc/mojo_embedder/async_layer_tree_frame_sink.cc @@ -117,9 +117,11 @@ void AsyncLayerTreeFrameSink::DetachFromClient() { begin_frame_source_.reset(); synthetic_begin_frame_source_.reset(); client_receiver_.reset(); + // `compositor_frame_sink_ptr_` points to either `compositor_frame_sink_` or + // `compositor_frame_sink_associated_`, so it must be set to nullptr first. + compositor_frame_sink_ptr_ = nullptr; compositor_frame_sink_.reset(); compositor_frame_sink_associated_.reset(); - compositor_frame_sink_ptr_ = nullptr; LayerTreeFrameSink::DetachFromClient(); } 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 53a1e93c220..a2893a801ee 100644 --- a/chromium/cc/mojo_embedder/async_layer_tree_frame_sink.h +++ b/chromium/cc/mojo_embedder/async_layer_tree_frame_sink.h @@ -131,13 +131,14 @@ class CC_MOJO_EMBEDDER_EXPORT AsyncLayerTreeFrameSink // Message pipes that will be bound when BindToClient() is called. UnboundMessagePipes pipes_; - // One of |compositor_frame_sink_| or |compositor_frame_sink_associated_| will - // be bound after calling BindToClient(). |compositor_frame_sink_ptr_| will - // point to message pipe we want to use. - raw_ptr<viz::mojom::CompositorFrameSink> compositor_frame_sink_ptr_ = nullptr; mojo::Remote<viz::mojom::CompositorFrameSink> compositor_frame_sink_; mojo::AssociatedRemote<viz::mojom::CompositorFrameSink> compositor_frame_sink_associated_; + // One of |compositor_frame_sink_| or |compositor_frame_sink_associated_| will + // be bound after calling BindToClient(). |compositor_frame_sink_ptr_| will + // point to message pipe we want to use. It must be declared last and cleared + // first. + raw_ptr<viz::mojom::CompositorFrameSink> compositor_frame_sink_ptr_ = nullptr; mojo::Receiver<viz::mojom::CompositorFrameSinkClient> client_receiver_{this}; THREAD_CHECKER(thread_checker_); diff --git a/chromium/cc/mojom/BUILD.gn b/chromium/cc/mojom/BUILD.gn index fc4ab95cb54..d2a1783867f 100644 --- a/chromium/cc/mojom/BUILD.gn +++ b/chromium/cc/mojom/BUILD.gn @@ -32,6 +32,7 @@ mojom("mojom") { public_deps = [ "//mojo/public/mojom/base", "//services/viz/public/mojom", + "//skia/public/mojom", "//ui/gfx/geometry/mojom", ] diff --git a/chromium/cc/mojom/DEPS b/chromium/cc/mojom/DEPS index 5f69639a07c..f3731eba10f 100644 --- a/chromium/cc/mojom/DEPS +++ b/chromium/cc/mojom/DEPS @@ -1,4 +1,5 @@ include_rules = [ "+mojo/public/cpp/base", "+services/viz/public/cpp/compositing", + "+skia/public/mojom", ] diff --git a/chromium/cc/mojom/render_frame_metadata.mojom b/chromium/cc/mojom/render_frame_metadata.mojom index 3ee3462988e..07112e7ffe3 100644 --- a/chromium/cc/mojom/render_frame_metadata.mojom +++ b/chromium/cc/mojom/render_frame_metadata.mojom @@ -8,6 +8,7 @@ import "mojo/public/mojom/base/time.mojom"; import "services/viz/public/mojom/compositing/local_surface_id.mojom"; import "services/viz/public/mojom/compositing/selection.mojom"; import "services/viz/public/mojom/compositing/vertical_scroll_direction.mojom"; +import "skia/public/mojom/skcolor4f.mojom"; import "ui/gfx/geometry/mojom/geometry.mojom"; // Contains information to assist in making a decision about forwarding @@ -27,7 +28,7 @@ struct RenderFrameMetadata { // The background color of a CompositorFrame. It can be used for filling the // content area if the primary surface is unavailable and fallback is not // specified. - uint32 root_background_color; + skia.mojom.SkColor4f root_background_color; // Scroll offset of the root layer. This optional parameter is only sent // during tests. diff --git a/chromium/cc/mojom/render_frame_metadata_mojom_traits.cc b/chromium/cc/mojom/render_frame_metadata_mojom_traits.cc index 3e4cdf17c6b..8aeffaa74d4 100644 --- a/chromium/cc/mojom/render_frame_metadata_mojom_traits.cc +++ b/chromium/cc/mojom/render_frame_metadata_mojom_traits.cc @@ -8,6 +8,7 @@ #include "mojo/public/cpp/base/time_mojom_traits.h" #include "services/viz/public/cpp/compositing/selection_mojom_traits.h" #include "services/viz/public/cpp/compositing/vertical_scroll_direction_mojom_traits.h" +#include "third_party/skia/include/core/SkColor.h" #include "ui/gfx/geometry/mojom/geometry_mojom_traits.h" #include "ui/gfx/mojom/selection_bound_mojom_traits.h" @@ -27,7 +28,6 @@ bool StructTraits< cc::mojom::RenderFrameMetadataDataView, cc::RenderFrameMetadata>::Read(cc::mojom::RenderFrameMetadataDataView data, cc::RenderFrameMetadata* out) { - 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->device_scale_factor = data.device_scale_factor(); @@ -60,7 +60,8 @@ bool StructTraits< data.ReadPreviousSurfacesVisualUpdateDuration( &out->previous_surfaces_visual_update_duration) && data.ReadCurrentSurfaceVisualUpdateDuration( - &out->current_surface_visual_update_duration); + &out->current_surface_visual_update_duration) && + data.ReadRootBackgroundColor(&out->root_background_color); } } // namespace mojo diff --git a/chromium/cc/mojom/render_frame_metadata_mojom_traits.h b/chromium/cc/mojom/render_frame_metadata_mojom_traits.h index 8631acd1a6e..eec5bed32c9 100644 --- a/chromium/cc/mojom/render_frame_metadata_mojom_traits.h +++ b/chromium/cc/mojom/render_frame_metadata_mojom_traits.h @@ -10,7 +10,9 @@ #include "cc/mojom/render_frame_metadata.mojom-shared.h" #include "cc/trees/render_frame_metadata.h" #include "services/viz/public/cpp/compositing/local_surface_id_mojom_traits.h" +#include "skia/public/mojom/skcolor4f_mojom_traits.h" #include "third_party/abseil-cpp/absl/types/optional.h" +#include "third_party/skia/include/core/SkColor.h" namespace mojo { @@ -31,7 +33,7 @@ template <> struct COMPONENT_EXPORT(CC_SHARED_MOJOM_TRAITS) StructTraits<cc::mojom::RenderFrameMetadataDataView, cc::RenderFrameMetadata> { - static SkColor root_background_color( + static SkColor4f root_background_color( const cc::RenderFrameMetadata& metadata) { return metadata.root_background_color; } diff --git a/chromium/cc/paint/display_item_list.h b/chromium/cc/paint/display_item_list.h index 0a52e86b7fe..6f7c59f9ec0 100644 --- a/chromium/cc/paint/display_item_list.h +++ b/chromium/cc/paint/display_item_list.h @@ -149,7 +149,9 @@ class CC_PAINT_EXPORT DisplayItemList absl::optional<DirectlyCompositedImageResult> GetDirectlyCompositedImageResult() const; - int num_slow_paths() const { return paint_op_buffer_.num_slow_paths(); } + int num_slow_paths_up_to_min_for_MSAA() const { + return paint_op_buffer_.num_slow_paths_up_to_min_for_MSAA(); + } bool HasNonAAPaint() const { return paint_op_buffer_.HasNonAAPaint(); } // This gives the total number of PaintOps. @@ -173,7 +175,9 @@ class CC_PAINT_EXPORT DisplayItemList void EmitTraceSnapshot() const; void GenerateDiscardableImagesMetadata(); - gfx::Rect VisualRectForTesting(int index) { return visual_rects_[index]; } + gfx::Rect VisualRectForTesting(int index) { + return visual_rects_[static_cast<size_t>(index)]; + } // Generate a PaintRecord from this DisplayItemList, leaving |this| in // an empty state. diff --git a/chromium/cc/paint/display_item_list_unittest.cc b/chromium/cc/paint/display_item_list_unittest.cc index 90608ac939f..894be54150d 100644 --- a/chromium/cc/paint/display_item_list_unittest.cc +++ b/chromium/cc/paint/display_item_list_unittest.cc @@ -75,13 +75,13 @@ class DisplayItemListTest : public testing::Test { } }; -#define EXPECT_TRACED_RECT(x, y, width, height, rect_list) \ - do { \ - ASSERT_EQ(4u, rect_list->GetListDeprecated().size()); \ - EXPECT_EQ(x, rect_list->GetListDeprecated()[0].GetIfDouble()); \ - EXPECT_EQ(y, rect_list->GetListDeprecated()[1].GetIfDouble()); \ - EXPECT_EQ(width, rect_list->GetListDeprecated()[2].GetIfDouble()); \ - EXPECT_EQ(height, rect_list->GetListDeprecated()[3].GetIfDouble()); \ +#define EXPECT_TRACED_RECT(x, y, width, height, rect_list) \ + do { \ + ASSERT_EQ(4u, rect_list->size()); \ + EXPECT_EQ(x, (*rect_list)[0].GetIfDouble()); \ + EXPECT_EQ(y, (*rect_list)[1].GetIfDouble()); \ + EXPECT_EQ(width, (*rect_list)[2].GetIfDouble()); \ + EXPECT_EQ(height, (*rect_list)[3].GetIfDouble()); \ } while (false) // AddToValue should not crash if there are different numbers of visual_rect @@ -106,34 +106,35 @@ TEST_F(DisplayItemListTest, TraceEmptyVisualRect) { // Pass: we don't crash std::unique_ptr<base::Value> root = ToBaseValue(list.get(), true); - const base::DictionaryValue* root_dict; - ASSERT_TRUE(root->GetAsDictionary(&root_dict)); - const base::DictionaryValue* params_dict; - ASSERT_TRUE(root_dict->GetDictionary("params", ¶ms_dict)); - const base::ListValue* items; - ASSERT_TRUE(params_dict->GetList("items", &items)); - ASSERT_EQ(2u, items->GetListDeprecated().size()); - - const base::Value* item_value; - const base::DictionaryValue* item_dict; - const base::ListValue* visual_rect; - std::string name; - - item_value = &items->GetListDeprecated()[0]; - ASSERT_TRUE(item_value->is_dict()); - item_dict = &base::Value::AsDictionaryValue(*item_value); - ASSERT_TRUE(item_dict->GetList("visual_rect", &visual_rect)); + const base::Value::Dict* root_dict = root->GetIfDict(); + ASSERT_NE(nullptr, root_dict); + const base::Value::Dict* params_dict = root_dict->FindDict("params"); + ASSERT_NE(nullptr, params_dict); + const base::Value::List* items = params_dict->FindList("items"); + ASSERT_NE(nullptr, items); + ASSERT_EQ(2u, items->size()); + + const base::Value::Dict* item_dict; + const base::Value::List* visual_rect; + const std::string* name; + + item_dict = ((*items)[0]).GetIfDict(); + ASSERT_NE(nullptr, item_dict); + visual_rect = item_dict->FindList("visual_rect"); + ASSERT_NE(nullptr, visual_rect); EXPECT_TRACED_RECT(0, 0, 0, 0, visual_rect); - EXPECT_TRUE(item_dict->GetString("name", &name)); - EXPECT_EQ("DrawRect", name); - - item_value = &items->GetListDeprecated()[1]; - ASSERT_TRUE(item_value->is_dict()); - item_dict = &base::Value::AsDictionaryValue(*item_value); - ASSERT_TRUE(item_dict->GetList("visual_rect", &visual_rect)); + name = item_dict->FindString("name"); + ASSERT_NE(nullptr, name); + EXPECT_EQ("DrawRect", *name); + + item_dict = ((*items)[1]).GetIfDict(); + ASSERT_NE(nullptr, item_dict); + visual_rect = item_dict->FindList("visual_rect"); + ASSERT_NE(nullptr, visual_rect); EXPECT_TRACED_RECT(8, 9, 10, 10, visual_rect); - EXPECT_TRUE(item_dict->GetString("name", &name)); - EXPECT_EQ("DrawRect", name); + name = item_dict->FindString("name"); + ASSERT_NE(nullptr, name); + EXPECT_EQ("DrawRect", *name); } TEST_F(DisplayItemListTest, SingleUnpairedRange) { @@ -460,45 +461,50 @@ TEST_F(DisplayItemListTest, AsValueWithNoOps) { // Pass |true| to ask for PaintOps even though there are none. std::unique_ptr<base::Value> root = ToBaseValue(list.get(), true); - const base::DictionaryValue* root_dict; - ASSERT_TRUE(root->GetAsDictionary(&root_dict)); + const base::Value::Dict* root_dict = root->GetIfDict(); + ASSERT_NE(nullptr, root_dict); // The traced value has a params dictionary as its root. { - const base::DictionaryValue* params_dict; - ASSERT_TRUE(root_dict->GetDictionary("params", ¶ms_dict)); + const base::Value::Dict* params_dict = root_dict->FindDict("params"); + ASSERT_NE(nullptr, params_dict); // The real contents of the traced value is in here. { - const base::ListValue* params_list; + const base::Value::List* params_list; // The layer_rect field is present by empty. - ASSERT_TRUE(params_dict->GetList("layer_rect", ¶ms_list)); + params_list = params_dict->FindList("layer_rect"); + ASSERT_NE(nullptr, params_list); EXPECT_TRACED_RECT(0, 0, 0, 0, params_list); // The items list is there but empty. - ASSERT_TRUE(params_dict->GetList("items", ¶ms_list)); - EXPECT_EQ(0u, params_list->GetListDeprecated().size()); + params_list = params_dict->FindList("items"); + ASSERT_NE(nullptr, params_list); + EXPECT_EQ(0u, params_list->size()); } } // Pass |false| to not include PaintOps. root = ToBaseValue(list.get(), false); - ASSERT_TRUE(root->GetAsDictionary(&root_dict)); + root_dict = root->GetIfDict(); + ASSERT_NE(nullptr, root_dict); // The traced value has a params dictionary as its root. { - const base::DictionaryValue* params_dict; - ASSERT_TRUE(root_dict->GetDictionary("params", ¶ms_dict)); + const base::Value::Dict* params_dict = root_dict->FindDict("params"); + ASSERT_NE(nullptr, params_dict); // The real contents of the traced value is in here. { - const base::ListValue* params_list; + const base::Value::List* params_list; // The layer_rect field is present by empty. - ASSERT_TRUE(params_dict->GetList("layer_rect", ¶ms_list)); + params_list = params_dict->FindList("layer_rect"); + ASSERT_NE(nullptr, params_list); EXPECT_TRACED_RECT(0, 0, 0, 0, params_list); // The items list is not there since we asked for no ops. - ASSERT_FALSE(params_dict->GetList("items", ¶ms_list)); + params_list = params_dict->FindList("items"); + ASSERT_EQ(nullptr, params_list); } } } @@ -542,24 +548,25 @@ TEST_F(DisplayItemListTest, AsValueWithOps) { // Pass |true| to ask for PaintOps to be included. std::unique_ptr<base::Value> root = ToBaseValue(list.get(), true); - const base::DictionaryValue* root_dict; - ASSERT_TRUE(root->GetAsDictionary(&root_dict)); + const base::Value::Dict* root_dict = root->GetIfDict(); + ASSERT_NE(nullptr, root_dict); // The traced value has a params dictionary as its root. { - const base::DictionaryValue* params_dict; - ASSERT_TRUE(root_dict->GetDictionary("params", ¶ms_dict)); + const base::Value::Dict* params_dict = root_dict->FindDict("params"); + ASSERT_NE(nullptr, params_dict); // The real contents of the traced value is in here. { - const base::ListValue* layer_rect_list; + const base::Value::List* layer_rect_list = + params_dict->FindList("layer_rect"); // The layer_rect field is present and has the bounds of the rtree. - ASSERT_TRUE(params_dict->GetList("layer_rect", &layer_rect_list)); + ASSERT_NE(nullptr, layer_rect_list); EXPECT_TRACED_RECT(2, 3, 8, 9, layer_rect_list); // The items list has 3 things in it since we built 3 visual rects. - const base::ListValue* items; - ASSERT_TRUE(params_dict->GetList("items", &items)); - ASSERT_EQ(7u, items->GetListDeprecated().size()); + const base::Value::List* items = params_dict->FindList("items"); + ASSERT_NE(nullptr, items); + ASSERT_EQ(7u, items->size()); const char* expected_names[] = {"Save", "Concat", "SaveLayer", "Translate", "DrawRect", "Restore", @@ -567,43 +574,45 @@ TEST_F(DisplayItemListTest, AsValueWithOps) { bool expected_has_skp[] = {false, true, true, true, true, false, false}; for (int i = 0; i < 7; ++i) { - const base::Value& item_value = items->GetListDeprecated()[i]; + const base::Value& item_value = (*items)[i]; ASSERT_TRUE(item_value.is_dict()); - const base::DictionaryValue& item_dict = - base::Value::AsDictionaryValue(item_value); + const base::Value::Dict& item_dict = item_value.GetDict(); - const base::ListValue* visual_rect; - ASSERT_TRUE(item_dict.GetList("visual_rect", &visual_rect)); + const base::Value::List* visual_rect = + item_dict.FindList("visual_rect"); + ASSERT_NE(nullptr, visual_rect); EXPECT_TRACED_RECT(2, 3, 8, 9, visual_rect); - std::string name; - EXPECT_TRUE(item_dict.GetString("name", &name)); - EXPECT_EQ(expected_names[i], name); + const std::string* name = item_dict.FindString("name"); + EXPECT_NE(nullptr, name); + EXPECT_EQ(expected_names[i], *name); - EXPECT_EQ( - expected_has_skp[i], - item_dict.GetString("skp64", static_cast<std::string*>(nullptr))); + EXPECT_EQ(expected_has_skp[i], + item_dict.FindString("skp64") != nullptr); } } } // Pass |false| to not include PaintOps. root = ToBaseValue(list.get(), false); - ASSERT_TRUE(root->GetAsDictionary(&root_dict)); + root_dict = root->GetIfDict(); + ASSERT_NE(nullptr, root_dict); // The traced value has a params dictionary as its root. { - const base::DictionaryValue* params_dict; - ASSERT_TRUE(root_dict->GetDictionary("params", ¶ms_dict)); + const base::Value::Dict* params_dict = root_dict->FindDict("params"); + ASSERT_NE(nullptr, params_dict); // The real contents of the traced value is in here. { - const base::ListValue* params_list; + const base::Value::List* params_list; // The layer_rect field is present and has the bounds of the rtree. - ASSERT_TRUE(params_dict->GetList("layer_rect", ¶ms_list)); + params_list = params_dict->FindList("layer_rect"); + ASSERT_NE(nullptr, params_list); EXPECT_TRACED_RECT(2, 3, 8, 9, params_list); // The items list is not present since we asked for no ops. - ASSERT_FALSE(params_dict->GetList("items", ¶ms_list)); + params_list = params_dict->FindList("items"); + ASSERT_EQ(nullptr, params_list); } } } diff --git a/chromium/cc/paint/filter_operation.cc b/chromium/cc/paint/filter_operation.cc index db70fd2f612..b5a36c2335c 100644 --- a/chromium/cc/paint/filter_operation.cc +++ b/chromium/cc/paint/filter_operation.cc @@ -54,7 +54,7 @@ FilterOperation::FilterOperation(FilterType type, float amount) amount_(amount), outer_threshold_(0), drop_shadow_offset_(0, 0), - drop_shadow_color_(0), + drop_shadow_color_(SkColors::kTransparent), zoom_inset_(0) { DCHECK_NE(type_, DROP_SHADOW); DCHECK_NE(type_, COLOR_MATRIX); @@ -69,7 +69,7 @@ FilterOperation::FilterOperation(FilterType type, amount_(amount), outer_threshold_(0), drop_shadow_offset_(0, 0), - drop_shadow_color_(0), + drop_shadow_color_(SkColors::kTransparent), zoom_inset_(0), blur_tile_mode_(tile_mode) { DCHECK_EQ(type_, BLUR); @@ -79,7 +79,7 @@ FilterOperation::FilterOperation(FilterType type, FilterOperation::FilterOperation(FilterType type, const gfx::Point& offset, float stdDeviation, - SkColor color) + SkColor4f color) : type_(type), amount_(stdDeviation), outer_threshold_(0), @@ -95,7 +95,7 @@ FilterOperation::FilterOperation(FilterType type, const Matrix& matrix) amount_(0), outer_threshold_(0), drop_shadow_offset_(0, 0), - drop_shadow_color_(0), + drop_shadow_color_(SkColors::kTransparent), zoom_inset_(0) { DCHECK_EQ(type_, COLOR_MATRIX); memcpy(matrix_, matrix, sizeof(matrix_)); @@ -106,7 +106,7 @@ FilterOperation::FilterOperation(FilterType type, float amount, int inset) amount_(amount), outer_threshold_(0), drop_shadow_offset_(0, 0), - drop_shadow_color_(0), + drop_shadow_color_(SkColors::kTransparent), zoom_inset_(inset) { DCHECK_EQ(type_, ZOOM); memset(matrix_, 0, sizeof(matrix_)); @@ -119,7 +119,7 @@ FilterOperation::FilterOperation(FilterType type, amount_(amount), outer_threshold_(outer_threshold), drop_shadow_offset_(0, 0), - drop_shadow_color_(0), + drop_shadow_color_(SkColors::kTransparent), zoom_inset_(0) { DCHECK_EQ(type_, STRETCH); memset(matrix_, 0, sizeof(matrix_)); @@ -131,7 +131,7 @@ FilterOperation::FilterOperation(FilterType type, amount_(0), outer_threshold_(0), drop_shadow_offset_(0, 0), - drop_shadow_color_(0), + drop_shadow_color_(SkColors::kTransparent), image_filter_(std::move(image_filter)), zoom_inset_(0) { DCHECK_EQ(type_, REFERENCE); @@ -146,7 +146,7 @@ FilterOperation::FilterOperation(FilterType type, amount_(inner_threshold), outer_threshold_(outer_threshold), drop_shadow_offset_(0, 0), - drop_shadow_color_(0), + drop_shadow_color_(SkColors::kTransparent), zoom_inset_(0), shape_(shape) { DCHECK_EQ(type_, ALPHA_THRESHOLD); @@ -190,7 +190,7 @@ static FilterOperation CreateNoOpFilter(FilterOperation::FilterType type) { return FilterOperation::CreateBlurFilter(0.f); case FilterOperation::DROP_SHADOW: return FilterOperation::CreateDropShadowFilter(gfx::Point(0, 0), 0.f, - SK_ColorTRANSPARENT); + SkColors::kTransparent); case FilterOperation::COLOR_MATRIX: { FilterOperation::Matrix matrix = {}; matrix[0] = matrix[6] = matrix[12] = matrix[18] = 1.f; @@ -318,7 +318,8 @@ void FilterOperation::AsValueInto(base::trace_event::TracedValue* value) const { case FilterOperation::DROP_SHADOW: value->SetDouble("std_deviation", amount_); MathUtil::AddToTracedValue("offset", drop_shadow_offset_, value); - value->SetInteger("color", drop_shadow_color_); + // TODO(crbug/1308932): Remove toSkColor and make all SkColor4f. + value->SetInteger("color", drop_shadow_color_.toSkColor()); break; case FilterOperation::COLOR_MATRIX: { value->BeginArray("matrix"); diff --git a/chromium/cc/paint/filter_operation.h b/chromium/cc/paint/filter_operation.h index 02002920792..9fce89757bd 100644 --- a/chromium/cc/paint/filter_operation.h +++ b/chromium/cc/paint/filter_operation.h @@ -75,7 +75,7 @@ class CC_PAINT_EXPORT FilterOperation { return drop_shadow_offset_; } - SkColor drop_shadow_color() const { + SkColor4f drop_shadow_color() const { DCHECK_EQ(type_, DROP_SHADOW); return drop_shadow_color_; } @@ -145,7 +145,7 @@ class CC_PAINT_EXPORT FilterOperation { static FilterOperation CreateDropShadowFilter(const gfx::Point& offset, float std_deviation, - SkColor color) { + SkColor4f color) { return FilterOperation(DROP_SHADOW, offset, std_deviation, color); } @@ -206,7 +206,7 @@ class CC_PAINT_EXPORT FilterOperation { drop_shadow_offset_ = offset; } - void set_drop_shadow_color(SkColor color) { + void set_drop_shadow_color(SkColor4f color) { DCHECK_EQ(type_, DROP_SHADOW); drop_shadow_color_ = color; } @@ -265,7 +265,7 @@ class CC_PAINT_EXPORT FilterOperation { FilterOperation(FilterType type, const gfx::Point& offset, float stdDeviation, - SkColor color); + SkColor4f color); FilterOperation(FilterType, const Matrix& matrix); @@ -284,7 +284,7 @@ class CC_PAINT_EXPORT FilterOperation { float amount_; float outer_threshold_; gfx::Point drop_shadow_offset_; - SkColor drop_shadow_color_; + SkColor4f drop_shadow_color_; sk_sp<PaintFilter> image_filter_; Matrix matrix_; int zoom_inset_; diff --git a/chromium/cc/paint/filter_operations_unittest.cc b/chromium/cc/paint/filter_operations_unittest.cc index 54fe83569ae..4684ef60780 100644 --- a/chromium/cc/paint/filter_operations_unittest.cc +++ b/chromium/cc/paint/filter_operations_unittest.cc @@ -54,7 +54,7 @@ TEST(FilterOperationsTest, MapRectDropShadowReferenceFilter) { ops.Append( FilterOperation::CreateReferenceFilter(sk_make_sp<DropShadowPaintFilter>( SkIntToScalar(3), SkIntToScalar(8), SkIntToScalar(4), - SkIntToScalar(9), SK_ColorBLACK, + SkIntToScalar(9), SkColors::kBlack, DropShadowPaintFilter::ShadowMode::kDrawShadowAndForeground, nullptr))); EXPECT_EQ(gfx::Rect(-9, -19, 34, 64), @@ -70,7 +70,7 @@ TEST(FilterOperationsTest, MapRectReverseDropShadowReferenceFilter) { ops.Append( FilterOperation::CreateReferenceFilter(sk_make_sp<DropShadowPaintFilter>( SkIntToScalar(3), SkIntToScalar(8), SkIntToScalar(4), - SkIntToScalar(9), SK_ColorBLACK, + SkIntToScalar(9), SkColors::kBlack, DropShadowPaintFilter::ShadowMode::kDrawShadowAndForeground, nullptr))); EXPECT_EQ(gfx::Rect(-15, -35, 34, 64), @@ -171,7 +171,8 @@ TEST(FilterOperationsTest, MapRectReverseNullReferenceFilter) { TEST(FilterOperationsTest, MapRectDropShadow) { FilterOperations ops; - ops.Append(FilterOperation::CreateDropShadowFilter(gfx::Point(3, 8), 20, 0)); + ops.Append(FilterOperation::CreateDropShadowFilter(gfx::Point(3, 8), 20, + SkColors::kTransparent)); EXPECT_EQ(gfx::Rect(-57, -52, 130, 130), ops.MapRect(gfx::Rect(0, 0, 10, 10), SkMatrix::I())); EXPECT_EQ(gfx::Rect(-114, -104, 260, 260), @@ -182,7 +183,8 @@ TEST(FilterOperationsTest, MapRectDropShadow) { TEST(FilterOperationsTest, MapRectReverseDropShadow) { FilterOperations ops; - ops.Append(FilterOperation::CreateDropShadowFilter(gfx::Point(3, 8), 20, 0)); + ops.Append(FilterOperation::CreateDropShadowFilter(gfx::Point(3, 8), 20, + SkColors::kTransparent)); EXPECT_EQ(gfx::Rect(-63, -68, 130, 130), ops.MapRectReverse(gfx::Rect(0, 0, 10, 10), SkMatrix::I())); EXPECT_EQ(gfx::Rect(-126, -136, 260, 260), @@ -196,7 +198,8 @@ TEST(FilterOperationsTest, MapRectDropShadowDoesNotContract) { // Even with a drop-shadow, the original content is still drawn. Thus the // content bounds are never contracted due to a drop-shadow. FilterOperations ops; - ops.Append(FilterOperation::CreateDropShadowFilter(gfx::Point(3, 8), 0, 0)); + ops.Append(FilterOperation::CreateDropShadowFilter(gfx::Point(3, 8), 0, + SkColors::kTransparent)); EXPECT_EQ(gfx::Rect(0, 0, 13, 18), ops.MapRect(gfx::Rect(0, 0, 10, 10), SkMatrix::I())); } @@ -205,7 +208,8 @@ TEST(FilterOperationsTest, MapRectReverseDropShadowDoesNotContract) { // Even with a drop-shadow, the original content is still drawn. Thus the // content bounds are never contracted due to a drop-shadow. FilterOperations ops; - ops.Append(FilterOperation::CreateDropShadowFilter(gfx::Point(3, 8), 0, 0)); + ops.Append(FilterOperation::CreateDropShadowFilter(gfx::Point(3, 8), 0, + SkColors::kTransparent)); EXPECT_EQ(gfx::Rect(-3, -8, 13, 18), ops.MapRectReverse(gfx::Rect(0, 0, 10, 10), SkMatrix::I())); } @@ -324,7 +328,7 @@ TEST(FilterOperationsTest, SaveAndRestore) { SAVE_RESTORE_AMOUNT(Blur, BLUR, 0.6f); SAVE_RESTORE_AMOUNT(SaturatingBrightness, SATURATING_BRIGHTNESS, 0.6f); SAVE_RESTORE_OFFSET_AMOUNT_COLOR(DropShadow, DROP_SHADOW, gfx::Point(3, 4), - 0.4f, 0xffffff00); + 0.4f, SkColors::kYellow); SkScalar matrix[20] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}; @@ -596,43 +600,74 @@ TEST(FilterOperationsTest, BlendBlurWithNull) { TEST(FilterOperationsTest, BlendDropShadowFilters) { FilterOperation from = FilterOperation::CreateDropShadowFilter( - gfx::Point(0, 0), 2.f, SkColorSetARGB(15, 34, 68, 136)); + gfx::Point(0, 0), 2.f, SkColor4f{0.13f, 0.27f, 0.53f, 0.06f}); FilterOperation to = FilterOperation::CreateDropShadowFilter( - gfx::Point(3, 5), 6.f, SkColorSetARGB(51, 30, 60, 120)); + gfx::Point(3, 5), 6.f, SkColor4f{0.12f, 0.24f, 0.47f, 0.2f}); + // In the test below we have to use EXPECT_NEAR as the color contain float for + // the components. In order to properly test the filterOperation we are + // equalizing the color aftewards. FilterOperation blended = FilterOperation::Blend(&from, &to, -0.75); FilterOperation expected = FilterOperation::CreateDropShadowFilter( - gfx::Point(-2, -4), 0.f, SkColorSetARGB(0, 0, 0, 0)); + gfx::Point(-2, -4), 0.f, SkColor4f{0.0f, 0.0f, 0.0f, 0.0f}); + SkColor4f blended_color = blended.drop_shadow_color(); + SkColor4f expected_color = expected.drop_shadow_color(); + EXPECT_NEAR(blended_color.fR, expected_color.fR, 0.0001f); + EXPECT_NEAR(blended_color.fG, expected_color.fG, 0.0001f); + EXPECT_NEAR(blended_color.fB, expected_color.fB, 0.0001f); + EXPECT_NEAR(blended_color.fA, expected_color.fA, 0.0001f); + expected.set_drop_shadow_color(blended_color); EXPECT_EQ(expected, blended); blended = FilterOperation::Blend(&from, &to, 0.25); expected = FilterOperation::CreateDropShadowFilter( - gfx::Point(1, 1), 3.f, SkColorSetARGB(24, 32, 64, 128)); + gfx::Point(1, 1), 3.f, SkColor4f{0.1247f, 0.2542f, 0.4984f, 0.095f}); + blended_color = blended.drop_shadow_color(); + expected_color = expected.drop_shadow_color(); + EXPECT_NEAR(blended_color.fR, expected_color.fR, 0.0001f); + EXPECT_NEAR(blended_color.fG, expected_color.fG, 0.0001f); + EXPECT_NEAR(blended_color.fB, expected_color.fB, 0.0001f); + EXPECT_NEAR(blended_color.fA, expected_color.fA, 0.0001f); + expected.set_drop_shadow_color(blended_color); EXPECT_EQ(expected, blended); blended = FilterOperation::Blend(&from, &to, 0.75); expected = FilterOperation::CreateDropShadowFilter( - gfx::Point(2, 4), 5.f, SkColorSetARGB(42, 30, 61, 121)); + gfx::Point(2, 4), 5.f, SkColor4f{0.1209f, 0.2427f, 0.4755f, 0.1649f}); + blended_color = blended.drop_shadow_color(); + expected_color = expected.drop_shadow_color(); + EXPECT_NEAR(blended_color.fR, expected_color.fR, 0.0001f); + EXPECT_NEAR(blended_color.fG, expected_color.fG, 0.0001f); + EXPECT_NEAR(blended_color.fB, expected_color.fB, 0.0001f); + EXPECT_NEAR(blended_color.fA, expected_color.fA, 0.0001f); + expected.set_drop_shadow_color(blended_color); EXPECT_EQ(expected, blended); blended = FilterOperation::Blend(&from, &to, 1.5); expected = FilterOperation::CreateDropShadowFilter( - gfx::Point(5, 8), 8.f, SkColorSetARGB(69, 30, 59, 118)); + gfx::Point(5, 8), 8.f, SkColor4f{0.1188f, 0.2366f, 0.4633f, 0.27}); + blended_color = blended.drop_shadow_color(); + expected_color = expected.drop_shadow_color(); + EXPECT_NEAR(blended_color.fR, expected_color.fR, 0.0001f); + EXPECT_NEAR(blended_color.fG, expected_color.fG, 0.0001f); + EXPECT_NEAR(blended_color.fB, expected_color.fB, 0.0001f); + EXPECT_NEAR(blended_color.fA, expected_color.fA, 0.0001f); + expected.set_drop_shadow_color(blended_color); EXPECT_EQ(expected, blended); } TEST(FilterOperationsTest, BlendDropShadowWithNull) { FilterOperation filter = FilterOperation::CreateDropShadowFilter( - gfx::Point(4, 4), 4.f, SkColorSetARGB(255, 40, 0, 0)); + gfx::Point(4, 4), 4.f, SkColor4f{0.16f, 0.0f, 0.0f, 1.0f}); FilterOperation blended = FilterOperation::Blend(&filter, nullptr, 0.25); FilterOperation expected = FilterOperation::CreateDropShadowFilter( - gfx::Point(3, 3), 3.f, SkColorSetARGB(191, 40, 0, 0)); + gfx::Point(3, 3), 3.f, SkColor4f{0.16f, 0.0f, 0.0f, 0.75f}); EXPECT_EQ(expected, blended); blended = FilterOperation::Blend(nullptr, &filter, 0.25); expected = FilterOperation::CreateDropShadowFilter( - gfx::Point(1, 1), 1.f, SkColorSetARGB(64, 40, 0, 0)); + gfx::Point(1, 1), 1.f, SkColor4f{0.16f, 0.0f, 0.0f, 0.25f}); EXPECT_EQ(expected, blended); } @@ -934,8 +969,8 @@ TEST(FilterOperationsTest, MaximumPixelMovement) { EXPECT_FLOAT_EQ(20.f * 3, filters.MaximumPixelMovement()); filters.Clear(); - filters.Append( - FilterOperation::CreateDropShadowFilter(gfx::Point(3, -8), 20, 0)); + filters.Append(FilterOperation::CreateDropShadowFilter( + gfx::Point(3, -8), 20, SkColors::kTransparent)); float max_movement = fmax(std::abs(3), std::abs(-8)) + 20.f * 3; EXPECT_FLOAT_EQ(max_movement, filters.MaximumPixelMovement()); diff --git a/chromium/cc/paint/image_transfer_cache_entry.cc b/chromium/cc/paint/image_transfer_cache_entry.cc index 7fdce720233..6c32b6c2981 100644 --- a/chromium/cc/paint/image_transfer_cache_entry.cc +++ b/chromium/cc/paint/image_transfer_cache_entry.cc @@ -209,6 +209,8 @@ size_t TargetColorParamsSize( // Floats for the SDR and HDR maximum luminance. target_color_params_size += sizeof(float); target_color_params_size += sizeof(float); + // uint32_t for tone mapping enabled or disabled. + target_color_params_size += sizeof(uint32_t); } return target_color_params_size; } @@ -222,6 +224,7 @@ void WriteTargetColorParams( writer.Write(target_color_params->color_space.ToSkColorSpace().get()); writer.Write(target_color_params->sdr_max_luminance_nits); writer.Write(target_color_params->hdr_max_luminance_relative); + writer.Write(target_color_params->enable_tone_mapping); } } @@ -244,6 +247,7 @@ bool ReadTargetColorParams( target_color_params->color_space = gfx::ColorSpace(*target_color_space); reader.Read(&target_color_params->sdr_max_luminance_nits); reader.Read(&target_color_params->hdr_max_luminance_relative); + reader.Read(&target_color_params->enable_tone_mapping); return true; } @@ -296,7 +300,7 @@ ClientImageTransferCacheEntry::ClientImageTransferCacheEntry( // 4-byte boundary. safe_size += 4; safe_size += pixmap_->computeByteSize(); - size_ = base::bits::AlignUp(safe_size.ValueOrDefault(0), + size_ = base::bits::AlignUp(size_t{safe_size.ValueOrDefault(0)}, PaintOpWriter::Alignment()); } @@ -346,7 +350,7 @@ ClientImageTransferCacheEntry::ClientImageTransferCacheEntry( safe_size += decoded_color_space_size + align; // SkColorSpace for YUVA image for (size_t i = 0; i < num_yuva_pixmaps; ++i) safe_size += SafeSizeForPixmap(*yuv_pixmaps_->at(i)); - size_ = base::bits::AlignUp(safe_size.ValueOrDefault(0), + size_ = base::bits::AlignUp(size_t{safe_size.ValueOrDefault(0)}, PaintOpWriter::Alignment()); } @@ -586,6 +590,7 @@ bool ServiceImageTransferCacheEntry::Deserialize( image_, target_color_params->color_space.ToSkColorSpace(), target_color_params->sdr_max_luminance_nits, target_color_params->hdr_max_luminance_relative, + target_color_params->enable_tone_mapping, fits_on_gpu_ ? context_ : nullptr); if (!image_) { DLOG(ERROR) << "Failed image color conversion"; diff --git a/chromium/cc/paint/image_transfer_cache_entry_unittest.cc b/chromium/cc/paint/image_transfer_cache_entry_unittest.cc index 817d3115deb..b18eb557135 100644 --- a/chromium/cc/paint/image_transfer_cache_entry_unittest.cc +++ b/chromium/cc/paint/image_transfer_cache_entry_unittest.cc @@ -36,6 +36,7 @@ #include "ui/gl/gl_context_egl.h" #include "ui/gl/gl_share_group.h" #include "ui/gl/gl_surface.h" +#include "ui/gl/gl_utils.h" #include "ui/gl/init/create_gr_gl_interface.h" #include "ui/gl/init/gl_factory.h" @@ -86,7 +87,8 @@ class ImageTransferCacheEntryTest public: void SetUp() override { // Initialize a GL GrContext for Skia. - surface_ = gl::init::CreateOffscreenGLSurface(gfx::Size()); + surface_ = gl::init::CreateOffscreenGLSurface(gl::GetDefaultDisplay(), + gfx::Size()); ASSERT_TRUE(surface_); share_group_ = base::MakeRefCounted<gl::GLShareGroup>(); gl_context_ = base::MakeRefCounted<gl::GLContextEGL>(share_group_.get()); @@ -94,9 +96,9 @@ class ImageTransferCacheEntryTest ASSERT_TRUE( gl_context_->Initialize(surface_.get(), gl::GLContextAttribs())); ASSERT_TRUE(gl_context_->MakeCurrent(surface_.get())); - sk_sp<GrGLInterface> interface(gl::init::CreateGrGLInterface( + sk_sp<GrGLInterface> gl_interface(gl::init::CreateGrGLInterface( *gl_context_->GetVersionInfo(), false /* use_version_es2 */)); - gr_context_ = GrDirectContext::MakeGL(std::move(interface)); + gr_context_ = GrDirectContext::MakeGL(std::move(gl_interface)); ASSERT_TRUE(gr_context_); } diff --git a/chromium/cc/paint/oop_pixeltest.cc b/chromium/cc/paint/oop_pixeltest.cc index 57c8bf8ff07..d438d117ac5 100644 --- a/chromium/cc/paint/oop_pixeltest.cc +++ b/chromium/cc/paint/oop_pixeltest.cc @@ -11,54 +11,41 @@ #include "base/files/file_path.h" #include "base/memory/raw_ptr.h" #include "base/path_service.h" -#include "base/strings/stringprintf.h" #include "base/test/test_switches.h" #include "base/threading/thread_task_runner_handle.h" #include "build/build_config.h" #include "cc/base/completion_event.h" -#include "cc/base/region.h" -#include "cc/layers/recording_source.h" #include "cc/paint/display_item_list.h" #include "cc/paint/paint_filter.h" #include "cc/paint/paint_flags.h" #include "cc/paint/paint_image_builder.h" #include "cc/raster/playback_image_provider.h" -#include "cc/raster/raster_source.h" #include "cc/test/pixel_comparator.h" #include "cc/test/pixel_test_utils.h" #include "cc/tiles/gpu_image_decode_cache.h" #include "components/viz/service/gl/gpu_service_impl.h" +#include "components/viz/test/buildflags.h" #include "components/viz/test/paths.h" #include "components/viz/test/test_gpu_service_holder.h" #include "components/viz/test/test_in_process_context_provider.h" -#include "gpu/GLES2/gl2extchromium.h" -#include "gpu/command_buffer/client/gles2_implementation.h" -#include "gpu/command_buffer/client/gles2_interface.h" #include "gpu/command_buffer/client/raster_implementation.h" -#include "gpu/command_buffer/client/raster_implementation_gles.h" #include "gpu/command_buffer/client/shared_image_interface.h" -#include "gpu/command_buffer/client/shared_memory_limits.h" -#include "gpu/command_buffer/common/context_creation_attribs.h" #include "gpu/command_buffer/common/shared_image_usage.h" #include "gpu/command_buffer/service/gr_shader_cache.h" -#include "gpu/config/gpu_switches.h" -#include "gpu/ipc/gl_in_process_context.h" -#include "gpu/skia_bindings/grcontext_for_gles2_interface.h" #include "ipc/common/gpu_client_ids.h" #include "skia/ext/legacy_display_globals.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/khronos/GLES2/gl2ext.h" +#include "third_party/skia/include/core/SkAlphaType.h" #include "third_party/skia/include/core/SkColor.h" +#include "third_party/skia/include/core/SkColorSpace.h" +#include "third_party/skia/include/core/SkColorType.h" #include "third_party/skia/include/core/SkGraphics.h" #include "third_party/skia/include/core/SkPictureRecorder.h" #include "third_party/skia/include/core/SkRect.h" #include "third_party/skia/include/core/SkSurface.h" #include "third_party/skia/include/core/SkTextBlob.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/GrYUVABackendTextures.h" -#include "ui/gfx/geometry/axis_transform2d.h" #include "ui/gfx/geometry/rect_conversions.h" #include "ui/gfx/geometry/skia_conversions.h" #include "ui/gl/gl_implementation.h" @@ -113,14 +100,7 @@ class OopPixelTest : public testing::Test, public: OopPixelTest() : gr_shader_cache_(kCacheLimitBytes, this) {} - void SetUp() override { - InitializeOOPContext(); - gles2_context_provider_ = - base::MakeRefCounted<viz::TestInProcessContextProvider>( - viz::TestContextType::kGLES2, /*support_locking=*/true); - gpu::ContextResult result = gles2_context_provider_->BindToCurrentThread(); - DCHECK_EQ(result, gpu::ContextResult::kSuccess); - } + void SetUp() override { InitializeOOPContext(); } // gpu::raster::GrShaderCache::Client implementation. void StoreShader(const std::string& key, const std::string& shader) override { @@ -140,8 +120,7 @@ class OopPixelTest : public testing::Test, raster_context_provider_->ContextCapabilities().max_texture_size; oop_image_cache_ = std::make_unique<GpuImageDecodeCache>( raster_context_provider_.get(), true, kRGBA_8888_SkColorType, - kWorkingSetSize, raster_max_texture_size, - PaintImage::GetNextGeneratorClientId(), nullptr); + kWorkingSetSize, raster_max_texture_size, nullptr); } class RasterOptions { @@ -197,7 +176,7 @@ class OopPixelTest : public testing::Test, int height = options.resource_size.height(); // Create and allocate a shared image on the raster interface. - auto* raster_implementation = raster_context_provider_->RasterInterface(); + auto* ri = raster_context_provider_->RasterInterface(); auto* sii = raster_context_provider_->SharedImageInterface(); uint32_t flags = gpu::SHARED_IMAGE_USAGE_RASTER | gpu::SHARED_IMAGE_USAGE_OOP_RASTERIZATION; @@ -206,8 +185,7 @@ class OopPixelTest : public testing::Test, options.target_color_params.color_space, kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, flags, gpu::kNullSurfaceHandle); EXPECT_TRUE(mailbox.Verify()); - raster_implementation->WaitSyncTokenCHROMIUM( - sii->GenUnverifiedSyncToken().GetConstData()); + ri->WaitSyncTokenCHROMIUM(sii->GenUnverifiedSyncToken().GetConstData()); // Assume legacy MSAA if sample count is positive. gpu::raster::MsaaMode msaa_mode = options.msaa_sample_count > 0 @@ -215,20 +193,20 @@ class OopPixelTest : public testing::Test, : gpu::raster::kNoMSAA; if (options.preclear) { - raster_implementation->BeginRasterCHROMIUM( + ri->BeginRasterCHROMIUM( options.preclear_color, /*needs_clear=*/options.preclear, options.msaa_sample_count, msaa_mode, options.use_lcd_text, /*visible=*/true, options.target_color_params.color_space, mailbox.name); - raster_implementation->EndRasterCHROMIUM(); + ri->EndRasterCHROMIUM(); } // "Out of process" raster! \o/ // If |options.preclear| is true, the mailbox has already been cleared by // the BeginRasterCHROMIUM call above, and we want to test that it is indeed // cleared, so set |needs_clear| to false here. - raster_implementation->BeginRasterCHROMIUM( + ri->BeginRasterCHROMIUM( options.background_color, /*needs_clear=*/!options.preclear, options.msaa_sample_count, msaa_mode, options.use_lcd_text, @@ -236,106 +214,70 @@ class OopPixelTest : public testing::Test, mailbox.name); size_t max_op_size_limit = gpu::raster::RasterInterface::kDefaultMaxOpSizeHint; - raster_implementation->RasterCHROMIUM( - display_item_list.get(), &image_provider, options.content_size, - options.full_raster_rect, options.playback_rect, options.post_translate, - gfx::Vector2dF(options.post_scale, options.post_scale), - options.requires_clear, &max_op_size_limit); + ri->RasterCHROMIUM(display_item_list.get(), &image_provider, + options.content_size, options.full_raster_rect, + options.playback_rect, options.post_translate, + gfx::Vector2dF(options.post_scale, options.post_scale), + options.requires_clear, &max_op_size_limit); for (const auto& list : options.additional_lists) { - raster_implementation->RasterCHROMIUM( - list.get(), &image_provider, options.content_size, - options.full_raster_rect, options.playback_rect, - options.post_translate, - gfx::Vector2dF(options.post_scale, options.post_scale), - options.requires_clear, &max_op_size_limit); + ri->RasterCHROMIUM(list.get(), &image_provider, options.content_size, + options.full_raster_rect, options.playback_rect, + options.post_translate, + gfx::Vector2dF(options.post_scale, options.post_scale), + options.requires_clear, &max_op_size_limit); } - raster_implementation->EndRasterCHROMIUM(); - raster_implementation->OrderingBarrierCHROMIUM(); + ri->EndRasterCHROMIUM(); + ri->OrderingBarrierCHROMIUM(); - EXPECT_EQ(raster_implementation->GetError(), - static_cast<unsigned>(GL_NO_ERROR)); + EXPECT_EQ(ri->GetError(), static_cast<unsigned>(GL_NO_ERROR)); - gpu::gles2::GLES2Interface* gl = gles2_context_provider_->ContextGL(); - SkBitmap result = ReadbackMailbox(gl, mailbox, options); + SkBitmap result = ReadbackMailbox(ri, mailbox, options.resource_size); gpu::SyncToken sync_token; - gl->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData()); + ri->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData()); sii->DestroySharedImage(sync_token, mailbox); return result; } - SkBitmap ReadbackMailbox(gpu::gles2::GLES2Interface* gl, + SkBitmap ReadbackMailbox(gpu::raster::RasterInterface* ri, const gpu::Mailbox& mailbox, - const RasterOptions& options) { - // Import the texture in gl, create an fbo and bind the texture to it. - GLuint gl_texture_id = - gl->CreateAndTexStorage2DSharedImageCHROMIUM(mailbox.name); - gl->BeginSharedImageAccessDirectCHROMIUM( - gl_texture_id, GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM); - GLuint fbo_id; - gl->GenFramebuffers(1, &fbo_id); - gl->BindFramebuffer(GL_FRAMEBUFFER, fbo_id); - gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, gl_texture_id, 0); - - // Read the data back. - int width = options.resource_size.width(); - int height = options.resource_size.height(); - std::unique_ptr<unsigned char[]> data( - new unsigned char[width * height * 4]); - gl->ReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data.get()); - - gl->DeleteFramebuffers(1, &fbo_id); - - gl->EndSharedImageAccessDirectCHROMIUM(gl_texture_id); - gl->DeleteTextures(1, &gl_texture_id); - - // Swizzle rgba->bgra - std::vector<SkPMColor> colors; - colors.reserve(width * height); - for (int h = 0; h < height; ++h) { - for (int w = 0; w < width; ++w) { - int i = (h * width + w) * 4; - colors.push_back(SkPreMultiplyARGB(data[i + 3], data[i + 0], - data[i + 1], data[i + 2])); - } - } - - SkBitmap bitmap; - bitmap.allocN32Pixels(width, height); - SkPixmap pixmap(SkImageInfo::MakeN32Premul(width, height), colors.data(), - width * sizeof(SkColor)); - bitmap.writePixels(pixmap); - return bitmap; + const gfx::Size& image_size, + sk_sp<SkColorSpace> color_space = nullptr) { + SkImageInfo image_info = SkImageInfo::MakeN32Premul( + image_size.width(), image_size.height(), color_space); + SkBitmap result; + result.allocPixels(image_info); + ri->ReadbackImagePixels(mailbox, image_info, image_info.minRowBytes(), 0, 0, + result.getPixels()); + return result; } - gpu::Mailbox CreateMailboxSharedImage(gpu::raster::RasterInterface* ri, - gpu::SharedImageInterface* sii, - const RasterOptions& options, - viz::ResourceFormat image_format) { + gpu::Mailbox CreateMailboxSharedImage( + gpu::raster::RasterInterface* ri, + gpu::SharedImageInterface* sii, + const RasterOptions& options, + viz::ResourceFormat image_format, + absl::optional<gfx::ColorSpace> color_space = absl::nullopt) { uint32_t flags = gpu::SHARED_IMAGE_USAGE_RASTER | gpu::SHARED_IMAGE_USAGE_OOP_RASTERIZATION; gpu::Mailbox mailbox = sii->CreateSharedImage( image_format, options.resource_size, - options.target_color_params.color_space, kTopLeft_GrSurfaceOrigin, - kPremul_SkAlphaType, flags, gpu::kNullSurfaceHandle); + color_space.value_or(options.target_color_params.color_space), + kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, flags, + gpu::kNullSurfaceHandle); EXPECT_TRUE(mailbox.Verify()); ri->WaitSyncTokenCHROMIUM(sii->GenUnverifiedSyncToken().GetConstData()); return mailbox; } - void UploadPixels(gpu::gles2::GLES2Interface* gl, + void UploadPixels(gpu::raster::RasterInterface* ri, const gpu::Mailbox& mailbox, - const gfx::Size& size, - GLenum format, - GLenum type, - const void* data) { - GLuint texture = gl->CreateAndTexStorage2DSharedImageCHROMIUM(mailbox.name); - gl->BindTexture(GL_TEXTURE_2D, texture); - gl->TexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, size.width(), size.height(), - format, type, data); - gl->BindTexture(GL_TEXTURE_2D, 0); - gl->DeleteTextures(1, &texture); + const SkImageInfo& info, + const SkBitmap& bitmap) { + ri->WritePixels(mailbox, 0, 0, 0, info.minRowBytes(), info, + bitmap.getPixels()); + ri->OrderingBarrierCHROMIUM(); + EXPECT_EQ(ri->GetError(), static_cast<unsigned>(GL_NO_ERROR)); } // Verifies |actual| matches the expected PNG image. @@ -367,7 +309,6 @@ class OopPixelTest : public testing::Test, protected: static constexpr size_t kWorkingSetSize = 64 * 1024 * 1024; scoped_refptr<viz::TestInProcessContextProvider> raster_context_provider_; - scoped_refptr<viz::TestInProcessContextProvider> gles2_context_provider_; std::unique_ptr<GpuImageDecodeCache> oop_image_cache_; gl::DisableNullDrawGLBindings enable_pixel_output_; std::unique_ptr<ImageProvider> image_provider_; @@ -836,10 +777,7 @@ TEST_F(OopPixelTest, DrawMailboxBackedImage) { ri, sii, options, viz::ResourceFormat::RGBA_8888); ri->OrderingBarrierCHROMIUM(); - auto* gl = gles2_context_provider_->ContextGL(); - UploadPixels(gl, src_mailbox, options.resource_size, GL_RGBA, - GL_UNSIGNED_BYTE, expected_bitmap.getPixels()); - gl->OrderingBarrierCHROMIUM(); + UploadPixels(ri, src_mailbox, expected_bitmap.info(), expected_bitmap); auto src_paint_image = PaintImageBuilder::WithDefault() @@ -1498,7 +1436,8 @@ TEST_F(OopPixelTest, DrawRectQueryMiddleOfDisplayList) { options.post_scale = 2.f; auto actual = Raster(display_item_list, options); - ExpectEquals(actual, FILE_PATH_LITERAL("oop_draw_rect_query.png")); + ExpectEquals(actual, FILE_PATH_LITERAL("oop_draw_rect_query.png"), + FuzzyPixelOffByOneComparator(/*discard_alpha=*/false)); } TEST_F(OopPixelTest, DrawRectColorSpace) { @@ -2140,159 +2079,193 @@ TEST_F(OopPixelTest, WritePixels) { SkImageInfo::MakeN32Premul(dest_size.width(), dest_size.height()), expected_pixels.data(), dest_size.width() * sizeof(SkColor)); - ri->WritePixels(dest_mailbox, 0, 0, 0, expected.info().minRowBytes(), - expected.info(), expected.getPixels()); - ri->OrderingBarrierCHROMIUM(); - EXPECT_EQ(ri->GetError(), static_cast<unsigned>(GL_NO_ERROR)); + UploadPixels(ri, dest_mailbox, expected.info(), expected); - gpu::gles2::GLES2Interface* gl = gles2_context_provider_->ContextGL(); - SkBitmap actual = ReadbackMailbox(gl, dest_mailbox, options); + SkBitmap actual = ReadbackMailbox(ri, dest_mailbox, options.resource_size); gpu::SyncToken sync_token; - gl->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData()); + ri->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData()); sii->DestroySharedImage(sync_token, dest_mailbox); ExpectEquals(actual, expected); } -namespace { -GrBackendTexture MakeBackendTexture(gpu::gles2::GLES2Interface* gl, - const gpu::Mailbox& mailbox, - gfx::Size size, - GLenum type) { - GrGLTextureInfo tex_info = { - GL_TEXTURE_2D, gl->CreateAndTexStorage2DSharedImageCHROMIUM(mailbox.name), - type}; - return GrBackendTexture(size.width(), size.height(), GrMipMapped::kNo, - tex_info); +TEST_F(OopPixelTest, CopySubTexture) { + const gfx::Size size(16, 16); + auto* ri = raster_context_provider_->RasterInterface(); + auto* sii = raster_context_provider_->SharedImageInterface(); + const gfx::ColorSpace source_color_space = gfx::ColorSpace::CreateSRGB(); + const gfx::ColorSpace dest_color_space = + gfx::ColorSpace::CreateDisplayP3D65(); + + // Create data to upload in sRGB (solid green). + SkBitmap upload_bitmap; + { + upload_bitmap.allocPixels(SkImageInfo::MakeN32Premul( + size.width(), size.height(), source_color_space.ToSkColorSpace())); + SkCanvas canvas(upload_bitmap, SkSurfaceProps{}); + SkPaint paint; + paint.setColor(SkColors::kGreen); + canvas.drawRect(SkRect::MakeWH(size.width(), size.height()), paint); + } + + // Create an sRGB SharedImage and upload to it. + gpu::Mailbox source_mailbox; + { + RasterOptions options(size); + options.target_color_params.color_space = source_color_space; + source_mailbox = CreateMailboxSharedImage(ri, sii, options, + viz::ResourceFormat::RGBA_8888); + ri->WaitSyncTokenCHROMIUM(sii->GenUnverifiedSyncToken().GetConstData()); + + ri->WritePixels(source_mailbox, 0, 0, GL_TEXTURE_2D, + upload_bitmap.rowBytes(), upload_bitmap.info(), + upload_bitmap.getPixels()); + } + + // Create a DisplayP3 SharedImage and copy to it. + gpu::Mailbox dest_mailbox; + { + RasterOptions options(size); + options.target_color_params.color_space = dest_color_space; + dest_mailbox = CreateMailboxSharedImage(ri, sii, options, + viz::ResourceFormat::RGBA_8888); + ri->WaitSyncTokenCHROMIUM(sii->GenUnverifiedSyncToken().GetConstData()); + + ri->CopySubTexture(source_mailbox, dest_mailbox, GL_TEXTURE_2D, 0, 0, 0, 0, + size.width(), size.height(), + /*unpack_flip_y=*/GL_FALSE, + /*unpack_premultiply_alpha=*/GL_FALSE); + } + + // Read the data back as DisplayP3, from the Display P3 SharedImage. + SkBitmap readback_bitmap; + { + readback_bitmap.allocPixels(SkImageInfo::MakeN32Premul( + size.width(), size.height(), dest_color_space.ToSkColorSpace())); + + ri->ReadbackImagePixels(dest_mailbox, readback_bitmap.info(), + readback_bitmap.rowBytes(), 0, 0, + readback_bitmap.getPixels()); + } + + // The pixel value should be unchanged, even though the source and dest are + // in different color spaces. No color conversion (which would change the + // pixel value) should have happened. + EXPECT_EQ(*upload_bitmap.getAddr32(0, 0), *readback_bitmap.getAddr32(0, 0)); } -} // namespace -TEST_F(OopPixelTest, ConvertYUVToRGB) { +// The Android emulator does not support RED_8 or RG_88 texture formats. +#if !BUILDFLAG(IS_ANDROID_EMULATOR) +using OopYUVToRGBConfig = ::testing::tuple<gfx::ColorSpace, bool>; + +class OopYUVToRGBPixelTest + : public OopPixelTest, + public ::testing::WithParamInterface<OopYUVToRGBConfig> { + public: + bool TestColorSpaceConversion() const { + return ::testing::get<1>(GetParam()); + } + + gfx::ColorSpace DestinationColorSpace() const { + return ::testing::get<0>(GetParam()); + } +}; + +TEST_P(OopYUVToRGBPixelTest, ConvertYUVToRGB) { + // The source color space for the YUV image. If color space conversion is + // disabled, or if `dest_color_space` is invalid, then this will be ignored. + const gfx::ColorSpace source_color_space(gfx::ColorSpace::PrimaryID::P3, + gfx::ColorSpace::TransferID::SRGB); + + // The output SharedImage color space. + const gfx::ColorSpace dest_color_space = DestinationColorSpace(); + RasterOptions options(gfx::Size(16, 16)); RasterOptions uv_options(gfx::Size(options.resource_size.width() / 2, options.resource_size.height() / 2)); auto* ri = raster_context_provider_->RasterInterface(); auto* sii = raster_context_provider_->SharedImageInterface(); + gpu::Mailbox dest_mailbox = CreateMailboxSharedImage( - ri, sii, options, viz::ResourceFormat::RGBA_8888); + ri, sii, options, viz::ResourceFormat::RGBA_8888, dest_color_space); + + constexpr viz::ResourceFormat format = viz::ResourceFormat::RED_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(); - auto y_pix = std::make_unique<uint8_t[]>(y_pixels_size); - auto u_pix = std::make_unique<uint8_t[]>(uv_pixels_size); - auto v_pix = std::make_unique<uint8_t[]>(uv_pixels_size); - - // Create a blue image - memset(y_pix.get(), 0x1d, y_pixels_size); - memset(u_pix.get(), 0xff, uv_pixels_size); - memset(v_pix.get(), 0x6b, uv_pixels_size); - - // Upload initial yuv image data - gpu::gles2::GLES2Interface* gl = gles2_context_provider_->ContextGL(); - UploadPixels(gl, yuv_mailboxes[0], options.resource_size, GL_LUMINANCE, - GL_UNSIGNED_BYTE, y_pix.get()); - UploadPixels(gl, yuv_mailboxes[1], uv_options.resource_size, GL_LUMINANCE, - GL_UNSIGNED_BYTE, u_pix.get()); - UploadPixels(gl, yuv_mailboxes[2], uv_options.resource_size, GL_LUMINANCE, - GL_UNSIGNED_BYTE, v_pix.get()); - gl->OrderingBarrierCHROMIUM(); + CreateMailboxSharedImage(ri, sii, options, format), + CreateMailboxSharedImage(ri, sii, uv_options, format), + CreateMailboxSharedImage(ri, sii, uv_options, format)}; + + SkImageInfo y_info = SkImageInfo::Make( + options.resource_size.width(), options.resource_size.height(), + kGray_8_SkColorType, kPremul_SkAlphaType, + options.target_color_params.color_space.ToSkColorSpace()); + + SkImageInfo uv_info = SkImageInfo::Make( + uv_options.resource_size.width(), uv_options.resource_size.height(), + kGray_8_SkColorType, kPremul_SkAlphaType, + uv_options.target_color_params.color_space.ToSkColorSpace()); + + // Create Y+U+V image planes for a solid blue image. + SkBitmap y_bitmap; + y_bitmap.allocPixels(y_info); + memset(y_bitmap.getPixels(), 0x1d, y_bitmap.computeByteSize()); + + SkBitmap u_bitmap; + u_bitmap.allocPixels(uv_info); + memset(u_bitmap.getPixels(), 0xff, u_bitmap.computeByteSize()); + + SkBitmap v_bitmap; + v_bitmap.allocPixels(uv_info); + memset(v_bitmap.getPixels(), 0x6b, v_bitmap.computeByteSize()); + + // Upload initial Y+U+V planes and convert to RGB. + UploadPixels(ri, yuv_mailboxes[0], y_info, y_bitmap); + UploadPixels(ri, yuv_mailboxes[1], uv_info, u_bitmap); + UploadPixels(ri, yuv_mailboxes[2], uv_info, v_bitmap); ri->ConvertYUVAMailboxesToRGB(dest_mailbox, kJPEG_SkYUVColorSpace, + TestColorSpaceConversion() + ? source_color_space.ToSkColorSpace().get() + : nullptr, 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, yuv_mailboxes[0], options.resource_size, GL_LUMINANCE8_EXT); - backend_textures[1] = MakeBackendTexture( - gl, yuv_mailboxes[1], uv_options.resource_size, GL_LUMINANCE8_EXT); - backend_textures[2] = MakeBackendTexture( - gl, yuv_mailboxes[2], uv_options.resource_size, GL_LUMINANCE8_EXT); - - SkYUVAInfo yuva_info( - {options.resource_size.width(), options.resource_size.height()}, - SkYUVAInfo::PlaneConfig::kY_U_V, SkYUVAInfo::Subsampling::k420, - kJPEG_Full_SkYUVColorSpace); - GrYUVABackendTextures yuva_textures(yuva_info, backend_textures, - kTopLeft_GrSurfaceOrigin); - - auto expected_image = SkImage::MakeFromYUVATextures( - gles2_context_provider_->GrContext(), yuva_textures); - - SkBitmap expected_bitmap; - expected_bitmap.allocN32Pixels(options.resource_size.width(), - options.resource_size.height()); - expected_image->readPixels(expected_bitmap.pixmap(), 0, 0); - ExpectEquals(actual_bitmap, expected_bitmap); - - for (auto& backend : backend_textures) { - GrGLTextureInfo info; - if (backend.getGLTextureInfo(&info)) - gl->DeleteTextures(1, &info.fID); - } + SkBitmap actual_bitmap = + ReadbackMailbox(ri, dest_mailbox, options.resource_size, + dest_color_space.ToSkColorSpace()); + + SkColor expected_color = + (TestColorSpaceConversion() && dest_color_space.IsValid()) + ? SkColorSetARGB(255, 61, 29, 252) + : SkColorSetARGB(255, 0, 0, 254); + SkBitmap expected_bitmap = MakeSolidColorBitmap( + options.resource_size, SkColor4f::FromColor(expected_color)); + + // Allow slight rounding error on all pixels. + FuzzyPixelComparator comparator( + /*discard_alpha=*/false, + /*error_pixels_percentage_limit=*/100.0f, + /*small_error_pixels_percentage_limit=*/0.0f, + /*avg_abs_error_limit=*/2.f, + /*max_abs_error_limit=*/2.f, + /*small_error_threshold=*/0); + ExpectEquals(actual_bitmap, expected_bitmap, comparator); gpu::SyncToken sync_token; - gl->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData()); sii->DestroySharedImage(sync_token, dest_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) { - RasterOptions options(gfx::Size(16, 16)); - SkImageInfo dest_info = SkImageInfo::MakeN32Premul( - options.resource_size.width(), options.resource_size.height(), - gfx::ColorSpace::CreateSRGB().ToSkColorSpace()); - - SkBitmap expected_bitmap; - expected_bitmap.allocPixels(dest_info); - - SkCanvas canvas(expected_bitmap, SkSurfaceProps{}); - canvas.drawColor(SkColors::kMagenta); - SkPaint green; - green.setColor(SkColors::kGreen); - canvas.drawRect(SkRect::MakeXYWH(1, 2, 3, 4), green); - - auto* ri = raster_context_provider_->RasterInterface(); - auto* sii = raster_context_provider_->SharedImageInterface(); - gpu::Mailbox mailbox = CreateMailboxSharedImage( - ri, sii, options, viz::ResourceFormat::RGBA_8888); - ri->OrderingBarrierCHROMIUM(); - - gpu::gles2::GLES2Interface* gl = gles2_context_provider_->ContextGL(); - UploadPixels(gl, mailbox, options.resource_size, GL_RGBA, GL_UNSIGNED_BYTE, - expected_bitmap.getPixels()); - gl->OrderingBarrierCHROMIUM(); - - SkBitmap actual_bitmap; - actual_bitmap.allocPixels(dest_info); - - ri->ReadbackImagePixels(mailbox, dest_info, dest_info.minRowBytes(), 0, 0, - actual_bitmap.getPixels()); - EXPECT_EQ(ri->GetError(), static_cast<unsigned>(GL_NO_ERROR)); - ri->OrderingBarrierCHROMIUM(); - - ExpectEquals(actual_bitmap, expected_bitmap); - - gpu::SyncToken sync_token; - gl->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData()); - sii->DestroySharedImage(sync_token, mailbox); -} +INSTANTIATE_TEST_SUITE_P( + P, + OopYUVToRGBPixelTest, + ::testing::Combine( + ::testing::Values(gfx::ColorSpace(gfx::ColorSpace::PrimaryID::BT2020, + gfx::ColorSpace::TransferID::SRGB), + gfx::ColorSpace()), + ::testing::Bool())); -// A workaround on Android that forces the use of GLES 2.0 instead of 3.0 -// prevents the use of the GL_RG textures required for NV12 format. This -// test will be reactiviated on Android once the workaround is removed. -#if !BUILDFLAG(IS_ANDROID) TEST_F(OopPixelTest, ConvertNV12ToRGB) { RasterOptions options(gfx::Size(16, 16)); RasterOptions uv_options(gfx::Size(options.resource_size.width() / 2, @@ -2303,70 +2276,57 @@ TEST_F(OopPixelTest, ConvertNV12ToRGB) { gpu::Mailbox dest_mailbox = CreateMailboxSharedImage( ri, sii, options, viz::ResourceFormat::RGBA_8888); gpu::Mailbox y_uv_mailboxes[2]{ - CreateMailboxSharedImage(ri, sii, options, - viz::ResourceFormat::LUMINANCE_8), + CreateMailboxSharedImage(ri, sii, options, viz::ResourceFormat::RED_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; - auto y_pix = std::make_unique<uint8_t[]>(y_pixels_size); - auto uv_pix = std::make_unique<uint8_t[]>(uv_pixels_size); - - memset(y_pix.get(), 0x1d, y_pixels_size); - for (size_t i = 0; i < uv_pixels_size; i += 2) { + SkImageInfo y_info = SkImageInfo::Make( + options.resource_size.width(), options.resource_size.height(), + kGray_8_SkColorType, kPremul_SkAlphaType, + options.target_color_params.color_space.ToSkColorSpace()); + + SkImageInfo uv_info = SkImageInfo::Make( + uv_options.resource_size.width(), uv_options.resource_size.height(), + kR8G8_unorm_SkColorType, kPremul_SkAlphaType, + uv_options.target_color_params.color_space.ToSkColorSpace()); + + // Create Y+UV image planes for a solid blue image. + SkBitmap y_bitmap; + y_bitmap.allocPixels(y_info); + memset(y_bitmap.getPixels(), 0x1d, y_bitmap.computeByteSize()); + + SkBitmap uv_bitmap; + uv_bitmap.allocPixels(uv_info); + uint8_t* uv_pix = static_cast<uint8_t*>(uv_bitmap.getPixels()); + for (size_t i = 0; i < uv_bitmap.computeByteSize(); i += 2) { uv_pix[i] = 0xff; uv_pix[i + 1] = 0x6d; } - gpu::gles2::GLES2Interface* gl = gles2_context_provider_->ContextGL(); - UploadPixels(gl, y_uv_mailboxes[0], options.resource_size, GL_LUMINANCE, - GL_UNSIGNED_BYTE, y_pix.get()); - UploadPixels(gl, y_uv_mailboxes[1], uv_options.resource_size, GL_RG, - GL_UNSIGNED_BYTE, uv_pix.get()); - gl->OrderingBarrierCHROMIUM(); + // Upload initial Y+UV planes and convert to RGB. + UploadPixels(ri, y_uv_mailboxes[0], y_info, y_bitmap); + UploadPixels(ri, y_uv_mailboxes[1], uv_info, uv_bitmap); ri->ConvertYUVAMailboxesToRGB(dest_mailbox, kJPEG_SkYUVColorSpace, + SkColorSpace::MakeSRGB().get(), 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_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::PlaneConfig::kY_UV, SkYUVAInfo::Subsampling::k420, - kJPEG_Full_SkYUVColorSpace); - GrYUVABackendTextures yuva_textures(yuva_info, backend_textures, - kTopLeft_GrSurfaceOrigin); - auto expected_image = SkImage::MakeFromYUVATextures( - gles2_context_provider_->GrContext(), yuva_textures); + SkBitmap actual_bitmap = + ReadbackMailbox(ri, dest_mailbox, options.resource_size); - SkBitmap expected_bitmap; - expected_bitmap.allocN32Pixels(options.resource_size.width(), - options.resource_size.height()); - expected_image->readPixels(expected_bitmap.pixmap(), 0, 0); - ExpectEquals(actual_bitmap, expected_bitmap); + SkBitmap expected_bitmap = MakeSolidColorBitmap( + options.resource_size, + SkColor4f::FromColor(SkColorSetARGB(255, 2, 0, 254))); - for (auto& backend : backend_textures) { - GrGLTextureInfo info; - if (backend.getGLTextureInfo(&info)) - gl->DeleteTextures(1, &info.fID); - } + ExpectEquals(actual_bitmap, expected_bitmap); gpu::SyncToken sync_token; - gl->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData()); sii->DestroySharedImage(sync_token, dest_mailbox); sii->DestroySharedImage(sync_token, y_uv_mailboxes[0]); sii->DestroySharedImage(sync_token, y_uv_mailboxes[1]); } -#endif // !BUILDFLAG(IS_ANDROID) +#endif // !BUILDFLAG(IS_ANDROID_EMULATOR) class OopPathPixelTest : public OopPixelTest, public ::testing::WithParamInterface<bool> { diff --git a/chromium/cc/paint/paint_cache.cc b/chromium/cc/paint/paint_cache.cc index 8da1afa8452..90a32da1328 100644 --- a/chromium/cc/paint/paint_cache.cc +++ b/chromium/cc/paint/paint_cache.cc @@ -90,15 +90,6 @@ bool ClientPaintCache::PurgeAll() { ServicePaintCache::ServicePaintCache() = default; ServicePaintCache::~ServicePaintCache() = default; -void ServicePaintCache::PutTextBlob(PaintCacheId id, sk_sp<SkTextBlob> blob) { - cached_blobs_.emplace(id, std::move(blob)); -} - -sk_sp<SkTextBlob> ServicePaintCache::GetTextBlob(PaintCacheId id) const { - auto it = cached_blobs_.find(id); - return it == cached_blobs_.end() ? nullptr : it->second; -} - void ServicePaintCache::PutPath(PaintCacheId id, SkPath path) { cached_paths_.emplace(id, std::move(path)); } @@ -115,9 +106,6 @@ void ServicePaintCache::Purge(PaintCacheDataType type, size_t n, const volatile PaintCacheId* ids) { switch (type) { - case PaintCacheDataType::kTextBlob: - EraseFromMap(&cached_blobs_, n, ids); - return; case PaintCacheDataType::kPath: EraseFromMap(&cached_paths_, n, ids); return; @@ -127,7 +115,6 @@ void ServicePaintCache::Purge(PaintCacheDataType type, } void ServicePaintCache::PurgeAll() { - cached_blobs_.clear(); cached_paths_.clear(); } diff --git a/chromium/cc/paint/paint_cache.h b/chromium/cc/paint/paint_cache.h index e0cd5384ebf..3a0a889fc9a 100644 --- a/chromium/cc/paint/paint_cache.h +++ b/chromium/cc/paint/paint_cache.h @@ -38,7 +38,7 @@ namespace cc { using PaintCacheId = uint32_t; using PaintCacheIds = std::vector<PaintCacheId>; -enum class PaintCacheDataType : uint32_t { kTextBlob, kPath, kLast = kPath }; +enum class PaintCacheDataType : uint32_t { kPath, kLast = kPath }; enum class PaintCacheEntryState : uint32_t { kEmpty, kCached, @@ -107,13 +107,6 @@ class CC_PAINT_EXPORT ServicePaintCache { ServicePaintCache(); ~ServicePaintCache(); - // Stores the |blob| received from the client in the cache. - void PutTextBlob(PaintCacheId id, sk_sp<SkTextBlob> blob); - - // Retrieves an entry for |id| stored in the cache. Or nullptr if the entry - // is not found. - sk_sp<SkTextBlob> GetTextBlob(PaintCacheId id) const; - // Stores |path| received from the client in the cache. void PutPath(PaintCacheId, SkPath path); @@ -125,11 +118,9 @@ class CC_PAINT_EXPORT ServicePaintCache { size_t n, const volatile PaintCacheId* ids); void PurgeAll(); - bool empty() const { return cached_blobs_.empty() && cached_paths_.empty(); } + bool empty() const { return cached_paths_.empty(); } private: - using BlobMap = std::map<PaintCacheId, sk_sp<SkTextBlob>>; - BlobMap cached_blobs_; using PathMap = std::map<PaintCacheId, SkPath>; PathMap cached_paths_; }; diff --git a/chromium/cc/paint/paint_cache_unittest.cc b/chromium/cc/paint/paint_cache_unittest.cc index 60ab43062ec..eca018ca823 100644 --- a/chromium/cc/paint/paint_cache_unittest.cc +++ b/chromium/cc/paint/paint_cache_unittest.cc @@ -11,18 +11,6 @@ namespace { constexpr size_t kDefaultBudget = 1024u; -sk_sp<SkTextBlob> CreateBlob() { - SkFont font; - font.setTypeface(SkTypeface::MakeDefault()); - - SkTextBlobBuilder builder; - int glyph_count = 5; - const auto& run = builder.allocRun(font, glyph_count, 1.2f, 2.3f); - // allocRun() allocates only the glyph buffer. - std::fill(run.glyphs, run.glyphs + glyph_count, 0); - return builder.make(); -} - SkPath CreatePath() { SkPath path; path.addCircle(2, 2, 5); @@ -91,17 +79,6 @@ TEST_P(PaintCacheTest, CommitPendingEntries) { TEST_P(PaintCacheTest, ServiceBasic) { ServicePaintCache service_cache; switch (GetType()) { - case PaintCacheDataType::kTextBlob: { - auto blob = CreateBlob(); - auto id = blob->uniqueID(); - EXPECT_EQ(nullptr, service_cache.GetTextBlob(id)); - service_cache.PutTextBlob(id, blob); - EXPECT_EQ(blob, service_cache.GetTextBlob(id)); - service_cache.Purge(GetType(), 1, &id); - EXPECT_EQ(nullptr, service_cache.GetTextBlob(id)); - - service_cache.PutTextBlob(id, blob); - } break; case PaintCacheDataType::kPath: { auto path = CreatePath(); auto id = path.getGenerationID(); @@ -125,8 +102,7 @@ TEST_P(PaintCacheTest, ServiceBasic) { INSTANTIATE_TEST_SUITE_P( P, PaintCacheTest, - ::testing::Range(static_cast<uint32_t>(0), - static_cast<uint32_t>(PaintCacheDataType::kLast))); + ::testing::Values(static_cast<uint32_t>(PaintCacheDataType::kPath))); } // namespace } // namespace cc diff --git a/chromium/cc/paint/paint_canvas.h b/chromium/cc/paint/paint_canvas.h index bd32f3d968b..3815f678501 100644 --- a/chromium/cc/paint/paint_canvas.h +++ b/chromium/cc/paint/paint_canvas.h @@ -134,11 +134,11 @@ class CC_PAINT_EXPORT PaintCanvas { virtual bool getLocalClipBounds(SkRect* bounds) const = 0; virtual SkIRect getDeviceClipBounds() const = 0; virtual bool getDeviceClipBounds(SkIRect* bounds) const = 0; - virtual void drawColor(SkColor color, SkBlendMode mode) = 0; - void drawColor(SkColor color) { drawColor(color, SkBlendMode::kSrcOver); } + virtual void drawColor(SkColor4f color, SkBlendMode mode) = 0; + void drawColor(SkColor4f color) { drawColor(color, SkBlendMode::kSrcOver); } // TODO(enne): This is a synonym for drawColor with kSrc. Remove it. - virtual void clear(SkColor color) = 0; + virtual void clear(SkColor4f color) = 0; virtual void drawLine(SkScalar x0, SkScalar y0, diff --git a/chromium/cc/paint/paint_filter.cc b/chromium/cc/paint/paint_filter.cc index 705aa95dabe..8f8c217bf64 100644 --- a/chromium/cc/paint/paint_filter.cc +++ b/chromium/cc/paint/paint_filter.cc @@ -538,7 +538,7 @@ DropShadowPaintFilter::DropShadowPaintFilter(SkScalar dx, SkScalar dy, SkScalar sigma_x, SkScalar sigma_y, - SkColor color, + SkColor4f color, ShadowMode shadow_mode, sk_sp<PaintFilter> input, const CropRect* crop_rect) @@ -551,13 +551,15 @@ DropShadowPaintFilter::DropShadowPaintFilter(SkScalar dx, shadow_mode_(shadow_mode), input_(std::move(input)) { if (shadow_mode == ShadowMode::kDrawShadowOnly) { - cached_sk_filter_ = - SkImageFilters::DropShadowOnly(dx_, dy_, sigma_x_, sigma_y_, color_, - GetSkFilter(input_.get()), crop_rect); + // TODO(crbug/1308932): Remove toSkColor and make all SkColor4f. + cached_sk_filter_ = SkImageFilters::DropShadowOnly( + dx_, dy_, sigma_x_, sigma_y_, color_.toSkColor(), + GetSkFilter(input_.get()), crop_rect); } else { - cached_sk_filter_ = - SkImageFilters::DropShadow(dx_, dy_, sigma_x_, sigma_y_, color_, - GetSkFilter(input_.get()), crop_rect); + // TODO(crbug/1308932): Remove toSkColor and make all SkColor4f. + cached_sk_filter_ = SkImageFilters::DropShadow( + dx_, dy_, sigma_x_, sigma_y_, color_.toSkColor(), + GetSkFilter(input_.get()), crop_rect); } } @@ -1022,12 +1024,11 @@ sk_sp<RecordPaintFilter> RecordPaintFilter::CreateScaledPaintRecord( // after PaintShader::CreateScaledPaintRecord. SkRect scaled_record_bounds = PaintRecord::GetFixedScaleBounds(ctm, record_bounds_, max_texture_size); - if (scaled_record_bounds.isEmpty()) - return nullptr; - gfx::SizeF raster_scale = { scaled_record_bounds.width() / record_bounds_.width(), scaled_record_bounds.height() / record_bounds_.height()}; + if (raster_scale.IsEmpty()) + return nullptr; return sk_make_sp<RecordPaintFilter>(record_, scaled_record_bounds, raster_scale, @@ -1291,9 +1292,10 @@ ShaderPaintFilter::ShaderPaintFilter(sk_sp<PaintShader> shader, if (alpha < 255) { // The blend effectively produces (shader * alpha), the rgb of the secondary // color are ignored. - SkColor color = SkColorSetARGB(alpha, 255, 255, 255); + SkColor4f color{1.0f, 1.0f, 1.0f, alpha / 255.0f}; + // TODO(crbug/1308932): Remove toSkColor and make all SkColor4f. sk_shader = SkShaders::Blend(SkBlendMode::kDstIn, std::move(sk_shader), - SkShaders::Color(color)); + SkShaders::Color(color.toSkColor())); } cached_sk_filter_ = @@ -1320,7 +1322,7 @@ sk_sp<PaintFilter> ShaderPaintFilter::SnapshotWithImagesInternal( orig_flags.setDither(dither_ == SkImageFilters::Dither::kYes); ScopedRasterFlags raster_flags(&orig_flags, image_provider, SkMatrix::I(), 0, - 255u); + 1.0f); const PaintFlags* snapshot = raster_flags.flags(); if (snapshot) { // Ref the updated paint shader so that it can outlive ScopedRasterFlags @@ -1377,7 +1379,7 @@ bool MatrixPaintFilter::operator==(const MatrixPaintFilter& other) const { LightingDistantPaintFilter::LightingDistantPaintFilter( LightingType lighting_type, const SkPoint3& direction, - SkColor light_color, + SkColor4f light_color, SkScalar surface_scale, SkScalar kconstant, SkScalar shininess, @@ -1393,14 +1395,16 @@ LightingDistantPaintFilter::LightingDistantPaintFilter( input_(std::move(input)) { switch (lighting_type_) { case LightingType::kDiffuse: + // TODO(crbug/1308932): Remove toSkColor and make all SkColor4f. cached_sk_filter_ = SkImageFilters::DistantLitDiffuse( - direction_, light_color_, surface_scale_, kconstant_, + direction_, light_color_.toSkColor(), surface_scale_, kconstant_, GetSkFilter(input_.get()), crop_rect); break; case LightingType::kSpecular: + // TODO(crbug/1308932): Remove toSkColor and make all SkColor4f. cached_sk_filter_ = SkImageFilters::DistantLitSpecular( - direction_, light_color_, surface_scale_, kconstant_, shininess_, - GetSkFilter(input_.get()), crop_rect); + direction_, light_color_.toSkColor(), surface_scale_, kconstant_, + shininess_, GetSkFilter(input_.get()), crop_rect); break; } } @@ -1436,7 +1440,7 @@ bool LightingDistantPaintFilter::operator==( LightingPointPaintFilter::LightingPointPaintFilter(LightingType lighting_type, const SkPoint3& location, - SkColor light_color, + SkColor4f light_color, SkScalar surface_scale, SkScalar kconstant, SkScalar shininess, @@ -1452,14 +1456,16 @@ LightingPointPaintFilter::LightingPointPaintFilter(LightingType lighting_type, input_(std::move(input)) { switch (lighting_type_) { case LightingType::kDiffuse: + // TODO(crbug/1308932): Remove toSkColor and make all SkColor4f. cached_sk_filter_ = SkImageFilters::PointLitDiffuse( - location_, light_color_, surface_scale_, kconstant_, + location_, light_color_.toSkColor(), surface_scale_, kconstant_, GetSkFilter(input_.get()), crop_rect); break; case LightingType::kSpecular: + // TODO(crbug/1308932): Remove toSkColor and make all SkColor4f. cached_sk_filter_ = SkImageFilters::PointLitSpecular( - location_, light_color_, surface_scale_, kconstant_, shininess_, - GetSkFilter(input_.get()), crop_rect); + location_, light_color_.toSkColor(), surface_scale_, kconstant_, + shininess_, GetSkFilter(input_.get()), crop_rect); break; } } @@ -1498,7 +1504,7 @@ LightingSpotPaintFilter::LightingSpotPaintFilter(LightingType lighting_type, const SkPoint3& target, SkScalar specular_exponent, SkScalar cutoff_angle, - SkColor light_color, + SkColor4f light_color, SkScalar surface_scale, SkScalar kconstant, SkScalar shininess, @@ -1517,15 +1523,18 @@ LightingSpotPaintFilter::LightingSpotPaintFilter(LightingType lighting_type, input_(std::move(input)) { switch (lighting_type_) { case LightingType::kDiffuse: + // TODO(crbug/1308932): Remove toSkColor and make all SkColor4f. cached_sk_filter_ = SkImageFilters::SpotLitDiffuse( - location_, target_, specular_exponent_, cutoff_angle_, light_color_, - surface_scale_, kconstant_, GetSkFilter(input_.get()), crop_rect); + location_, target_, specular_exponent_, cutoff_angle_, + light_color_.toSkColor(), surface_scale_, kconstant_, + GetSkFilter(input_.get()), crop_rect); break; case LightingType::kSpecular: + // TODO(crbug/1308932): Remove toSkColor and make all SkColor4f. cached_sk_filter_ = SkImageFilters::SpotLitSpecular( - location_, target_, specular_exponent_, cutoff_angle_, light_color_, - surface_scale_, kconstant_, shininess_, GetSkFilter(input_.get()), - crop_rect); + location_, target_, specular_exponent_, cutoff_angle_, + light_color_.toSkColor(), surface_scale_, kconstant_, shininess_, + GetSkFilter(input_.get()), crop_rect); break; } } diff --git a/chromium/cc/paint/paint_filter.h b/chromium/cc/paint/paint_filter.h index 93a456dff66..7cd1a3d5c23 100644 --- a/chromium/cc/paint/paint_filter.h +++ b/chromium/cc/paint/paint_filter.h @@ -20,7 +20,6 @@ #include "third_party/skia/include/effects/SkImageFilters.h" namespace viz { -class GLRenderer; class SkiaRenderer; class SoftwareRenderer; } // namespace viz @@ -146,7 +145,6 @@ class CC_PAINT_EXPORT PaintFilter : public SkRefCnt { // For cached skia filter access in SkPaint conversions. Mostly used during // raster. friend class PaintFlags; - friend class viz::GLRenderer; friend class viz::SkiaRenderer; friend class viz::SoftwareRenderer; @@ -222,7 +220,7 @@ class CC_PAINT_EXPORT DropShadowPaintFilter final : public PaintFilter { SkScalar dy, SkScalar sigma_x, SkScalar sigma_y, - SkColor color, + SkColor4f color, ShadowMode shadow_mode, sk_sp<PaintFilter> input, const CropRect* crop_rect = nullptr); @@ -232,7 +230,7 @@ class CC_PAINT_EXPORT DropShadowPaintFilter final : public PaintFilter { SkScalar dy() const { return dy_; } SkScalar sigma_x() const { return sigma_x_; } SkScalar sigma_y() const { return sigma_y_; } - SkColor color() const { return color_; } + SkColor4f color() const { return color_; } ShadowMode shadow_mode() const { return shadow_mode_; } const sk_sp<PaintFilter>& input() const { return input_; } @@ -248,7 +246,7 @@ class CC_PAINT_EXPORT DropShadowPaintFilter final : public PaintFilter { SkScalar dy_; SkScalar sigma_x_; SkScalar sigma_y_; - SkColor color_; + SkColor4f color_; ShadowMode shadow_mode_; sk_sp<PaintFilter> input_; }; @@ -756,7 +754,7 @@ class CC_PAINT_EXPORT LightingDistantPaintFilter final : public PaintFilter { // For specular lighting type only, shininess denotes the specular exponent. LightingDistantPaintFilter(LightingType lighting_type, const SkPoint3& direction, - SkColor light_color, + SkColor4f light_color, SkScalar surface_scale, SkScalar kconstant, SkScalar shininess, @@ -766,7 +764,7 @@ class CC_PAINT_EXPORT LightingDistantPaintFilter final : public PaintFilter { LightingType lighting_type() const { return lighting_type_; } const SkPoint3& direction() const { return direction_; } - SkColor light_color() const { return light_color_; } + SkColor4f light_color() const { return light_color_; } SkScalar surface_scale() const { return surface_scale_; } SkScalar kconstant() const { return kconstant_; } SkScalar shininess() const { return shininess_; } @@ -782,7 +780,7 @@ class CC_PAINT_EXPORT LightingDistantPaintFilter final : public PaintFilter { private: LightingType lighting_type_; SkPoint3 direction_; - SkColor light_color_; + SkColor4f light_color_; SkScalar surface_scale_; SkScalar kconstant_; SkScalar shininess_; @@ -797,7 +795,7 @@ class CC_PAINT_EXPORT LightingPointPaintFilter final : public PaintFilter { // For specular lighting type only, shininess denotes the specular exponent. LightingPointPaintFilter(LightingType lighting_type, const SkPoint3& location, - SkColor light_color, + SkColor4f light_color, SkScalar surface_scale, SkScalar kconstant, SkScalar shininess, @@ -807,7 +805,7 @@ class CC_PAINT_EXPORT LightingPointPaintFilter final : public PaintFilter { LightingType lighting_type() const { return lighting_type_; } const SkPoint3& location() const { return location_; } - SkColor light_color() const { return light_color_; } + SkColor4f light_color() const { return light_color_; } SkScalar surface_scale() const { return surface_scale_; } SkScalar kconstant() const { return kconstant_; } SkScalar shininess() const { return shininess_; } @@ -823,7 +821,7 @@ class CC_PAINT_EXPORT LightingPointPaintFilter final : public PaintFilter { private: LightingType lighting_type_; SkPoint3 location_; - SkColor light_color_; + SkColor4f light_color_; SkScalar surface_scale_; SkScalar kconstant_; SkScalar shininess_; @@ -841,7 +839,7 @@ class CC_PAINT_EXPORT LightingSpotPaintFilter final : public PaintFilter { const SkPoint3& target, SkScalar specular_exponent, SkScalar cutoff_angle, - SkColor light_color, + SkColor4f light_color, SkScalar surface_scale, SkScalar kconstant, SkScalar shininess, @@ -854,7 +852,7 @@ class CC_PAINT_EXPORT LightingSpotPaintFilter final : public PaintFilter { const SkPoint3& target() const { return target_; } SkScalar specular_exponent() const { return specular_exponent_; } SkScalar cutoff_angle() const { return cutoff_angle_; } - SkColor light_color() const { return light_color_; } + SkColor4f light_color() const { return light_color_; } SkScalar surface_scale() const { return surface_scale_; } SkScalar kconstant() const { return kconstant_; } SkScalar shininess() const { return shininess_; } @@ -873,7 +871,7 @@ class CC_PAINT_EXPORT LightingSpotPaintFilter final : public PaintFilter { SkPoint3 target_; SkScalar specular_exponent_; SkScalar cutoff_angle_; - SkColor light_color_; + SkColor4f light_color_; SkScalar surface_scale_; SkScalar kconstant_; SkScalar shininess_; diff --git a/chromium/cc/paint/paint_filter_unittest.cc b/chromium/cc/paint/paint_filter_unittest.cc index a25d3fd9ba5..a075980d2d8 100644 --- a/chromium/cc/paint/paint_filter_unittest.cc +++ b/chromium/cc/paint/paint_filter_unittest.cc @@ -59,7 +59,7 @@ sk_sp<PaintFilter> CreateTestFilter(PaintFilter::Type filter_type, record_filter, &crop_rect); case PaintFilter::Type::kDropShadow: return sk_make_sp<DropShadowPaintFilter>( - 0.1, 0.2f, 0.3f, 0.4f, SK_ColorWHITE, + 0.1, 0.2f, 0.3f, 0.4f, SkColors::kWhite, DropShadowPaintFilter::ShadowMode::kDrawShadowOnly, image_filter, &crop_rect); case PaintFilter::Type::kMagnifier: @@ -124,15 +124,15 @@ sk_sp<PaintFilter> CreateTestFilter(PaintFilter::Type filter_type, case PaintFilter::Type::kLightingDistant: return sk_make_sp<LightingDistantPaintFilter>( PaintFilter::LightingType::kDiffuse, SkPoint3::Make(0.1f, 0.2f, 0.3f), - SK_ColorWHITE, 0.1f, 0.2f, 0.3f, image_filter, &crop_rect); + SkColors::kWhite, 0.1f, 0.2f, 0.3f, image_filter, &crop_rect); case PaintFilter::Type::kLightingPoint: return sk_make_sp<LightingPointPaintFilter>( PaintFilter::LightingType::kDiffuse, SkPoint3::Make(0.1f, 0.2f, 0.3f), - SK_ColorWHITE, 0.1f, 0.2f, 0.3f, record_filter, &crop_rect); + SkColors::kWhite, 0.1f, 0.2f, 0.3f, record_filter, &crop_rect); case PaintFilter::Type::kLightingSpot: return sk_make_sp<LightingSpotPaintFilter>( PaintFilter::LightingType::kDiffuse, SkPoint3::Make(0.1f, 0.2f, 0.3f), - SkPoint3::Make(0.4f, 0.5f, 0.6f), 0.1f, 0.2f, SK_ColorWHITE, 0.4f, + SkPoint3::Make(0.4f, 0.5f, 0.6f), 0.1f, 0.2f, SkColors::kWhite, 0.4f, 0.5f, 0.6f, image_filter, &crop_rect); case PaintFilter::Type::kStretch: return sk_make_sp<StretchPaintFilter>(0.1f, 0.2f, 100.f, 200.f, diff --git a/chromium/cc/paint/paint_flags.h b/chromium/cc/paint/paint_flags.h index 537c13f9b2c..ba137d0fac8 100644 --- a/chromium/cc/paint/paint_flags.h +++ b/chromium/cc/paint/paint_flags.h @@ -51,9 +51,14 @@ class CC_PAINT_EXPORT PaintFlags { ALWAYS_INLINE uint8_t getAlpha() const { return SkColorGetA(color_.toSkColor()); } + ALWAYS_INLINE float getAlphaf() const { return color_.fA; } ALWAYS_INLINE void setAlpha(uint8_t a) { color_ = SkColor4f::FromColor(SkColorSetA(color_.toSkColor(), a)); } + template <class F, class = std::enable_if_t<std::is_same_v<F, float>>> + ALWAYS_INLINE void setAlphaf(F a) { + color_.fA = a; + } ALWAYS_INLINE void setBlendMode(SkBlendMode mode) { blend_mode_ = static_cast<uint32_t>(mode); } @@ -73,7 +78,7 @@ class CC_PAINT_EXPORT PaintFlags { kLast = kHigh, }; ALWAYS_INLINE void setFilterQuality(FilterQuality quality) { - bitfields_.filter_quality_ = static_cast<int>(quality); + bitfields_.filter_quality_ = static_cast<uint32_t>(quality); } ALWAYS_INLINE FilterQuality getFilterQuality() const { return static_cast<FilterQuality>(bitfields_.filter_quality_); diff --git a/chromium/cc/paint/paint_image.cc b/chromium/cc/paint/paint_image.cc index ed3a8f51214..ed04d6b6c24 100644 --- a/chromium/cc/paint/paint_image.cc +++ b/chromium/cc/paint/paint_image.cc @@ -71,6 +71,8 @@ bool PaintImage::operator==(const PaintImage& other) const { return false; if (paint_worklet_input_ != other.paint_worklet_input_) return false; + // Do not check may_be_lcp_candidate_ as it should not affect any rendering + // operation, only metrics collection. return true; } @@ -230,7 +232,7 @@ bool PaintImage::DecodeYuv(const SkYUVAPixmaps& pixmaps, DCHECK(paint_image_generator_); const uint32_t lazy_pixel_ref = stable_id(); return paint_image_generator_->GetYUVAPlanes(pixmaps, frame_index, - lazy_pixel_ref); + lazy_pixel_ref, client_id); } bool PaintImage::DecodeFromGenerator(void* memory, @@ -397,6 +399,7 @@ std::string PaintImage::ToString() const { << " animation_type_: " << static_cast<int>(animation_type_) << " completion_state_: " << static_cast<int>(completion_state_) << " is_multipart_: " << is_multipart_ + << " may_be_lcp_candidate_: " << may_be_lcp_candidate_ << " is YUV: " << IsYuv(SkYUVAPixmapInfo::SupportedDataTypes::All()); return str.str(); } diff --git a/chromium/cc/paint/paint_image.h b/chromium/cc/paint/paint_image.h index 32cbb7fdaeb..859f4487dc8 100644 --- a/chromium/cc/paint/paint_image.h +++ b/chromium/cc/paint/paint_image.h @@ -264,6 +264,7 @@ class CC_PAINT_EXPORT PaintImage { CompletionState completion_state() const { return completion_state_; } bool is_multipart() const { return is_multipart_; } bool is_high_bit_depth() const { return is_high_bit_depth_; } + bool may_be_lcp_candidate() const { return may_be_lcp_candidate_; } int repetition_count() const { return repetition_count_; } bool ShouldAnimate() const; AnimationSequenceId reset_animation_sequence_id() const { @@ -389,6 +390,12 @@ class CC_PAINT_EXPORT PaintImage { // Whether this image has more than 8 bits per color channel. bool is_high_bit_depth_ = false; + // Whether this image may untimately be a candidate for Largest Contentful + // Paint. The final LCP contribution of an image is unknown until we present + // it, but this flag is intended for metrics on when we do not present the + // image when the system claims. + bool may_be_lcp_candidate_ = false; + // An incrementing sequence number maintained by the painter to indicate if // this animation should be reset in the compositor. Incrementing this number // will reset this animation in the compositor for the first frame which has a diff --git a/chromium/cc/paint/paint_image_builder.h b/chromium/cc/paint/paint_image_builder.h index 008d0fa5dc0..f75c3ffc06e 100644 --- a/chromium/cc/paint/paint_image_builder.h +++ b/chromium/cc/paint/paint_image_builder.h @@ -87,6 +87,10 @@ class CC_PAINT_EXPORT PaintImageBuilder { paint_image_.is_high_bit_depth_ = is_high_bit_depth; return std::move(*this); } + PaintImageBuilder&& set_may_be_lcp_candidate(bool may_be_lcp_candidate) { + paint_image_.may_be_lcp_candidate_ = may_be_lcp_candidate; + return std::move(*this); + } PaintImageBuilder&& set_repetition_count(int count) { paint_image_.repetition_count_ = count; return std::move(*this); diff --git a/chromium/cc/paint/paint_image_generator.h b/chromium/cc/paint/paint_image_generator.h index 7c52f61681f..8941b1feb5b 100644 --- a/chromium/cc/paint/paint_image_generator.h +++ b/chromium/cc/paint/paint_image_generator.h @@ -61,7 +61,8 @@ class CC_PAINT_EXPORT PaintImageGenerator : public SkRefCnt { // DecodingImageGenerator tracing needs. Remove it. virtual bool GetYUVAPlanes(const SkYUVAPixmaps& pixmaps, size_t frame_index, - uint32_t lazy_pixel_ref) = 0; + uint32_t lazy_pixel_ref, + PaintImage::GeneratorClientId client_id) = 0; // Returns the smallest size that is at least as big as the requested size, // such that we can decode to exactly that scale. diff --git a/chromium/cc/paint/paint_op_buffer.cc b/chromium/cc/paint/paint_op_buffer.cc index 1081d238920..475cf1c02ea 100644 --- a/chromium/cc/paint/paint_op_buffer.cc +++ b/chromium/cc/paint/paint_op_buffer.cc @@ -1910,9 +1910,9 @@ void SaveLayerAlphaOp::Raster(const SaveLayerAlphaOp* op, // See PaintOp::kUnsetRect bool unset = op->bounds.left() == SK_ScalarInfinity; absl::optional<SkPaint> paint; - if (op->alpha != 0xFF) { + if (op->alpha != 1.0f) { paint.emplace(); - paint->setAlpha(op->alpha); + paint->setAlpha(op->alpha * 255.0f); } SkCanvas::SaveLayerRec rec(unset ? nullptr : &op->bounds, base::OptionalOrNullptr(paint)); @@ -2737,7 +2737,7 @@ int DrawPathOp::CountSlowPaths() const { } int DrawRecordOp::CountSlowPaths() const { - return record->num_slow_paths(); + return record->num_slow_paths_up_to_min_for_MSAA(); } bool DrawRecordOp::HasNonAAPaint() const { @@ -2929,7 +2929,7 @@ PaintOpBuffer& PaintOpBuffer::operator=(PaintOpBuffer&& other) { used_ = other.used_; reserved_ = other.reserved_; op_count_ = other.op_count_; - num_slow_paths_ = other.num_slow_paths_; + num_slow_paths_up_to_min_for_MSAA_ = other.num_slow_paths_up_to_min_for_MSAA_; subrecord_bytes_used_ = other.subrecord_bytes_used_; subrecord_op_count_ = other.subrecord_op_count_; has_non_aa_paint_ = other.has_non_aa_paint_; @@ -2960,7 +2960,7 @@ void PaintOpBuffer::Reset() { // that if called. used_ = 0; op_count_ = 0; - num_slow_paths_ = 0; + num_slow_paths_up_to_min_for_MSAA_ = 0; has_non_aa_paint_ = false; subrecord_bytes_used_ = 0; subrecord_op_count_ = 0; @@ -3019,7 +3019,7 @@ PaintOpBuffer::PlaybackFoldingIterator::PlaybackFoldingIterator( PaintOpBuffer::PlaybackFoldingIterator::~PlaybackFoldingIterator() = default; void PaintOpBuffer::PlaybackFoldingIterator::FindNextOp() { - current_alpha_ = 255u; + current_alpha_ = 1.0f; for (current_op_ = NextUnfoldedOp(); current_op_; current_op_ = NextUnfoldedOp()) { if (current_op_->GetType() != PaintOpType::SaveLayerAlpha) @@ -3060,7 +3060,7 @@ void PaintOpBuffer::PlaybackFoldingIterator::FindNextOp() { auto* draw_color_op = static_cast<const DrawColorOp*>(draw_op); SkColor4f color = draw_color_op->color; folded_draw_color_.color = {color.fR, color.fG, color.fB, - save_op->alpha / 255 * color.fA}; + save_op->alpha * color.fA}; current_op_ = &folded_draw_color_; break; } @@ -3143,7 +3143,7 @@ void PaintOpBuffer::Playback(SkCanvas* canvas, auto* context = canvas->recordingContext(); const ScopedRasterFlags scoped_flags( &flags_op->flags, new_params.image_provider, canvas->getTotalMatrix(), - context ? context->maxTextureSize() : 0, iter.alpha()); + context ? context->maxTextureSize() : 0, iter.alpha() / 255.0f); if (const auto* raster_flags = scoped_flags.flags()) flags_op->RasterWithFlags(canvas, raster_flags, new_params); } else { @@ -3285,7 +3285,8 @@ void PaintOpBuffer::ShrinkToFit() { bool PaintOpBuffer::operator==(const PaintOpBuffer& other) const { if (op_count_ != other.op_count_) return false; - if (num_slow_paths_ != other.num_slow_paths_) + if (num_slow_paths_up_to_min_for_MSAA_ != + other.num_slow_paths_up_to_min_for_MSAA_) return false; if (subrecord_bytes_used_ != other.subrecord_bytes_used_) return false; diff --git a/chromium/cc/paint/paint_op_buffer.h b/chromium/cc/paint/paint_op_buffer.h index 37f9aaad81c..fbb5b964f03 100644 --- a/chromium/cc/paint/paint_op_buffer.h +++ b/chromium/cc/paint/paint_op_buffer.h @@ -976,7 +976,8 @@ class CC_PAINT_EXPORT SaveLayerOp final : public PaintOpWithFlags { class CC_PAINT_EXPORT SaveLayerAlphaOp final : public PaintOp { public: static constexpr PaintOpType kType = PaintOpType::SaveLayerAlpha; - SaveLayerAlphaOp(const SkRect* bounds, uint8_t alpha) + template <class F, class = std::enable_if_t<std::is_same_v<F, float>>> + SaveLayerAlphaOp(const SkRect* bounds, F alpha) : PaintOp(kType), bounds(bounds ? *bounds : kUnsetRect), alpha(alpha) {} static void Raster(const SaveLayerAlphaOp* op, SkCanvas* canvas, @@ -988,7 +989,7 @@ class CC_PAINT_EXPORT SaveLayerAlphaOp final : public PaintOp { HAS_SERIALIZATION_FUNCTIONS(); SkRect bounds; - uint8_t alpha; + float alpha; private: SaveLayerAlphaOp() : PaintOp(kType) {} @@ -1078,6 +1079,9 @@ using LargestPaintOp = DrawImageRectOp, DrawDRRectOp>::type; +// Defined outside of the class as this const is used in multiple files. +static constexpr int kMinNumberOfSlowPathsForMSAA = 6; + class CC_PAINT_EXPORT PaintOpBuffer : public SkRefCnt { public: enum { kInitialBufferSize = 4096 }; @@ -1133,7 +1137,9 @@ class CC_PAINT_EXPORT PaintOpBuffer : public SkRefCnt { size_t total_op_count() const { return op_count_ + subrecord_op_count_; } size_t next_op_offset() const { return used_; } - int num_slow_paths() const { return num_slow_paths_; } + int num_slow_paths_up_to_min_for_MSAA() const { + return num_slow_paths_up_to_min_for_MSAA_; + } bool HasNonAAPaint() const { return has_non_aa_paint_; } bool HasDiscardableImages() const { return has_discardable_images_; } @@ -1202,8 +1208,10 @@ class CC_PAINT_EXPORT PaintOpBuffer : public SkRefCnt { static_assert(!std::is_same<T, PaintOp>::value, "AnalyzeAddedOp needs a subtype of PaintOp"); - num_slow_paths_ += op->CountSlowPathsFromFlags(); - num_slow_paths_ += op->CountSlowPaths(); + if (num_slow_paths_up_to_min_for_MSAA_ < kMinNumberOfSlowPathsForMSAA) { + num_slow_paths_up_to_min_for_MSAA_ += op->CountSlowPathsFromFlags(); + num_slow_paths_up_to_min_for_MSAA_ += op->CountSlowPaths(); + } has_non_aa_paint_ |= op->HasNonAAPaint(); @@ -1233,7 +1241,8 @@ class CC_PAINT_EXPORT PaintOpBuffer : public SkRefCnt { size_t GetOpOffsetForTracing(const PaintOp* op) const { DCHECK_GE(reinterpret_cast<const char*>(op), data_.get()); - size_t result = reinterpret_cast<const char*>(op) - data_.get(); + size_t result = + static_cast<size_t>(reinterpret_cast<const char*>(op) - data_.get()); DCHECK_LT(result, used_); return result; } @@ -1408,7 +1417,9 @@ class CC_PAINT_EXPORT PaintOpBuffer : public SkRefCnt { operator bool() const { return !!current_op_; } // Guaranteed to be 255 for all ops without flags. - uint8_t alpha() const { return current_alpha_; } + uint8_t alpha() const { + return static_cast<uint8_t>(current_alpha_ * 255.0f); + } private: void FindNextOp(); @@ -1424,7 +1435,7 @@ class CC_PAINT_EXPORT PaintOpBuffer : public SkRefCnt { // analysis of sampling profiler data and tab_search:top100:2020). RAW_PTR_EXCLUSION const PaintOp* current_op_ = nullptr; - uint8_t current_alpha_ = 255; + float current_alpha_ = 1.0f; }; private: @@ -1452,8 +1463,10 @@ class CC_PAINT_EXPORT PaintOpBuffer : public SkRefCnt { size_t subrecord_bytes_used_ = 0; // Record total op count of referenced sub-record and display lists. size_t subrecord_op_count_ = 0; - // Record paths for veto-to-msaa for gpu raster. - int num_slow_paths_ = 0; + // Record paths for veto-to-msaa for gpu raster. Counting slow paths can be + // very expensive, we stop counting them once reaching the minimum number + // required for an MSAA sample count for raster. + int num_slow_paths_up_to_min_for_MSAA_ = 0; bool has_non_aa_paint_ : 1; bool has_discardable_images_ : 1; diff --git a/chromium/cc/paint/paint_op_buffer_fuzzer.cc b/chromium/cc/paint/paint_op_buffer_fuzzer.cc index 3324ae0afaf..a20a5050d25 100644 --- a/chromium/cc/paint/paint_op_buffer_fuzzer.cc +++ b/chromium/cc/paint/paint_op_buffer_fuzzer.cc @@ -130,8 +130,9 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { if (bytes_for_fonts > size) bytes_for_fonts = size / 2; // PaintOpBuffer only accepts 4 bytes aligned buffer. - bytes_for_fonts = - base::bits::AlignDown(bytes_for_fonts, cc::PaintOpWriter::Alignment()); + bytes_for_fonts = base::bits::AlignDown( + bytes_for_fonts, + base::checked_cast<uint32_t>(cc::PaintOpWriter::Alignment())); FontSupport font_support; scoped_refptr<gpu::ServiceFontManager> font_manager( diff --git a/chromium/cc/paint/paint_op_buffer_serializer.cc b/chromium/cc/paint/paint_op_buffer_serializer.cc index b57a7309556..8d0411722b1 100644 --- a/chromium/cc/paint/paint_op_buffer_serializer.cc +++ b/chromium/cc/paint/paint_op_buffer_serializer.cc @@ -142,8 +142,7 @@ void PaintOpBufferSerializer::ClearForOpaqueRaster( SkClipOp::kDifference, false); SerializeOp(canvas, &inner_clip_op, nullptr, params); } - DrawColorOp clear_op(SkColor4f::FromColor(preamble.background_color), - SkBlendMode::kSrc); + DrawColorOp clear_op(preamble.background_color, SkBlendMode::kSrc); SerializeOp(canvas, &clear_op, nullptr, params); RestoreToCount(canvas, 1, params); } @@ -321,9 +320,9 @@ bool PaintOpBufferSerializer::SerializeOpWithFlags( uint8_t alpha) { // We use a null |image_provider| here because images are decoded during // serialization. - const ScopedRasterFlags scoped_flags(&flags_op->flags, nullptr, - canvas->getTotalMatrix(), - options_.max_texture_size, alpha); + const ScopedRasterFlags scoped_flags( + &flags_op->flags, nullptr, canvas->getTotalMatrix(), + options_.max_texture_size, alpha / 255.0f); const PaintFlags* flags_to_serialize = scoped_flags.flags(); if (!flags_to_serialize) return true; diff --git a/chromium/cc/paint/paint_op_buffer_serializer.h b/chromium/cc/paint/paint_op_buffer_serializer.h index c1f2ac7c06a..b44825b10b2 100644 --- a/chromium/cc/paint/paint_op_buffer_serializer.h +++ b/chromium/cc/paint/paint_op_buffer_serializer.h @@ -49,7 +49,7 @@ class CC_PAINT_EXPORT PaintOpBufferSerializer { // potentially being partially transparent, if post scaled). bool requires_clear = true; // If clearing is needed, the color to clear to. - SkColor background_color = SK_ColorTRANSPARENT; + SkColor4f background_color = SkColors::kTransparent; }; // Serialize the buffer with a preamble. This function wraps the buffer in a // save/restore and includes any translations, scales, and clearing as diff --git a/chromium/cc/paint/paint_op_buffer_unittest.cc b/chromium/cc/paint/paint_op_buffer_unittest.cc index 802675a4d1c..9c54c44c373 100644 --- a/chromium/cc/paint/paint_op_buffer_unittest.cc +++ b/chromium/cc/paint/paint_op_buffer_unittest.cc @@ -74,7 +74,7 @@ class PaintOpSerializationTestUtils { shader->start_radius_ = 13.4f; shader->tx_ = SkTileMode::kRepeat; shader->ty_ = SkTileMode::kMirror; - shader->fallback_color_ = SkColorSetARGB(254, 252, 250, 248); + shader->fallback_color_ = {0.99f, 0.98f, 0.97f, 0.99f}; shader->scaling_behavior_ = PaintShader::ScalingBehavior::kRasterAtScale; if (use_matrix) { shader->local_matrix_.emplace(SkMatrix::I()); @@ -88,8 +88,9 @@ class PaintOpSerializationTestUtils { shader->start_degrees_ = 123; shader->end_degrees_ = 456; // TODO(vmpstr): Add PaintImage/PaintRecord. - shader->colors_ = {SkColorSetARGB(1, 2, 3, 4), SkColorSetARGB(5, 6, 7, 8), - SkColorSetARGB(9, 0, 1, 2)}; + shader->colors_ = {{0.1f, 0.2f, 0.3f, 0.4f}, + {0.05f, 0.15f, 0.25f, 0.35f}, + {0.0f, 0.5f, 0.9f, 0.1f}}; shader->positions_ = {0.f, 0.4f, 1.f}; } }; @@ -230,12 +231,13 @@ TEST_F(PaintOpAppendTest, MoveThenReappendOperatorEq) { TEST(PaintOpBufferTest, SaveDrawRestore) { PaintOpBuffer buffer; - uint8_t alpha = 100; + float alpha = 0.4f; buffer.push<SaveLayerAlphaOp>(nullptr, alpha); + int paint_flags_alpha = 50; PaintFlags draw_flags; - draw_flags.setColor(SK_ColorMAGENTA); - draw_flags.setAlpha(50); + draw_flags.setColor(SkColors::kMagenta); + draw_flags.setAlpha(paint_flags_alpha); EXPECT_TRUE(draw_flags.SupportsFoldingAlpha()); SkRect rect = SkRect::MakeXYWH(1, 2, 3, 4); buffer.push<DrawRectOp>(rect, draw_flags); @@ -250,15 +252,15 @@ TEST(PaintOpBufferTest, SaveDrawRestore) { // Expect the alpha from the draw and the save layer to be folded together. // Since alpha is stored in a uint8_t and gets rounded, so use tolerance. - float expected_alpha = alpha * 50 / 255.f; - EXPECT_LE(std::abs(expected_alpha - canvas.paint_.getAlpha()), 1.f); + float expected_alpha = alpha * paint_flags_alpha / 255.0f; + EXPECT_LE(std::abs(expected_alpha - canvas.paint_.getAlphaf()), 0.01f); } // Verify that we don't optimize SaveLayerAlpha / DrawTextBlob / Restore. TEST(PaintOpBufferTest, SaveDrawTextBlobRestore) { PaintOpBuffer buffer; - uint8_t alpha = 100; + float alpha = 0.4f; buffer.push<SaveLayerAlphaOp>(nullptr, alpha); PaintFlags paint_flags; @@ -280,11 +282,11 @@ TEST(PaintOpBufferTest, SaveDrawTextBlobRestore) { TEST(PaintOpBufferTest, SaveDrawRestoreFail_BadFlags) { PaintOpBuffer buffer; - uint8_t alpha = 100; + float alpha = 0.4f; buffer.push<SaveLayerAlphaOp>(nullptr, alpha); PaintFlags draw_flags; - draw_flags.setColor(SK_ColorMAGENTA); + draw_flags.setColor(SkColors::kMagenta); draw_flags.setAlpha(50); draw_flags.setBlendMode(SkBlendMode::kSrc); EXPECT_FALSE(draw_flags.SupportsFoldingAlpha()); @@ -307,11 +309,11 @@ TEST(PaintOpBufferTest, SaveDrawRestoreFail_BadFlags) { TEST(PaintOpBufferTest, SaveDrawRestore_BadFlags255Alpha) { PaintOpBuffer buffer; - uint8_t alpha = 255; + float alpha = 1.0f; buffer.push<SaveLayerAlphaOp>(nullptr, alpha); PaintFlags draw_flags; - draw_flags.setColor(SK_ColorMAGENTA); + draw_flags.setColor(SkColors::kMagenta); draw_flags.setAlpha(50); draw_flags.setBlendMode(SkBlendMode::kColorBurn); EXPECT_FALSE(draw_flags.SupportsFoldingAlpha()); @@ -332,11 +334,11 @@ TEST(PaintOpBufferTest, SaveDrawRestore_BadFlags255Alpha) { TEST(PaintOpBufferTest, SaveDrawRestoreFail_TooManyOps) { PaintOpBuffer buffer; - uint8_t alpha = 100; + float alpha = 0.4f; buffer.push<SaveLayerAlphaOp>(nullptr, alpha); PaintFlags draw_flags; - draw_flags.setColor(SK_ColorMAGENTA); + draw_flags.setColor(SkColors::kMagenta); draw_flags.setAlpha(50); draw_flags.setBlendMode(SkBlendMode::kSrcOver); EXPECT_TRUE(draw_flags.SupportsFoldingAlpha()); @@ -359,7 +361,7 @@ TEST(PaintOpBufferTest, SaveDrawRestoreFail_TooManyOps) { TEST(PaintOpBufferTest, SaveDrawRestore_SingleOpNotADrawOp) { PaintOpBuffer buffer; - uint8_t alpha = 100; + float alpha = 0.4f; buffer.push<SaveLayerAlphaOp>(nullptr, alpha); buffer.push<NoopOp>(); @@ -377,9 +379,10 @@ TEST(PaintOpBufferTest, SaveDrawRestore_SingleOpNotADrawOp) { TEST(PaintOpBufferTest, SaveDrawRestore_SingleOpRecordWithSingleOp) { sk_sp<PaintRecord> record = sk_make_sp<PaintRecord>(); + int paint_flags_alpha = 50; PaintFlags draw_flags; - draw_flags.setColor(SK_ColorMAGENTA); - draw_flags.setAlpha(50); + draw_flags.setColor(SkColors::kMagenta); + draw_flags.setAlpha(paint_flags_alpha); EXPECT_TRUE(draw_flags.SupportsFoldingAlpha()); SkRect rect = SkRect::MakeXYWH(1, 2, 3, 4); record->push<DrawRectOp>(rect, draw_flags); @@ -387,7 +390,7 @@ TEST(PaintOpBufferTest, SaveDrawRestore_SingleOpRecordWithSingleOp) { PaintOpBuffer buffer; - uint8_t alpha = 100; + float alpha = 0.4f; buffer.push<SaveLayerAlphaOp>(nullptr, alpha); buffer.push<DrawRecordOp>(std::move(record)); buffer.push<RestoreOp>(); @@ -399,8 +402,8 @@ TEST(PaintOpBufferTest, SaveDrawRestore_SingleOpRecordWithSingleOp) { EXPECT_EQ(0, canvas.restore_count_); EXPECT_EQ(rect, canvas.draw_rect_); - float expected_alpha = alpha * 50 / 255.f; - EXPECT_LE(std::abs(expected_alpha - canvas.paint_.getAlpha()), 1.f); + float expected_alpha = alpha * paint_flags_alpha / 255.f; + EXPECT_LE(std::abs(expected_alpha - canvas.paint_.getAlphaf()), 0.01f); } // The same as the above SingleOpRecord test, but the single op is not @@ -415,7 +418,7 @@ TEST(PaintOpBufferTest, SaveDrawRestore_SingleOpRecordWithSingleNonDrawOp) { PaintOpBuffer buffer; - uint8_t alpha = 100; + float alpha = 0.4f; buffer.push<SaveLayerAlphaOp>(nullptr, alpha); buffer.push<DrawRecordOp>(std::move(record)); buffer.push<RestoreOp>(); @@ -429,7 +432,7 @@ TEST(PaintOpBufferTest, SaveDrawRestore_SingleOpRecordWithSingleNonDrawOp) { TEST(PaintOpBufferTest, SaveLayerRestore_DrawColor) { PaintOpBuffer buffer; - uint8_t alpha = 100; + float alpha = 0.4f; SkColor original = SkColorSetA(50, SK_ColorRED); buffer.push<SaveLayerAlphaOp>(nullptr, alpha); @@ -507,7 +510,7 @@ TEST(PaintOpBufferTest, DiscardableImagesTracking_OpWithFlags) { TEST(PaintOpBufferTest, SlowPaths) { auto buffer = sk_make_sp<PaintOpBuffer>(); - EXPECT_EQ(buffer->num_slow_paths(), 0); + EXPECT_EQ(buffer->num_slow_paths_up_to_min_for_MSAA(), 0); // Op without slow paths PaintFlags noop_flags; @@ -523,13 +526,13 @@ TEST(PaintOpBufferTest, SlowPaths) { line_effect_slow.setPathEffect(SkDashPathEffect::Make(intervals, 2, 0)); buffer->push<DrawLineOp>(1.f, 2.f, 3.f, 4.f, line_effect_slow); - EXPECT_EQ(buffer->num_slow_paths(), 1); + EXPECT_EQ(buffer->num_slow_paths_up_to_min_for_MSAA(), 1); // Line effect special case that Skia handles specially. PaintFlags line_effect = line_effect_slow; line_effect.setStrokeCap(PaintFlags::kButt_Cap); buffer->push<DrawLineOp>(1.f, 2.f, 3.f, 4.f, line_effect); - EXPECT_EQ(buffer->num_slow_paths(), 1); + EXPECT_EQ(buffer->num_slow_paths_up_to_min_for_MSAA(), 1); // Antialiased convex path is not slow. SkPath path; @@ -537,7 +540,7 @@ TEST(PaintOpBufferTest, SlowPaths) { EXPECT_TRUE(path.isConvex()); buffer->push<ClipPathOp>(path, SkClipOp::kIntersect, /*antialias=*/true, UsePaintCache::kDisabled); - EXPECT_EQ(buffer->num_slow_paths(), 1); + EXPECT_EQ(buffer->num_slow_paths_up_to_min_for_MSAA(), 1); // Concave paths are slow only when antialiased. SkPath concave = path; @@ -545,19 +548,19 @@ TEST(PaintOpBufferTest, SlowPaths) { EXPECT_FALSE(concave.isConvex()); buffer->push<ClipPathOp>(concave, SkClipOp::kIntersect, /*antialias=*/true, UsePaintCache::kDisabled); - EXPECT_EQ(buffer->num_slow_paths(), 2); + EXPECT_EQ(buffer->num_slow_paths_up_to_min_for_MSAA(), 2); buffer->push<ClipPathOp>(concave, SkClipOp::kIntersect, /*antialias=*/false, UsePaintCache::kDisabled); - EXPECT_EQ(buffer->num_slow_paths(), 2); + EXPECT_EQ(buffer->num_slow_paths_up_to_min_for_MSAA(), 2); // Drawing a record with slow paths into another adds the same // number of slow paths as the record. auto buffer2 = sk_make_sp<PaintOpBuffer>(); - EXPECT_EQ(0, buffer2->num_slow_paths()); + EXPECT_EQ(0, buffer2->num_slow_paths_up_to_min_for_MSAA()); buffer2->push<DrawRecordOp>(buffer); - EXPECT_EQ(2, buffer2->num_slow_paths()); + EXPECT_EQ(2, buffer2->num_slow_paths_up_to_min_for_MSAA()); buffer2->push<DrawRecordOp>(buffer); - EXPECT_EQ(4, buffer2->num_slow_paths()); + EXPECT_EQ(4, buffer2->num_slow_paths_up_to_min_for_MSAA()); } TEST(PaintOpBufferTest, NonAAPaint) { @@ -819,7 +822,7 @@ TEST_F(PaintOpBufferOffsetsTest, ContiguousIndicesWithSaveLayerAlphaRestore) { push_op<DrawColorOp>(SkColor4f::FromColor(0u), SkBlendMode::kClear); push_op<DrawColorOp>(SkColor4f::FromColor(1u), SkBlendMode::kClear); - uint8_t alpha = 100; + float alpha = 0.4f; push_op<SaveLayerAlphaOp>(nullptr, alpha); push_op<RestoreOp>(); push_op<DrawColorOp>(SkColor4f::FromColor(2u), SkBlendMode::kClear); @@ -845,7 +848,7 @@ TEST_F(PaintOpBufferOffsetsTest, push_op<DrawColorOp>(SkColor4f::FromColor(0u), SkBlendMode::kClear); push_op<DrawColorOp>(SkColor4f::FromColor(1u), SkBlendMode::kClear); - uint8_t alpha = 100; + float alpha = 0.4f; push_op<SaveLayerAlphaOp>(nullptr, alpha); push_op<DrawColorOp>(SkColor4f::FromColor(2u), SkBlendMode::kClear); push_op<DrawColorOp>(SkColor4f::FromColor(3u), SkBlendMode::kClear); @@ -895,7 +898,7 @@ TEST_F(PaintOpBufferOffsetsTest, add_draw_rect(0u); add_draw_rect(1u); - uint8_t alpha = 100; + float alpha = 0.4f; push_op<SaveLayerAlphaOp>(nullptr, alpha); add_draw_rect(2u); push_op<RestoreOp>(); @@ -928,7 +931,7 @@ TEST_F(PaintOpBufferOffsetsTest, add_draw_rect(0u); add_draw_rect(1u); - uint8_t alpha = 100; + float alpha = 0.4f; push_op<SaveLayerAlphaOp>(nullptr, alpha); add_draw_rect(2u); add_draw_rect(3u); @@ -987,7 +990,7 @@ TEST(PaintOpBufferTest, SaveLayerAlphaDrawRestoreWithBadBlendMode) { }; add_draw_rect(&buffer, 0u); - uint8_t alpha = 100; + float alpha = 0.4f; buffer.push<SaveLayerAlphaOp>(nullptr, alpha); add_draw_rect(&buffer, 1u); buffer.push<RestoreOp>(); @@ -1016,7 +1019,7 @@ TEST(PaintOpBufferTest, UnmatchedSaveRestoreNoSideEffects) { // Push 2 saves. - uint8_t alpha = 100; + float alpha = 0.4f; buffer.push<SaveLayerAlphaOp>(nullptr, alpha); add_draw_rect(&buffer, 0u); buffer.push<SaveLayerAlphaOp>(nullptr, alpha); @@ -1127,7 +1130,7 @@ std::vector<PaintFlags> test_flags = { flags.setStrokeJoin(PaintFlags::kBevel_Join); flags.setStyle(PaintFlags::kStroke_Style); flags.setFilterQuality(PaintFlags::FilterQuality::kMedium); - flags.setShader(PaintShader::MakeColor(SkColorSetARGB(1, 2, 3, 4))); + flags.setShader(PaintShader::MakeColor({0.1f, 0.2f, 0.3f, 0.4f})); return flags; }(), [] { @@ -1159,7 +1162,8 @@ std::vector<PaintFlags> test_flags = { looper_builder.addLayer(layer_info); flags.setLooper(looper_builder.detach()); - sk_sp<PaintShader> shader = PaintShader::MakeColor(SK_ColorTRANSPARENT); + sk_sp<PaintShader> shader = + PaintShader::MakeColor(SkColors::kTransparent); PaintOpSerializationTestUtils::FillArbitraryShaderValues(shader.get(), true); flags.setShader(std::move(shader)); @@ -1168,13 +1172,14 @@ std::vector<PaintFlags> test_flags = { }(), [] { PaintFlags flags; - flags.setShader(PaintShader::MakeColor(SkColorSetARGB(12, 34, 56, 78))); + flags.setShader(PaintShader::MakeColor({0.1f, 0.2f, 0.3f, 0.4f})); return flags; }(), [] { PaintFlags flags; - sk_sp<PaintShader> shader = PaintShader::MakeColor(SK_ColorTRANSPARENT); + sk_sp<PaintShader> shader = + PaintShader::MakeColor(SkColors::kTransparent); PaintOpSerializationTestUtils::FillArbitraryShaderValues(shader.get(), false); flags.setShader(std::move(shader)); @@ -1184,9 +1189,9 @@ std::vector<PaintFlags> test_flags = { [] { PaintFlags flags; SkPoint points[2] = {SkPoint::Make(1, 2), SkPoint::Make(3, 4)}; - SkColor colors[3] = {SkColorSetARGB(1, 2, 3, 4), - SkColorSetARGB(4, 3, 2, 1), - SkColorSetARGB(0, 10, 20, 30)}; + SkColor4f colors[3] = {{0.1f, 0.2f, 0.3f, 0.4f}, + {0.4f, 0.3f, 0.2f, 0.1f}, + {0.2f, 0.4f, 0.6f, 0.0f}}; SkScalar positions[3] = {0.f, 0.3f, 1.f}; flags.setShader(PaintShader::MakeLinearGradient(points, colors, positions, 3, SkTileMode::kMirror)); @@ -1195,9 +1200,9 @@ std::vector<PaintFlags> test_flags = { }(), [] { PaintFlags flags; - SkColor colors[3] = {SkColorSetARGB(1, 2, 3, 4), - SkColorSetARGB(4, 3, 2, 1), - SkColorSetARGB(0, 10, 20, 30)}; + SkColor4f colors[3] = {{0.1f, 0.2f, 0.3f, 0.4f}, + {0.4f, 0.3f, 0.2f, 0.1f}, + {0.2f, 0.4f, 0.6f, 0.0f}}; flags.setShader(PaintShader::MakeSweepGradient( 0.2f, -0.8f, colors, nullptr, 3, SkTileMode::kMirror, 10, 20)); return flags; @@ -1725,12 +1730,12 @@ void PushSaveLayerOps(PaintOpBuffer* buffer) { } void PushSaveLayerAlphaOps(PaintOpBuffer* buffer) { - size_t len = std::min(test_uint8s.size(), test_rects.size()); + size_t len = std::min(test_floats.size(), test_rects.size()); for (size_t i = 0; i < len; ++i) - buffer->push<SaveLayerAlphaOp>(&test_rects[i], test_uint8s[i]); + buffer->push<SaveLayerAlphaOp>(&test_rects[i], test_floats[i]); // Test optional args. - buffer->push<SaveLayerAlphaOp>(nullptr, test_uint8s[0]); + buffer->push<SaveLayerAlphaOp>(nullptr, test_floats[0]); ValidateOps<SaveLayerAlphaOp>(buffer); } @@ -2443,11 +2448,11 @@ TEST(PaintOpBufferTest, ClipsImagesDuringSerialization) { TEST(PaintOpBufferSerializationTest, AlphaFoldingDuringSerialization) { PaintOpBuffer buffer; - uint8_t alpha = 100; + float alpha = 0.4f; buffer.push<SaveLayerAlphaOp>(nullptr, alpha); PaintFlags draw_flags; - draw_flags.setColor(SK_ColorMAGENTA); + draw_flags.setColor(SkColors::kMagenta); draw_flags.setAlpha(50); SkRect rect = SkRect::MakeXYWH(1, 2, 3, 4); buffer.push<DrawRectOp>(rect, draw_flags); @@ -2497,7 +2502,7 @@ TEST(PaintOpBufferSerializationTest, AlphaFoldingDuringSerialization) { ASSERT_EQ(op->GetType(), PaintOpType::DrawRect); // Expect the alpha from the draw and the save layer to be folded together. // Since alpha is stored in a uint8_t and gets rounded, so use tolerance. - float expected_alpha = alpha * 50 / 255.f; + float expected_alpha = alpha * 50; EXPECT_LE(std::abs(expected_alpha - static_cast<const DrawRectOp*>(op)->flags.getAlpha()), 1.f); @@ -2730,7 +2735,7 @@ TEST(PaintOpBufferTest, ValidateRects) { buffer.push<DrawRectOp>(bad_rect, test_flags[0]); buffer.push<SaveLayerOp>(&bad_rect, nullptr); buffer.push<SaveLayerOp>(&bad_rect, &test_flags[0]); - buffer.push<SaveLayerAlphaOp>(&bad_rect, test_uint8s[0]); + buffer.push<SaveLayerAlphaOp>(&bad_rect, test_floats[0]); TestOptionsProvider options_provider; @@ -3387,7 +3392,7 @@ TEST_P(PaintFilterSerializationTest, Basic) { 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, + 5.f, 10.f, 0.1f, 0.3f, SkColors::kBlue, DropShadowPaintFilter::ShadowMode::kDrawShadowOnly, nullptr)}, sk_sp<PaintFilter>{new MagnifierPaintFilter(SkRect::MakeXYWH(5, 6, 7, 8), 10.5f, nullptr)}, @@ -3408,13 +3413,13 @@ TEST_P(PaintFilterSerializationTest, Basic) { SkMatrix::I(), PaintFlags::FilterQuality::kHigh, nullptr)}, sk_sp<PaintFilter>{new LightingDistantPaintFilter( PaintFilter::LightingType::kSpecular, SkPoint3::Make(1, 2, 3), - SK_ColorCYAN, 1.1f, 2.2f, 3.3f, nullptr)}, + SkColors::kCyan, 1.1f, 2.2f, 3.3f, nullptr)}, sk_sp<PaintFilter>{new LightingPointPaintFilter( PaintFilter::LightingType::kDiffuse, SkPoint3::Make(2, 3, 4), - SK_ColorRED, 1.2f, 3.4f, 5.6f, nullptr)}, + SkColors::kRed, 1.2f, 3.4f, 5.6f, nullptr)}, sk_sp<PaintFilter>{new LightingSpotPaintFilter( PaintFilter::LightingType::kSpecular, SkPoint3::Make(100, 200, 300), - SkPoint3::Make(400, 500, 600), 1, 2, SK_ColorMAGENTA, 3, 4, 5, + SkPoint3::Make(400, 500, 600), 1, 2, SkColors::kMagenta, 3, 4, 5, nullptr)}, sk_sp<PaintFilter>{ new ImagePaintFilter(CreateDiscardablePaintImage(gfx::Size(100, 100)), @@ -4141,7 +4146,7 @@ TEST(PaintOpBufferTest, HasEffectsPreventingLCDTextForSaveLayerAlpha) { TEST(PaintOpBufferTest, NeedsAdditionalInvalidationForLCDText) { auto buffer1 = sk_make_sp<PaintOpBuffer>(); - buffer1->push<SaveLayerAlphaOp>(nullptr, uint8_t{100}); + buffer1->push<SaveLayerAlphaOp>(nullptr, 0.4f); EXPECT_FALSE(buffer1->has_draw_text_ops()); EXPECT_TRUE(buffer1->has_save_layer_alpha_ops()); EXPECT_FALSE(buffer1->has_effects_preventing_lcd_text_for_save_layer_alpha()); diff --git a/chromium/cc/paint/paint_op_helper_unittest.cc b/chromium/cc/paint/paint_op_helper_unittest.cc index 8a4aa431d23..9c66ff044f3 100644 --- a/chromium/cc/paint/paint_op_helper_unittest.cc +++ b/chromium/cc/paint/paint_op_helper_unittest.cc @@ -272,7 +272,7 @@ TEST(PaintOpHelper, SaveLayerToString) { TEST(PaintOpHelper, SaveLayerAlphaToString) { SkRect bounds = SkRect::MakeXYWH(1, 2, 3, 4); - SaveLayerAlphaOp op(&bounds, 255); + SaveLayerAlphaOp op(&bounds, 1.0f); std::string str = PaintOpHelper::ToString(&op); EXPECT_EQ(str, "SaveLayerAlphaOp(bounds=[1.000,2.000 3.000x4.000], alpha=255)"); diff --git a/chromium/cc/paint/paint_op_perftest.cc b/chromium/cc/paint/paint_op_perftest.cc index 043d818068c..c3bba0bfdc5 100644 --- a/chromium/cc/paint/paint_op_perftest.cc +++ b/chromium/cc/paint/paint_op_perftest.cc @@ -137,7 +137,7 @@ TEST_F(PaintOpPerfTest, ManyFlagsOps) { looper_builder.addLayer(layer_info); flags.setLooper(looper_builder.detach()); - sk_sp<PaintShader> shader = PaintShader::MakeColor(SK_ColorTRANSPARENT); + sk_sp<PaintShader> shader = PaintShader::MakeColor(SkColors::kTransparent); flags.setShader(std::move(shader)); SkPath path; diff --git a/chromium/cc/paint/paint_op_reader.cc b/chromium/cc/paint/paint_op_reader.cc index 26278227497..6f9ad5cd016 100644 --- a/chromium/cc/paint/paint_op_reader.cc +++ b/chromium/cc/paint/paint_op_reader.cc @@ -566,7 +566,8 @@ void PaintOpReader::Read(sk_sp<PaintShader>* shader) { kInsufficientRemainingBytes_Read_PaintShader_ColorSize); return; } - size_t colors_bytes = colors_size * sizeof(SkColor); + size_t colors_bytes = + colors_size * (colors_size > 0 ? sizeof(ref.colors_[0]) : 0u); if (colors_bytes > remaining_bytes_) { SetInvalid(DeserializationError:: kInsufficientRemainingBytes_Read_PaintShader_ColorBytes); @@ -917,12 +918,12 @@ void PaintOpReader::ReadDropShadowPaintFilter( Read(&color); ReadEnum(&shadow_mode); Read(&input); - if (!valid_) return; - filter->reset(new DropShadowPaintFilter(dx, dy, sigma_x, sigma_y, color, - shadow_mode, std::move(input), - base::OptionalOrNullptr(crop_rect))); + // TODO(crbug/1308932): Remove FromColor and make all SkColor4f. + filter->reset(new DropShadowPaintFilter( + dx, dy, sigma_x, sigma_y, SkColor4f::FromColor(color), shadow_mode, + std::move(input), base::OptionalOrNullptr(crop_rect))); } void PaintOpReader::ReadMagnifierPaintFilter( @@ -1296,9 +1297,11 @@ void PaintOpReader::ReadLightingDistantPaintFilter( Read(&input); if (!valid_) return; + // TODO(crbug/1308932): Remove FromColor and make all SkColor4f. filter->reset(new LightingDistantPaintFilter( - lighting_type, direction, light_color, surface_scale, kconstant, - shininess, std::move(input), base::OptionalOrNullptr(crop_rect))); + lighting_type, direction, SkColor4f::FromColor(light_color), + surface_scale, kconstant, shininess, std::move(input), + base::OptionalOrNullptr(crop_rect))); } void PaintOpReader::ReadLightingPointPaintFilter( @@ -1321,9 +1324,11 @@ void PaintOpReader::ReadLightingPointPaintFilter( Read(&input); if (!valid_) return; + // TODO(crbug/1308932): Remove FromColor and make all SkColor4f. filter->reset(new LightingPointPaintFilter( - lighting_type, location, light_color, surface_scale, kconstant, shininess, - std::move(input), base::OptionalOrNullptr(crop_rect))); + lighting_type, location, SkColor4f::FromColor(light_color), surface_scale, + kconstant, shininess, std::move(input), + base::OptionalOrNullptr(crop_rect))); } void PaintOpReader::ReadLightingSpotPaintFilter( @@ -1353,10 +1358,11 @@ void PaintOpReader::ReadLightingSpotPaintFilter( if (!valid_) return; + // TODO(crbug/1308932): Remove FromColor and make all SkColor4f. filter->reset(new LightingSpotPaintFilter( lighting_type, location, target, specular_exponent, cutoff_angle, - light_color, surface_scale, kconstant, shininess, std::move(input), - base::OptionalOrNullptr(crop_rect))); + SkColor4f::FromColor(light_color), surface_scale, kconstant, shininess, + std::move(input), base::OptionalOrNullptr(crop_rect))); } void PaintOpReader::ReadStretchPaintFilter( diff --git a/chromium/cc/paint/paint_op_writer.cc b/chromium/cc/paint/paint_op_writer.cc index f899b75f254..497cdeed0db 100644 --- a/chromium/cc/paint/paint_op_writer.cc +++ b/chromium/cc/paint/paint_op_writer.cc @@ -544,7 +544,9 @@ void PaintOpWriter::Write(const PaintShader* shader, } WriteSize(shader->colors_.size()); - WriteData(shader->colors_.size() * sizeof(SkColor), shader->colors_.data()); + WriteData(shader->colors_.size() * + (shader->colors_.size() > 0 ? sizeof(shader->colors_[0]) : 0u), + shader->colors_.data()); WriteSize(shader->positions_.size()); WriteData(shader->positions_.size() * sizeof(SkScalar), @@ -715,7 +717,8 @@ void PaintOpWriter::Write(const DropShadowPaintFilter& filter, WriteSimple(filter.dy()); WriteSimple(filter.sigma_x()); WriteSimple(filter.sigma_y()); - WriteSimple(filter.color()); + // TODO(crbug/1308932): Remove toSkColor and make all SkColor4f. + WriteSimple(filter.color().toSkColor()); WriteEnum(filter.shadow_mode()); Write(filter.input().get(), current_ctm); } @@ -879,7 +882,8 @@ void PaintOpWriter::Write(const LightingDistantPaintFilter& filter, const SkM44& current_ctm) { WriteEnum(filter.lighting_type()); WriteSimple(filter.direction()); - WriteSimple(filter.light_color()); + // TODO(crbug/1308932): Remove toSkColor and make all SkColor4f. + WriteSimple(filter.light_color().toSkColor()); WriteSimple(filter.surface_scale()); WriteSimple(filter.kconstant()); WriteSimple(filter.shininess()); @@ -890,7 +894,8 @@ void PaintOpWriter::Write(const LightingPointPaintFilter& filter, const SkM44& current_ctm) { WriteEnum(filter.lighting_type()); WriteSimple(filter.location()); - WriteSimple(filter.light_color()); + // TODO(crbug/1308932): Remove toSkColor and make all SkColor4f. + WriteSimple(filter.light_color().toSkColor()); WriteSimple(filter.surface_scale()); WriteSimple(filter.kconstant()); WriteSimple(filter.shininess()); @@ -904,7 +909,8 @@ void PaintOpWriter::Write(const LightingSpotPaintFilter& filter, WriteSimple(filter.target()); WriteSimple(filter.specular_exponent()); WriteSimple(filter.cutoff_angle()); - WriteSimple(filter.light_color()); + // TODO(crbug/1308932): Remove toSkColor and make all SkColor4f. + WriteSimple(filter.light_color().toSkColor()); WriteSimple(filter.surface_scale()); WriteSimple(filter.kconstant()); WriteSimple(filter.shininess()); diff --git a/chromium/cc/paint/paint_shader.cc b/chromium/cc/paint/paint_shader.cc index 02031a92250..c96bf1c0650 100644 --- a/chromium/cc/paint/paint_shader.cc +++ b/chromium/cc/paint/paint_shader.cc @@ -65,7 +65,7 @@ sk_sp<PaintShader> PaintShader::MakeEmpty() { return shader; } -sk_sp<PaintShader> PaintShader::MakeColor(SkColor color) { +sk_sp<PaintShader> PaintShader::MakeColor(SkColor4f color) { sk_sp<PaintShader> shader(new PaintShader(Type::kColor)); // Just one color. Store it in the fallback color. Easy. @@ -76,13 +76,13 @@ sk_sp<PaintShader> PaintShader::MakeColor(SkColor color) { } sk_sp<PaintShader> PaintShader::MakeLinearGradient(const SkPoint points[], - const SkColor colors[], + const SkColor4f colors[], const SkScalar pos[], int count, SkTileMode mode, uint32_t flags, const SkMatrix* local_matrix, - SkColor fallback_color) { + SkColor4f fallback_color) { sk_sp<PaintShader> shader(new PaintShader(Type::kLinearGradient)); // There are always two points, the start and the end. @@ -98,13 +98,13 @@ sk_sp<PaintShader> PaintShader::MakeLinearGradient(const SkPoint points[], sk_sp<PaintShader> PaintShader::MakeRadialGradient(const SkPoint& center, SkScalar radius, - const SkColor colors[], + const SkColor4f colors[], const SkScalar pos[], int count, SkTileMode mode, uint32_t flags, const SkMatrix* local_matrix, - SkColor fallback_color) { + SkColor4f fallback_color) { sk_sp<PaintShader> shader(new PaintShader(Type::kRadialGradient)); shader->center_ = center; @@ -122,13 +122,13 @@ sk_sp<PaintShader> PaintShader::MakeTwoPointConicalGradient( SkScalar start_radius, const SkPoint& end, SkScalar end_radius, - const SkColor colors[], + const SkColor4f colors[], const SkScalar pos[], int count, SkTileMode mode, uint32_t flags, const SkMatrix* local_matrix, - SkColor fallback_color) { + SkColor4f fallback_color) { sk_sp<PaintShader> shader(new PaintShader(Type::kTwoPointConicalGradient)); shader->start_point_ = start; @@ -145,7 +145,7 @@ sk_sp<PaintShader> PaintShader::MakeTwoPointConicalGradient( sk_sp<PaintShader> PaintShader::MakeSweepGradient(SkScalar cx, SkScalar cy, - const SkColor colors[], + const SkColor4f colors[], const SkScalar pos[], int color_count, SkTileMode mode, @@ -153,7 +153,7 @@ sk_sp<PaintShader> PaintShader::MakeSweepGradient(SkScalar cx, SkScalar end_degrees, uint32_t flags, const SkMatrix* local_matrix, - SkColor fallback_color) { + SkColor4f fallback_color) { sk_sp<PaintShader> shader(new PaintShader(Type::kSweepGradient)); shader->center_ = SkPoint::Make(cx, cy); @@ -223,7 +223,7 @@ size_t PaintShader::GetSerializedSize(const PaintShader* shader) { sizeof(shader->id_) + PaintOpWriter::GetRecordSize(shader->record_.get()) + sizeof(shader->colors_.size()) + - shader->colors_.size() * sizeof(SkColor) + + shader->colors_.size() * sizeof(SkColor4f) + sizeof(shader->positions_.size()) + shader->positions_.size() * sizeof(SkScalar); } @@ -386,6 +386,13 @@ sk_sp<SkShader> PaintShader::GetSkShader( SkSamplingOptions sampling( PaintFlags::FilterQualityToSkSamplingOptions(quality)); + // TODO(crbug/1308932): Remove this helper vector colors and make all + // SkColor4f. + std::vector<SkColor> colors; + colors.reserve(colors.size()); + for (auto& c : colors_) + colors.push_back(c.toSkColor()); + switch (shader_type_) { case Type::kEmpty: return SkShaders::Empty(); @@ -394,27 +401,32 @@ sk_sp<SkShader> PaintShader::GetSkShader( break; case Type::kLinearGradient: { SkPoint points[2] = {start_point_, end_point_}; + // TODO(crbug/1308932): Remove this helper vector colors and make all + // SkColor4f. return SkGradientShader::MakeLinear( - points, colors_.data(), + points, colors.data(), positions_.empty() ? nullptr : positions_.data(), - static_cast<int>(colors_.size()), tx_, flags_, + static_cast<int>(colors.size()), tx_, flags_, base::OptionalOrNullptr(local_matrix_)); } case Type::kRadialGradient: return SkGradientShader::MakeRadial( center_, start_radius_, colors_.data(), + nullptr /*sk_sp<SkColorSpace>*/, positions_.empty() ? nullptr : positions_.data(), static_cast<int>(colors_.size()), tx_, flags_, base::OptionalOrNullptr(local_matrix_)); case Type::kTwoPointConicalGradient: return SkGradientShader::MakeTwoPointConical( start_point_, start_radius_, end_point_, end_radius_, colors_.data(), + nullptr /*sk_sp<SkColorSpace>*/, positions_.empty() ? nullptr : positions_.data(), static_cast<int>(colors_.size()), tx_, flags_, base::OptionalOrNullptr(local_matrix_)); case Type::kSweepGradient: return SkGradientShader::MakeSweep( center_.x(), center_.y(), colors_.data(), + nullptr /*sk_sp<SkColorSpace>*/, positions_.empty() ? nullptr : positions_.data(), static_cast<int>(colors_.size()), tx_, start_degrees_, end_degrees_, flags_, base::OptionalOrNullptr(local_matrix_)); @@ -453,7 +465,7 @@ sk_sp<SkShader> PaintShader::GetSkShader( // If we didn't create a shader for whatever reason, create a fallback // color one. - return SkShaders::Color(fallback_color_); + return SkShaders::Color(fallback_color_.toSkColor()); } void PaintShader::ResolveSkObjects(const gfx::SizeF* raster_scale, @@ -476,7 +488,7 @@ void PaintShader::ResolveSkObjects(const gfx::SizeF* raster_scale, } } -void PaintShader::SetColorsAndPositions(const SkColor* colors, +void PaintShader::SetColorsAndPositions(const SkColor4f* colors, const SkScalar* positions, int count) { #if DCHECK_IS_ON() @@ -498,7 +510,8 @@ void PaintShader::SetMatrixAndTiling(const SkMatrix* matrix, ty_ = ty; } -void PaintShader::SetFlagsAndFallback(uint32_t flags, SkColor fallback_color) { +void PaintShader::SetFlagsAndFallback(uint32_t flags, + SkColor4f fallback_color) { flags_ = flags; fallback_color_ = fallback_color; } @@ -516,7 +529,7 @@ bool PaintShader::IsOpaque() const { if (tx_ == SkTileMode::kDecal) return false; for (const auto& c : colors_) - if (SkColorGetA(c) != 0xFF) + if (!c.isOpaque()) return false; return true; case Type::kTwoPointConicalGradient: @@ -538,7 +551,7 @@ bool PaintShader::IsOpaque() const { NOTREACHED(); break; } - return SkColorGetA(fallback_color_) == 0xFF; + return fallback_color_.isOpaque(); } bool PaintShader::IsValid() const { diff --git a/chromium/cc/paint/paint_shader.h b/chromium/cc/paint/paint_shader.h index 80519ed3e89..b5317433a8c 100644 --- a/chromium/cc/paint/paint_shader.h +++ b/chromium/cc/paint/paint_shader.h @@ -53,47 +53,47 @@ class CC_PAINT_EXPORT PaintShader : public SkRefCnt { static sk_sp<PaintShader> MakeEmpty(); - static sk_sp<PaintShader> MakeColor(SkColor color); + static sk_sp<PaintShader> MakeColor(SkColor4f color); // TODO(crbug.com/1155544) SkMatrix is deprecated in favor of SkM44. static sk_sp<PaintShader> MakeLinearGradient( const SkPoint* points, - const SkColor* colors, + const SkColor4f colors[], const SkScalar* pos, int count, SkTileMode mode, uint32_t flags = 0, const SkMatrix* local_matrix = nullptr, - SkColor fallback_color = SK_ColorTRANSPARENT); + SkColor4f fallback_color = SkColors::kTransparent); static sk_sp<PaintShader> MakeRadialGradient( const SkPoint& center, SkScalar radius, - const SkColor colors[], + const SkColor4f colors[], const SkScalar pos[], int color_count, SkTileMode mode, uint32_t flags = 0, const SkMatrix* local_matrix = nullptr, - SkColor fallback_color = SK_ColorTRANSPARENT); + SkColor4f fallback_color = SkColors::kTransparent); static sk_sp<PaintShader> MakeTwoPointConicalGradient( const SkPoint& start, SkScalar start_radius, const SkPoint& end, SkScalar end_radius, - const SkColor colors[], + const SkColor4f colors[], const SkScalar pos[], int color_count, SkTileMode mode, uint32_t flags = 0, const SkMatrix* local_matrix = nullptr, - SkColor fallback_color = SK_ColorTRANSPARENT); + SkColor4f fallback_color = SkColors::kTransparent); static sk_sp<PaintShader> MakeSweepGradient( SkScalar cx, SkScalar cy, - const SkColor colors[], + const SkColor4f colors[], const SkScalar pos[], int color_count, SkTileMode mode, @@ -101,7 +101,7 @@ class CC_PAINT_EXPORT PaintShader : public SkRefCnt { SkScalar end_degrees, uint32_t flags = 0, const SkMatrix* local_matrix = nullptr, - SkColor fallback_color = SK_ColorTRANSPARENT); + SkColor4f fallback_color = SkColors::kTransparent); // |tile_rect| is not null only if the |image| is paint worklet backed. static sk_sp<PaintShader> MakeImage(const PaintImage& image, @@ -226,11 +226,11 @@ class CC_PAINT_EXPORT PaintShader : public SkRefCnt { sk_sp<PaintShader> CreatePaintWorkletRecord( ImageProvider* image_provider) const; - void SetColorsAndPositions(const SkColor* colors, + void SetColorsAndPositions(const SkColor4f* colors, const SkScalar* positions, int count); void SetMatrixAndTiling(const SkMatrix* matrix, SkTileMode tx, SkTileMode ty); - void SetFlagsAndFallback(uint32_t flags, SkColor fallback_color); + void SetFlagsAndFallback(uint32_t flags, SkColor4f fallback_color); Type shader_type_ = Type::kShaderCount; @@ -239,7 +239,7 @@ class CC_PAINT_EXPORT PaintShader : public SkRefCnt { SkScalar start_radius_ = 0; SkTileMode tx_ = SkTileMode::kClamp; SkTileMode ty_ = SkTileMode::kClamp; - SkColor fallback_color_ = SK_ColorTRANSPARENT; + SkColor4f fallback_color_ = SkColors::kTransparent; ScalingBehavior scaling_behavior_ = ScalingBehavior::kRasterAtScale; absl::optional<SkMatrix> local_matrix_; @@ -260,10 +260,10 @@ class CC_PAINT_EXPORT PaintShader : public SkRefCnt { // will be rasterized. absl::optional<gfx::SizeF> tile_scale_; - std::vector<SkColor> colors_; + std::vector<SkColor4f> colors_; std::vector<SkScalar> positions_; - // Cached intermediates, for cc::Paint objects that may not be thread-safe + // Cached intermediates, for Paint objects that may not be thread-safe sk_sp<SkPicture> sk_cached_picture_; sk_sp<SkImage> sk_cached_image_; diff --git a/chromium/cc/paint/paint_worklet_input.cc b/chromium/cc/paint/paint_worklet_input.cc index 4efca99b90e..be838a57cb6 100644 --- a/chromium/cc/paint/paint_worklet_input.cc +++ b/chromium/cc/paint/paint_worklet_input.cc @@ -55,7 +55,7 @@ PaintWorkletInput::PropertyValue::PropertyValue() = default; PaintWorkletInput::PropertyValue::PropertyValue(float value) : float_value(value) {} -PaintWorkletInput::PropertyValue::PropertyValue(SkColor value) +PaintWorkletInput::PropertyValue::PropertyValue(SkColor4f value) : color_value(value) {} PaintWorkletInput::PropertyValue::PropertyValue(const PropertyValue& other) = diff --git a/chromium/cc/paint/paint_worklet_input.h b/chromium/cc/paint/paint_worklet_input.h index 88b4d2fa67e..9a76ec28391 100644 --- a/chromium/cc/paint/paint_worklet_input.h +++ b/chromium/cc/paint/paint_worklet_input.h @@ -70,13 +70,13 @@ class CC_PAINT_EXPORT PaintWorkletInput struct CC_PAINT_EXPORT PropertyValue { PropertyValue(); explicit PropertyValue(float value); - explicit PropertyValue(SkColor value); + explicit PropertyValue(SkColor4f value); PropertyValue(const PropertyValue&); ~PropertyValue(); bool has_value() const; void reset(); absl::optional<float> float_value; - absl::optional<SkColor> color_value; + absl::optional<SkColor4f> color_value; }; virtual gfx::SizeF GetSize() const = 0; diff --git a/chromium/cc/paint/record_paint_canvas.cc b/chromium/cc/paint/record_paint_canvas.cc index 0f917af47e1..1ddabced058 100644 --- a/chromium/cc/paint/record_paint_canvas.cc +++ b/chromium/cc/paint/record_paint_canvas.cc @@ -95,7 +95,7 @@ int RecordPaintCanvas::saveLayer(const SkRect* bounds, } int RecordPaintCanvas::saveLayerAlpha(const SkRect* bounds, uint8_t alpha) { - push<SaveLayerAlphaOp>(bounds, alpha); + push<SaveLayerAlphaOp>(bounds, static_cast<float>(alpha / 255.0f)); return GetCanvas()->saveLayerAlpha(bounds, alpha); } @@ -227,12 +227,12 @@ bool RecordPaintCanvas::getDeviceClipBounds(SkIRect* bounds) const { return GetCanvas()->getDeviceClipBounds(bounds); } -void RecordPaintCanvas::drawColor(SkColor color, SkBlendMode mode) { - push<DrawColorOp>(SkColor4f::FromColor(color), mode); +void RecordPaintCanvas::drawColor(SkColor4f color, SkBlendMode mode) { + push<DrawColorOp>(color, mode); } -void RecordPaintCanvas::clear(SkColor color) { - push<DrawColorOp>(SkColor4f::FromColor(color), SkBlendMode::kSrc); +void RecordPaintCanvas::clear(SkColor4f color) { + push<DrawColorOp>(color, SkBlendMode::kSrc); } void RecordPaintCanvas::drawLine(SkScalar x0, diff --git a/chromium/cc/paint/record_paint_canvas.h b/chromium/cc/paint/record_paint_canvas.h index 31d36d54b60..cda8ce7ad82 100644 --- a/chromium/cc/paint/record_paint_canvas.h +++ b/chromium/cc/paint/record_paint_canvas.h @@ -63,8 +63,8 @@ class CC_PAINT_EXPORT RecordPaintCanvas : public PaintCanvas { bool getLocalClipBounds(SkRect* bounds) const override; SkIRect getDeviceClipBounds() const override; bool getDeviceClipBounds(SkIRect* bounds) const override; - void drawColor(SkColor color, SkBlendMode mode) override; - void clear(SkColor color) override; + void drawColor(SkColor4f color, SkBlendMode mode) override; + void clear(SkColor4f color) override; void drawLine(SkScalar x0, SkScalar y0, diff --git a/chromium/cc/paint/scoped_raster_flags.cc b/chromium/cc/paint/scoped_raster_flags.cc index 171b5514ae3..b6260303e7a 100644 --- a/chromium/cc/paint/scoped_raster_flags.cc +++ b/chromium/cc/paint/scoped_raster_flags.cc @@ -4,41 +4,14 @@ #include "cc/paint/scoped_raster_flags.h" +#include <utility> + #include "cc/paint/image_provider.h" #include "cc/paint/image_transfer_cache_entry.h" #include "cc/paint/paint_filter.h" #include "cc/paint/paint_image_builder.h" namespace cc { -ScopedRasterFlags::ScopedRasterFlags(const PaintFlags* flags, - ImageProvider* image_provider, - const SkMatrix& ctm, - int max_texture_size, - uint8_t alpha) - : original_flags_(flags) { - if (image_provider) { - decode_stashing_image_provider_.emplace(image_provider); - - // We skip the op if any images fail to decode. - DecodeImageShader(ctm); - if (decode_failed_) - return; - DecodeRecordShader(ctm, max_texture_size); - if (decode_failed_) - return; - DecodeFilter(); - if (decode_failed_) - return; - } - - if (alpha != 255) { - DCHECK(flags->SupportsFoldingAlpha()); - MutableFlags()->setAlpha(SkMulDiv255Round(flags->getAlpha(), alpha)); - } - - AdjustStrokeIfNeeded(ctm); -} - ScopedRasterFlags::~ScopedRasterFlags() = default; void ScopedRasterFlags::DecodeImageShader(const SkMatrix& ctm) { diff --git a/chromium/cc/paint/scoped_raster_flags.h b/chromium/cc/paint/scoped_raster_flags.h index 792cbc19fdd..0c2dcf01254 100644 --- a/chromium/cc/paint/scoped_raster_flags.h +++ b/chromium/cc/paint/scoped_raster_flags.h @@ -18,11 +18,35 @@ namespace cc { class CC_PAINT_EXPORT ScopedRasterFlags { public: // |flags| and |image_provider| must outlive this class. + template <class F, class = std::enable_if_t<std::is_same_v<F, float>>> ScopedRasterFlags(const PaintFlags* flags, ImageProvider* image_provider, const SkMatrix& ctm, int max_texture_size, - uint8_t alpha); + F alpha) + : original_flags_(flags) { + if (image_provider) { + decode_stashing_image_provider_.emplace(image_provider); + + // We skip the op if any images fail to decode. + DecodeImageShader(ctm); + if (decode_failed_) + return; + DecodeRecordShader(ctm, max_texture_size); + if (decode_failed_) + return; + DecodeFilter(); + if (decode_failed_) + return; + } + + if (alpha != 1.0f) { + DCHECK(flags->SupportsFoldingAlpha()); + MutableFlags()->setAlphaf(flags->getAlphaf() * alpha); + } + + AdjustStrokeIfNeeded(ctm); + } ScopedRasterFlags(const ScopedRasterFlags&) = delete; ~ScopedRasterFlags(); diff --git a/chromium/cc/paint/scoped_raster_flags_unittest.cc b/chromium/cc/paint/scoped_raster_flags_unittest.cc index 232bf0ed06b..78e1f3e616c 100644 --- a/chromium/cc/paint/scoped_raster_flags_unittest.cc +++ b/chromium/cc/paint/scoped_raster_flags_unittest.cc @@ -81,7 +81,7 @@ TEST(ScopedRasterFlagsTest, DecodePaintWorkletImageShader) { flags.setShader(shader); MockPaintWorkletImageProvider provider; - ScopedRasterFlags scoped_flags(&flags, &provider, SkMatrix::I(), 0, 255); + ScopedRasterFlags scoped_flags(&flags, &provider, SkMatrix::I(), 0, 1.0f); ASSERT_TRUE(scoped_flags.flags()); EXPECT_TRUE(scoped_flags.flags()->getShader()->shader_type() == PaintShader::Type::kPaintRecord); @@ -104,7 +104,7 @@ TEST(ScopedRasterFlagsTest, KeepsDecodesAlive) { PaintFlags flags; flags.setShader(record_shader); { - ScopedRasterFlags scoped_flags(&flags, &provider, SkMatrix::I(), 0, 255); + ScopedRasterFlags scoped_flags(&flags, &provider, SkMatrix::I(), 0, 1.0f); ASSERT_TRUE(scoped_flags.flags()); EXPECT_NE(scoped_flags.flags(), &flags); SkPaint paint = scoped_flags.flags()->ToSkPaint(); @@ -116,13 +116,13 @@ TEST(ScopedRasterFlagsTest, KeepsDecodesAlive) { TEST(ScopedRasterFlagsTest, NoImageProvider) { PaintFlags flags; - flags.setAlpha(255); + flags.setAlphaf(1.0f); flags.setShader(PaintShader::MakeImage( CreateDiscardablePaintImage(gfx::Size(10, 10)), SkTileMode::kClamp, SkTileMode::kClamp, &SkMatrix::I())); - ScopedRasterFlags scoped_flags(&flags, nullptr, SkMatrix::I(), 0, 10); + ScopedRasterFlags scoped_flags(&flags, nullptr, SkMatrix::I(), 0, 0.1f); EXPECT_NE(scoped_flags.flags(), &flags); - EXPECT_EQ(scoped_flags.flags()->getAlpha(), SkMulDiv255Round(255, 10)); + EXPECT_EQ(scoped_flags.flags()->getAlphaf(), 1.0f * 0.1f); } TEST(ScopedRasterFlagsTest, ThinAliasedStroke) { @@ -133,21 +133,21 @@ TEST(ScopedRasterFlagsTest, ThinAliasedStroke) { struct { SkMatrix ctm; - uint8_t alpha; + float alpha; bool expect_same_flags; bool expect_aa; float expect_stroke_width; - uint8_t expect_alpha; + float expect_alpha; } tests[] = { // No downscaling => no stroke change. - {SkMatrix::Scale(1.0f, 1.0f), 255, true, false, 1.0f, 0xFF}, + {SkMatrix::Scale(1.0f, 1.0f), 1.0f, true, false, 1.0f, 1.0f}, // Symmetric downscaling => modulated hairline stroke. - {SkMatrix::Scale(0.5f, 0.5f), 255, false, false, 0.0f, 0x80}, + {SkMatrix::Scale(0.5f, 0.5f), 1.0f, false, false, 0.0f, 0.5f}, // Symmetric downscaling w/ alpha => modulated hairline stroke. - {SkMatrix::Scale(0.5f, 0.5f), 127, false, false, 0.0f, 0x40}, + {SkMatrix::Scale(0.5f, 0.5f), 0.5f, false, false, 0.0f, 0.25f}, // Anisotropic scaling => AA stroke. - {SkMatrix::Scale(0.5f, 1.5f), 255, false, true, 1.0f, 0xFF}, + {SkMatrix::Scale(0.5f, 1.5f), 1.0f, false, true, 1.0f, 1.0f}, }; for (const auto& test : tests) { @@ -157,7 +157,8 @@ TEST(ScopedRasterFlagsTest, ThinAliasedStroke) { EXPECT_EQ(scoped_flags.flags() == &flags, test.expect_same_flags); EXPECT_EQ(scoped_flags.flags()->isAntiAlias(), test.expect_aa); EXPECT_EQ(scoped_flags.flags()->getStrokeWidth(), test.expect_stroke_width); - EXPECT_EQ(scoped_flags.flags()->getAlpha(), test.expect_alpha); + EXPECT_LE(std::abs(scoped_flags.flags()->getAlphaf() - test.expect_alpha), + 0.01f); } } diff --git a/chromium/cc/paint/skia_paint_canvas.cc b/chromium/cc/paint/skia_paint_canvas.cc index d90b124fb79..bd49fa4cc1f 100644 --- a/chromium/cc/paint/skia_paint_canvas.cc +++ b/chromium/cc/paint/skia_paint_canvas.cc @@ -160,11 +160,11 @@ bool SkiaPaintCanvas::getDeviceClipBounds(SkIRect* bounds) const { return canvas_->getDeviceClipBounds(bounds); } -void SkiaPaintCanvas::drawColor(SkColor color, SkBlendMode mode) { +void SkiaPaintCanvas::drawColor(SkColor4f color, SkBlendMode mode) { canvas_->drawColor(color, mode); } -void SkiaPaintCanvas::clear(SkColor color) { +void SkiaPaintCanvas::clear(SkColor4f color) { canvas_->clear(color); } @@ -175,7 +175,7 @@ void SkiaPaintCanvas::drawLine(SkScalar x0, const PaintFlags& flags) { ScopedRasterFlags raster_flags(&flags, image_provider_, canvas_->getTotalMatrix(), GetMaxTextureSize(), - 255u); + 1.0f); if (!raster_flags.flags()) return; @@ -189,7 +189,7 @@ void SkiaPaintCanvas::drawLine(SkScalar x0, void SkiaPaintCanvas::drawRect(const SkRect& rect, const PaintFlags& flags) { ScopedRasterFlags raster_flags(&flags, image_provider_, canvas_->getTotalMatrix(), GetMaxTextureSize(), - 255u); + 1.0f); if (!raster_flags.flags()) return; raster_flags.flags()->DrawToSk( @@ -201,7 +201,7 @@ void SkiaPaintCanvas::drawRect(const SkRect& rect, const PaintFlags& flags) { void SkiaPaintCanvas::drawIRect(const SkIRect& rect, const PaintFlags& flags) { ScopedRasterFlags raster_flags(&flags, image_provider_, canvas_->getTotalMatrix(), GetMaxTextureSize(), - 255u); + 1.0f); if (!raster_flags.flags()) return; raster_flags.flags()->DrawToSk( @@ -213,7 +213,7 @@ void SkiaPaintCanvas::drawIRect(const SkIRect& rect, const PaintFlags& flags) { void SkiaPaintCanvas::drawOval(const SkRect& oval, const PaintFlags& flags) { ScopedRasterFlags raster_flags(&flags, image_provider_, canvas_->getTotalMatrix(), GetMaxTextureSize(), - 255u); + 1.0f); if (!raster_flags.flags()) return; raster_flags.flags()->DrawToSk( @@ -225,7 +225,7 @@ void SkiaPaintCanvas::drawOval(const SkRect& oval, const PaintFlags& flags) { void SkiaPaintCanvas::drawRRect(const SkRRect& rrect, const PaintFlags& flags) { ScopedRasterFlags raster_flags(&flags, image_provider_, canvas_->getTotalMatrix(), GetMaxTextureSize(), - 255u); + 1.0f); if (!raster_flags.flags()) return; raster_flags.flags()->DrawToSk( @@ -239,7 +239,7 @@ void SkiaPaintCanvas::drawDRRect(const SkRRect& outer, const PaintFlags& flags) { ScopedRasterFlags raster_flags(&flags, image_provider_, canvas_->getTotalMatrix(), GetMaxTextureSize(), - 255u); + 1.0f); if (!raster_flags.flags()) return; raster_flags.flags()->DrawToSk( @@ -255,7 +255,7 @@ void SkiaPaintCanvas::drawRoundRect(const SkRect& rect, const PaintFlags& flags) { ScopedRasterFlags raster_flags(&flags, image_provider_, canvas_->getTotalMatrix(), GetMaxTextureSize(), - 255u); + 1.0f); if (!raster_flags.flags()) return; raster_flags.flags()->DrawToSk( @@ -270,7 +270,7 @@ void SkiaPaintCanvas::drawPath(const SkPath& path, UsePaintCache) { ScopedRasterFlags raster_flags(&flags, image_provider_, canvas_->getTotalMatrix(), GetMaxTextureSize(), - 255u); + 1.0f); if (!raster_flags.flags()) return; raster_flags.flags()->DrawToSk( @@ -288,7 +288,7 @@ void SkiaPaintCanvas::drawImage(const PaintImage& image, absl::optional<ScopedRasterFlags> scoped_flags; if (flags) { scoped_flags.emplace(flags, image_provider_, canvas_->getTotalMatrix(), - GetMaxTextureSize(), 255u); + GetMaxTextureSize(), 1.0f); if (!scoped_flags->flags()) return; } @@ -309,7 +309,7 @@ void SkiaPaintCanvas::drawImageRect(const PaintImage& image, absl::optional<ScopedRasterFlags> scoped_flags; if (flags) { scoped_flags.emplace(flags, image_provider_, canvas_->getTotalMatrix(), - GetMaxTextureSize(), 255u); + GetMaxTextureSize(), 1.0f); if (!scoped_flags->flags()) return; } @@ -347,7 +347,7 @@ void SkiaPaintCanvas::drawTextBlob(sk_sp<SkTextBlob> blob, const PaintFlags& flags) { ScopedRasterFlags raster_flags(&flags, image_provider_, canvas_->getTotalMatrix(), GetMaxTextureSize(), - 255u); + 1.0f); if (!raster_flags.flags()) return; raster_flags.flags()->DrawToSk(canvas_, diff --git a/chromium/cc/paint/skia_paint_canvas.h b/chromium/cc/paint/skia_paint_canvas.h index e4f8131228f..09e7d3b8f9f 100644 --- a/chromium/cc/paint/skia_paint_canvas.h +++ b/chromium/cc/paint/skia_paint_canvas.h @@ -103,8 +103,8 @@ class CC_PAINT_EXPORT SkiaPaintCanvas final : public PaintCanvas { bool getLocalClipBounds(SkRect* bounds) const override; SkIRect getDeviceClipBounds() const override; bool getDeviceClipBounds(SkIRect* bounds) const override; - void drawColor(SkColor color, SkBlendMode mode) override; - void clear(SkColor color) override; + void drawColor(SkColor4f color, SkBlendMode mode) override; + void clear(SkColor4f color) override; void drawLine(SkScalar x0, SkScalar y0, diff --git a/chromium/cc/paint/skia_paint_image_generator.cc b/chromium/cc/paint/skia_paint_image_generator.cc index a49188fd299..37df37e1e02 100644 --- a/chromium/cc/paint/skia_paint_image_generator.cc +++ b/chromium/cc/paint/skia_paint_image_generator.cc @@ -41,8 +41,8 @@ bool SkiaPaintImageGenerator::onQueryYUVAInfo( } bool SkiaPaintImageGenerator::onGetYUVAPlanes(const SkYUVAPixmaps& planes) { - return paint_image_generator_->GetYUVAPlanes(planes, frame_index_, - uniqueID()); + return paint_image_generator_->GetYUVAPlanes(planes, frame_index_, uniqueID(), + client_id_); } } // namespace cc diff --git a/chromium/cc/paint/skottie_wrapper_impl.cc b/chromium/cc/paint/skottie_wrapper_impl.cc index 703575304f0..85901b8d48e 100644 --- a/chromium/cc/paint/skottie_wrapper_impl.cc +++ b/chromium/cc/paint/skottie_wrapper_impl.cc @@ -295,6 +295,7 @@ class SkottieWrapperImpl : public SkottieWrapper { void Seek(float t, FrameDataCallback frame_data_cb) override LOCKS_EXCLUDED(lock_) { + TRACE_EVENT1("cc", "SkottieWrapperImpl::Seek", "timestamp", t); base::AutoLock lock(lock_); // There's no need to reset |current_frame_data_cb_| to null when finished. // The callback is guaranteed to only be invoked synchronously during calls @@ -310,6 +311,7 @@ class SkottieWrapperImpl : public SkottieWrapper { const SkottieColorMap& color_map, const SkottieTextPropertyValueMap& text_map) override LOCKS_EXCLUDED(lock_) { + TRACE_EVENT1("cc", "SkottieWrapperImpl::Draw", "timestamp", t); base::AutoLock lock(lock_); current_frame_data_cb_ = std::move(frame_data_cb); property_manager_->color_property_manager().SetNewValues(color_map); diff --git a/chromium/cc/paint/solid_color_analyzer_unittest.cc b/chromium/cc/paint/solid_color_analyzer_unittest.cc index a343e1d4cc5..aac6c8a4f9b 100644 --- a/chromium/cc/paint/solid_color_analyzer_unittest.cc +++ b/chromium/cc/paint/solid_color_analyzer_unittest.cc @@ -81,21 +81,21 @@ TEST_F(SolidColorAnalyzerTest, Empty) { TEST_F(SolidColorAnalyzerTest, ClearTransparent) { Initialize(); SkColor4f color = SkColor4f::FromColor(SkColorSetARGB(0, 12, 34, 56)); - canvas()->clear(color.toSkColor()); + canvas()->clear(color); EXPECT_EQ(SkColors::kTransparent, GetColor()); } TEST_F(SolidColorAnalyzerTest, ClearSolid) { Initialize(); SkColor4f color = SkColor4f::FromColor(SkColorSetARGB(255, 65, 43, 21)); - canvas()->clear(color.toSkColor()); + canvas()->clear(color); EXPECT_EQ(color, GetColor()); } TEST_F(SolidColorAnalyzerTest, ClearTranslucent) { Initialize(); SkColor4f color = SkColor4f::FromColor(SkColorSetARGB(128, 11, 22, 33)); - canvas()->clear(color.toSkColor()); + canvas()->clear(color); #if BUILDFLAG(IS_MAC) // TODO(andrescj): remove the special treatment of OS_MAC once // https://crbug.com/922899 is fixed. @@ -108,7 +108,7 @@ TEST_F(SolidColorAnalyzerTest, ClearTranslucent) { TEST_F(SolidColorAnalyzerTest, DrawColor) { Initialize(); SkColor4f color = SkColor4f::FromColor(SkColorSetARGB(255, 11, 22, 33)); - canvas()->drawColor(color.toSkColor()); + canvas()->drawColor(color); EXPECT_EQ(color, GetColor()); } @@ -197,8 +197,8 @@ TEST_F(SolidColorAnalyzerTest, DrawRectWithTranslateSolid) { TEST_F(SolidColorAnalyzerTest, TwoOpsNotSolid) { Initialize(); SkColor4f color = SkColor4f::FromColor(SkColorSetARGB(255, 65, 43, 21)); - canvas()->clear(color.toSkColor()); - canvas()->clear(color.toSkColor()); + canvas()->clear(color); + canvas()->clear(color); EXPECT_FALSE(IsSolidColor()); } diff --git a/chromium/cc/paint/target_color_params.cc b/chromium/cc/paint/target_color_params.cc index 0bdad5f4170..fcfdd27c48e 100644 --- a/chromium/cc/paint/target_color_params.cc +++ b/chromium/cc/paint/target_color_params.cc @@ -15,16 +15,19 @@ size_t TargetColorParams::GetHash() const { reinterpret_cast<const uint32_t*>(&hdr_max_luminance_relative); const uint32_t* sdr_max_luminance_nits_int = reinterpret_cast<const uint32_t*>(&sdr_max_luminance_nits); - return base::HashInts(color_space.GetHash(), - base::HashInts(*hdr_max_luminance_relative_int, - *sdr_max_luminance_nits_int)); + size_t hash = color_space.GetHash(); + hash = base::HashInts(hash, *hdr_max_luminance_relative_int); + hash = base::HashInts(hash, *sdr_max_luminance_nits_int); + hash = base::HashInts(hash, enable_tone_mapping); + return hash; } std::string TargetColorParams::ToString() const { std::ostringstream str; str << "color_space: " << color_space.ToString() << "sdr_max_luminance_nits: " << sdr_max_luminance_nits - << "hdr_max_luminance_relative: " << hdr_max_luminance_relative; + << "hdr_max_luminance_relative: " << hdr_max_luminance_relative + << "enable_tone_mapping: " << enable_tone_mapping; return str.str(); } diff --git a/chromium/cc/paint/target_color_params.h b/chromium/cc/paint/target_color_params.h index f4eb00b83c1..f7189d51a59 100644 --- a/chromium/cc/paint/target_color_params.h +++ b/chromium/cc/paint/target_color_params.h @@ -33,6 +33,9 @@ struct CC_PAINT_EXPORT TargetColorParams { // luminance (a non-HDR-capable display will have a value of 1). float hdr_max_luminance_relative = 1.f; + // Whether or not tone mapping should be applied. + bool enable_tone_mapping = true; + bool operator==(const TargetColorParams& other) const { return color_space == other.color_space && sdr_max_luminance_nits == other.sdr_max_luminance_nits && diff --git a/chromium/cc/raster/gpu_raster_buffer_provider.cc b/chromium/cc/raster/gpu_raster_buffer_provider.cc index bf0d9d7b2d4..f77c3ae2492 100644 --- a/chromium/cc/raster/gpu_raster_buffer_provider.cc +++ b/chromium/cc/raster/gpu_raster_buffer_provider.cc @@ -150,8 +150,7 @@ GpuRasterBufferProvider::GpuRasterBufferProvider( tile_format_(tile_format), max_tile_size_(max_tile_size), pending_raster_queries_(pending_raster_queries), - random_generator_(static_cast<uint32_t>(base::RandUint64())), - bernoulli_distribution_(raster_metric_probability), + raster_metric_probability_(raster_metric_probability), is_using_raw_draw_(features::IsUsingRawDraw()) { DCHECK(pending_raster_queries); DCHECK(compositor_context_provider); @@ -288,8 +287,8 @@ void GpuRasterBufferProvider::RasterBufferImpl::PlaybackOnWorkerThreadInternal( client_->worker_context_provider_->RasterInterface(); DCHECK(ri); - const bool measure_raster_metric = - client_->bernoulli_distribution_(client_->random_generator_); + const bool measure_raster_metric = client_->metrics_subsampler_.ShouldSample( + client_->raster_metric_probability_); gfx::Rect playback_rect = raster_full_rect; if (resource_has_previous_content_) { @@ -412,8 +411,8 @@ void GpuRasterBufferProvider::RasterBufferImpl::RasterizeSource( "Gpu.Rasterization.Raster.NumPaintOps", raster_source->GetDisplayItemList()->num_paint_ops()); UMA_HISTOGRAM_COUNTS_100( - "Gpu.Rasterization.Raster.NumSlowPaths", - raster_source->GetDisplayItemList()->num_slow_paths()); + "Gpu.Rasterization.Raster.NumSlowPathsUpToMinForMSAA", + raster_source->GetDisplayItemList()->num_slow_paths_up_to_min_for_MSAA()); ri->EndRasterCHROMIUM(); // TODO(ericrk): Handle unpremultiply+dither for 4444 cases. diff --git a/chromium/cc/raster/gpu_raster_buffer_provider.h b/chromium/cc/raster/gpu_raster_buffer_provider.h index f8ca6f9f656..30eb7a0e06f 100644 --- a/chromium/cc/raster/gpu_raster_buffer_provider.h +++ b/chromium/cc/raster/gpu_raster_buffer_provider.h @@ -7,10 +7,10 @@ #include <stdint.h> #include <memory> -#include <random> #include <vector> #include "base/memory/raw_ptr.h" +#include "base/rand_util.h" #include "base/time/time.h" #include "cc/raster/raster_buffer_provider.h" #include "cc/raster/raster_query_queue.h" @@ -145,9 +145,9 @@ class CC_EXPORT GpuRasterBufferProvider : public RasterBufferProvider { const raw_ptr<RasterQueryQueue> pending_raster_queries_; + const double raster_metric_probability_; // Accessed with the worker context lock acquired. - std::mt19937 random_generator_; - std::bernoulli_distribution bernoulli_distribution_; + base::MetricsSubSampler metrics_subsampler_; const bool is_using_raw_draw_; }; diff --git a/chromium/cc/raster/raster_source.cc b/chromium/cc/raster/raster_source.cc index 42f1a147ec5..dfe4361c5a5 100644 --- a/chromium/cc/raster/raster_source.cc +++ b/chromium/cc/raster/raster_source.cc @@ -181,9 +181,9 @@ bool RasterSource::IsSolidColor() const { return is_solid_color_; } -SkColor RasterSource::GetSolidColor() const { +SkColor4f RasterSource::GetSolidColor() const { DCHECK(IsSolidColor()); - return solid_color_.toSkColor(); + return solid_color_; } bool RasterSource::HasRecordings() const { diff --git a/chromium/cc/raster/raster_source.h b/chromium/cc/raster/raster_source.h index 9e0fc1b719e..a7cc8567ed0 100644 --- a/chromium/cc/raster/raster_source.h +++ b/chromium/cc/raster/raster_source.h @@ -88,7 +88,7 @@ class CC_EXPORT RasterSource : public base::RefCountedThreadSafe<RasterSource> { // Returns the color of the raster source if it is solid color. The results // are unspecified if IsSolidColor returns false. - SkColor GetSolidColor() const; + SkColor4f GetSolidColor() const; // Returns the recorded layer size of this raster source. gfx::Size GetSize() const; diff --git a/chromium/cc/raster/tile_task.cc b/chromium/cc/raster/tile_task.cc index f0c1574663a..d4bc5c153b4 100644 --- a/chromium/cc/raster/tile_task.cc +++ b/chromium/cc/raster/tile_task.cc @@ -31,4 +31,14 @@ bool TileTask::HasCompleted() const { return did_complete_; } +bool TileTask::TaskContainsLCPCandidateImages() const { + for (auto dependent : dependencies_) { + if (!dependent->HasCompleted() && + dependent->TaskContainsLCPCandidateImages()) { + return true; + } + } + return false; +} + } // namespace cc diff --git a/chromium/cc/raster/tile_task.h b/chromium/cc/raster/tile_task.h index d835dbe26f2..7544e234f59 100644 --- a/chromium/cc/raster/tile_task.h +++ b/chromium/cc/raster/tile_task.h @@ -35,6 +35,10 @@ class CC_EXPORT TileTask : public Task { // of the task. virtual void OnTaskCompleted() = 0; + // This method should report true if the task contains image decodes that + // might be for LCP candidates. + virtual bool TaskContainsLCPCandidateImages() const; + void DidComplete(); bool HasCompleted() const; diff --git a/chromium/cc/resources/scoped_ui_resource.h b/chromium/cc/resources/scoped_ui_resource.h index 716741c898a..e0a11045e58 100644 --- a/chromium/cc/resources/scoped_ui_resource.h +++ b/chromium/cc/resources/scoped_ui_resource.h @@ -40,6 +40,8 @@ class CC_EXPORT ScopedUIResource : public UIResourceClient { // Returns the memory usage of the bitmap. size_t EstimateMemoryUsage() const { return bitmap_.SizeInBytes(); } + bool IsUniquelyOwned() const { return bitmap_.IsUniquelyOwned(); } + protected: ScopedUIResource(UIResourceManager* ui_resource_manager, const UIResourceBitmap& bitmap); diff --git a/chromium/cc/resources/ui_resource_bitmap.cc b/chromium/cc/resources/ui_resource_bitmap.cc index ca32943d825..b917278724c 100644 --- a/chromium/cc/resources/ui_resource_bitmap.cc +++ b/chromium/cc/resources/ui_resource_bitmap.cc @@ -79,7 +79,11 @@ UIResourceBitmap::UIResourceBitmap(const SkBitmap& skbitmap) { SkBitmap copy; if (features::IsDrDcEnabled()) { // TODO(vikassoni): Forcing everything to N32 while android backing cannot - // support some other formats. + // support some other formats. Note that DrDc is disabled on some gl + // renderers and hence gpus via gpu driver bug workaround. That workaround + // is not applied here and so on those disable gpus, everything will still + // be forced to N32 even though drdc is disabled. This should be fine for + // now and would be fixed later. crbug.com/1354201. if (skbitmap.colorType() != kN32_SkColorType) { SkImageInfo new_info = skbitmap.info().makeColorType(kN32_SkColorType); copy.allocPixels(new_info, new_info.minRowBytes()); diff --git a/chromium/cc/resources/ui_resource_bitmap.h b/chromium/cc/resources/ui_resource_bitmap.h index a919811f9bc..73082cb9faa 100644 --- a/chromium/cc/resources/ui_resource_bitmap.h +++ b/chromium/cc/resources/ui_resource_bitmap.h @@ -52,6 +52,8 @@ class CC_EXPORT UIResourceBitmap { size_t SizeInBytes() const; size_t row_bytes() const { return pixel_ref_ ? pixel_ref_->rowBytes() : 0; } + bool IsUniquelyOwned() const { return pixel_ref_->unique(); } + private: friend class AutoLockUIResourceBitmap; diff --git a/chromium/cc/resources/ui_resource_manager.cc b/chromium/cc/resources/ui_resource_manager.cc index 83cfdccbade..e0d184eda7d 100644 --- a/chromium/cc/resources/ui_resource_manager.cc +++ b/chromium/cc/resources/ui_resource_manager.cc @@ -7,6 +7,8 @@ #include <algorithm> #include <utility> +#include "base/check.h" +#include "base/containers/cxx20_erase.h" #include "cc/resources/scoped_ui_resource.h" namespace cc { @@ -65,10 +67,11 @@ void UIResourceManager::RecreateUIResources() { base::flat_map<UIResourceId, gfx::Size> UIResourceManager::GetUIResourceSizes() const { - base::flat_map<UIResourceId, gfx::Size> result; + base::flat_map<UIResourceId, gfx::Size>::container_type items( + ui_resource_client_map_.size()); for (const auto pair : ui_resource_client_map_) - result.emplace(pair.first, pair.second.size); - return result; + items.push_back({pair.first, pair.second.size}); + return base::flat_map<UIResourceId, gfx::Size>(std::move(items)); } std::vector<UIResourceRequest> UIResourceManager::TakeUIResourcesRequests() { @@ -83,6 +86,18 @@ UIResourceId UIResourceManager::GetOrCreateUIResource(const SkBitmap& bitmap) { if (resource != owned_shared_resources_.end()) return resource->second->id(); + // Evict all UIResources whose bitmaps are no longer referenced outside of the + // map. + base::EraseIf(owned_shared_resources_, + [](auto& pair) { return pair.second->IsUniquelyOwned(); }); + + // Max capacity of `owned_shared_resources_`. A DCHECK() would fire if cache + // size after eviction does not fall below the limit. 256 is an arbitrarily + // chosen number that is greater than the max number of images we expect to + // ever use concurrently. + constexpr size_t kMaxSkBitmapResources = 256u; + DCHECK_LT(owned_shared_resources_.size(), kMaxSkBitmapResources); + auto scoped_resource = ScopedUIResource::Create(this, UIResourceBitmap(bitmap)); auto id = scoped_resource->id(); diff --git a/chromium/cc/resources/ui_resource_manager.h b/chromium/cc/resources/ui_resource_manager.h index cf18142a115..1806a8f6c40 100644 --- a/chromium/cc/resources/ui_resource_manager.h +++ b/chromium/cc/resources/ui_resource_manager.h @@ -16,6 +16,15 @@ namespace cc { class ScopedUIResource; +// UIResourceManager creates and manages UIResourceBitmaps and UIResourceIDs +// for given bitmaps for a LayerTreeHost. There are two ways to use the +// interface: +// 1. `CreateUIResource`/`DeleteUIResource` to explicitly manage the lifetime +// of UIResources. +// 2. `GetOrCreateUIResource` to create shared UIResources backed by the same +// SkBitmap (SkPixelRef to be exact). The created UIResources are owned by +// the manager and are released when all references outside the manager's +// map are dropped. class CC_EXPORT UIResourceManager { public: UIResourceManager(); @@ -46,10 +55,16 @@ class CC_EXPORT UIResourceManager { // were evicted on the impl thread. void RecreateUIResources(); - // Creates a resource given an SkBitmap. Multiple calls with bitmaps that - // share the same SkPixelRef will share a single resource ID. + // Creates a resource given a SkBitmap. Multiple calls with bitmaps that + // share the same SkPixelRef will share a single resource ID. The returned + // `UIResourceId` will only be valid as long as something else holds a + // reference to the SkBitmap UIResourceId GetOrCreateUIResource(const SkBitmap& bitmap); + size_t owned_shared_resources_size_for_test() const { + return owned_shared_resources_.size(); + } + private: struct UIResourceClientData { UIResourceClient* client; @@ -64,9 +79,8 @@ class CC_EXPORT UIResourceManager { UIResourceRequestQueue ui_resource_request_queue_; // A map from bitmaps to the ScopedUIResource we've created for them. The - // resources are never released over the duration of the lifetime of |this|. - // If you want to release a resource added here, add a function (or extend - // DeleteUIResource). + // resources are released when all references of SkPixelRefs outside the map + // are dropped. std::unordered_map<SkPixelRef*, std::unique_ptr<ScopedUIResource>> owned_shared_resources_; }; diff --git a/chromium/cc/scheduler/commit_earlyout_reason.h b/chromium/cc/scheduler/commit_earlyout_reason.h index 7fe59e45962..050da072348 100644 --- a/chromium/cc/scheduler/commit_earlyout_reason.h +++ b/chromium/cc/scheduler/commit_earlyout_reason.h @@ -32,7 +32,7 @@ inline const char* CommitEarlyOutReasonToString(CommitEarlyOutReason reason) { return "???"; } -inline bool CommitEarlyOutHandledCommit(CommitEarlyOutReason reason) { +inline bool MainFrameAppliedDeltas(CommitEarlyOutReason reason) { return reason == CommitEarlyOutReason::FINISHED_NO_UPDATES; } diff --git a/chromium/cc/scheduler/scheduler.h b/chromium/cc/scheduler/scheduler.h index bf83e8447a7..1ace7b5b90e 100644 --- a/chromium/cc/scheduler/scheduler.h +++ b/chromium/cc/scheduler/scheduler.h @@ -296,7 +296,7 @@ class CC_EXPORT Scheduler : public viz::BeginFrameObserverBase { // Owned by LayerTreeHostImpl and is destroyed when LayerTreeHostImpl is // destroyed. - raw_ptr<CompositorFrameReportingController> + raw_ptr<CompositorFrameReportingController, DanglingUntriaged> compositor_frame_reporting_controller_; // What the latest deadline was, and when it was scheduled. diff --git a/chromium/cc/tiles/gpu_image_decode_cache.cc b/chromium/cc/tiles/gpu_image_decode_cache.cc index f9c24c2c0c5..4b5068986f3 100644 --- a/chromium/cc/tiles/gpu_image_decode_cache.cc +++ b/chromium/cc/tiles/gpu_image_decode_cache.cc @@ -27,6 +27,7 @@ #include "base/threading/thread_task_runner_handle.h" #include "base/trace_event/memory_dump_manager.h" #include "cc/base/devtools_instrumentation.h" +#include "cc/base/features.h" #include "cc/base/histograms.h" #include "cc/base/switches.h" #include "cc/paint/paint_flags.h" @@ -551,7 +552,10 @@ class GpuImageDecodeTaskImpl : public TileTask { const ImageDecodeCache::TracingInfo& tracing_info, GpuImageDecodeCache::DecodeTaskType task_type) : TileTask(TileTask::SupportsConcurrentExecution::kYes, - TileTask::SupportsBackgroundThreadPriority::kYes), + (base::FeatureList::IsEnabled( + features::kNormalPriorityImageDecoding) + ? TileTask::SupportsBackgroundThreadPriority::kNo + : TileTask::SupportsBackgroundThreadPriority::kYes)), cache_(cache), image_(draw_image), tracing_info_(tracing_info), @@ -584,6 +588,13 @@ class GpuImageDecodeTaskImpl : public TileTask { cache_->OnImageDecodeTaskCompleted(image_, task_type_); } + // Overridden from TileTask: + bool TaskContainsLCPCandidateImages() const override { + if (!HasCompleted() && image_.paint_image().may_be_lcp_candidate()) + return true; + return TileTask::TaskContainsLCPCandidateImages(); + } + protected: ~GpuImageDecodeTaskImpl() override = default; @@ -977,13 +988,12 @@ GpuImageDecodeCache::GpuImageDecodeCache( SkColorType color_type, size_t max_working_set_bytes, int max_texture_size, - PaintImage::GeneratorClientId generator_client_id, RasterDarkModeFilter* const dark_mode_filter) : color_type_(color_type), use_transfer_cache_(use_transfer_cache), context_(context), max_texture_size_(max_texture_size), - generator_client_id_(generator_client_id), + generator_client_id_(PaintImage::GetNextGeneratorClientId()), enable_clipped_image_scaling_( base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kEnableClippedImageScaling)), @@ -1582,6 +1592,8 @@ void GpuImageDecodeCache::OnImageDecodeTaskCompleted( // Decode task is complete, remove our reference to it. ImageData* image_data = GetImageDataForDrawImage(draw_image, cache_key); DCHECK(image_data); + UMA_HISTOGRAM_BOOLEAN("Compositing.DecodeLCPCandidateImage.Hardware", + draw_image.paint_image().may_be_lcp_candidate()); if (task_type == DecodeTaskType::kPartOfUploadTask) { DCHECK(image_data->decode.task); image_data->decode.task = nullptr; @@ -3015,16 +3027,12 @@ bool GpuImageDecodeCache::NeedsDarkModeFilterForTesting( void GpuImageDecodeCache::OnMemoryPressure( base::MemoryPressureListener::MemoryPressureLevel level) { + if (!ImageDecodeCacheUtils::ShouldEvictCaches(level)) + return; + base::AutoLock lock(lock_); - switch (level) { - case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE: - case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE: - break; - case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL: - base::AutoReset<bool> reset(&aggressively_freeing_resources_, true); - EnsureCapacity(0); - break; - } + base::AutoReset<bool> reset(&aggressively_freeing_resources_, true); + EnsureCapacity(0); } bool GpuImageDecodeCache::SupportsColorSpaceConversion() const { diff --git a/chromium/cc/tiles/gpu_image_decode_cache.h b/chromium/cc/tiles/gpu_image_decode_cache.h index c159315df72..c1625780abc 100644 --- a/chromium/cc/tiles/gpu_image_decode_cache.h +++ b/chromium/cc/tiles/gpu_image_decode_cache.h @@ -145,7 +145,6 @@ class CC_EXPORT GpuImageDecodeCache SkColorType color_type, size_t max_working_set_bytes, int max_texture_size, - PaintImage::GeneratorClientId client_id, RasterDarkModeFilter* const dark_mode_filter); ~GpuImageDecodeCache() override; diff --git a/chromium/cc/tiles/gpu_image_decode_cache_perftest.cc b/chromium/cc/tiles/gpu_image_decode_cache_perftest.cc index 0e8bba94795..ac48ff8cc7f 100644 --- a/chromium/cc/tiles/gpu_image_decode_cache_perftest.cc +++ b/chromium/cc/tiles/gpu_image_decode_cache_perftest.cc @@ -55,8 +55,7 @@ class GpuImageDecodeCachePerfTest ASSERT_EQ(result, gpu::ContextResult::kSuccess); cache_ = std::make_unique<GpuImageDecodeCache>( context_provider_.get(), UseTransferCache(), kRGBA_8888_SkColorType, - kCacheSize, MaxTextureSize(), PaintImage::GetNextGeneratorClientId(), - nullptr); + kCacheSize, MaxTextureSize(), nullptr); } protected: diff --git a/chromium/cc/tiles/gpu_image_decode_cache_unittest.cc b/chromium/cc/tiles/gpu_image_decode_cache_unittest.cc index b72b54d5569..4a97f9713e7 100644 --- a/chromium/cc/tiles/gpu_image_decode_cache_unittest.cc +++ b/chromium/cc/tiles/gpu_image_decode_cache_unittest.cc @@ -428,8 +428,7 @@ class GpuImageDecodeCacheTest RasterDarkModeFilter* const dark_mode_filter = nullptr) { return std::make_unique<GpuImageDecodeCache>( context_provider_.get(), use_transfer_cache_, color_type_, - memory_limit_bytes, max_texture_size_, - PaintImage::GetNextGeneratorClientId(), dark_mode_filter); + memory_limit_bytes, max_texture_size_, dark_mode_filter); } // Returns dimensions for an image that will not fit in GPU memory and hence diff --git a/chromium/cc/tiles/image_controller.cc b/chromium/cc/tiles/image_controller.cc index 9fba377bf2c..1dec3e72d4e 100644 --- a/chromium/cc/tiles/image_controller.cc +++ b/chromium/cc/tiles/image_controller.cc @@ -4,66 +4,81 @@ #include "cc/tiles/image_controller.h" +#include <utility> + #include "base/bind.h" +#include "base/feature_list.h" #include "base/task/task_traits.h" -#include "base/threading/thread_restrictions.h" #include "base/trace_event/trace_event.h" #include "cc/base/completion_event.h" #include "cc/tiles/tile_task_manager.h" namespace cc { +namespace { + +// When this feature is enabled, StopWorkerTasks() skips waiting synchronously +// if no task is running. +const base::Feature kImageControllerWaitOnlyForRunningTask{ + "ImageControllerWaitOnlyForRunningTask", base::FEATURE_DISABLED_BY_DEFAULT}; + +} // namespace + ImageController::ImageDecodeRequestId ImageController::s_next_image_decode_queue_id_ = 1; ImageController::ImageController( - base::SequencedTaskRunner* origin_task_runner, + scoped_refptr<base::SequencedTaskRunner> origin_task_runner, scoped_refptr<base::SequencedTaskRunner> worker_task_runner) - : worker_task_runner_(std::move(worker_task_runner)), - origin_task_runner_(origin_task_runner) { - weak_ptr_ = weak_ptr_factory_.GetWeakPtr(); + : worker_task_runner_(std::move(worker_task_runner)) { + worker_state_ = std::make_unique<WorkerState>(std::move(origin_task_runner), + weak_ptr_factory_.GetWeakPtr()); } ImageController::~ImageController() { StopWorkerTasks(); for (auto& request : orphaned_decode_requests_) std::move(request.callback).Run(request.id, ImageDecodeResult::FAILURE); + if (worker_task_runner_) { + // Delete `worker_state_` on `worker_task_runner_` (or elsewhere via the + // callback's destructor if `worker_task_runner_` stopped accepting tasks). + worker_task_runner_->PostTask( + FROM_HERE, base::BindOnce([](std::unique_ptr<WorkerState>) {}, + std::move(worker_state_))); + } } +ImageController::WorkerState::WorkerState( + scoped_refptr<base::SequencedTaskRunner> origin_task_runner, + base::WeakPtr<ImageController> weak_ptr) + : origin_task_runner(std::move(origin_task_runner)), weak_ptr(weak_ptr) {} +ImageController::WorkerState::~WorkerState() = default; + void ImageController::StopWorkerTasks() { // We can't have worker threads without a cache_ or a worker_task_runner_, so // terminate early. if (!cache_ || !worker_task_runner_) return; - // Abort all tasks that are currently scheduled to run (we'll wait for them to - // finish next). - { - base::AutoLock hold(lock_); - abort_tasks_ = true; + base::AutoLock hold(worker_state_->lock); + worker_state_->abort_task = true; + + // If a worker task is running (or if the "wait only for running task" feature + // is disabled), post a task and wait for its completion to "flush" the queue. + if (!base::FeatureList::IsEnabled(kImageControllerWaitOnlyForRunningTask) || + worker_state_->task_state == WorkerTaskState::kRunningTask) { + base::AutoUnlock release(worker_state_->lock); + CompletionEvent completion_event; + worker_task_runner_->PostTask( + FROM_HERE, base::BindOnce(&CompletionEvent::Signal, + base::Unretained(&completion_event))); + completion_event.Wait(); } - // Post a task that will simply signal a completion event to ensure that we - // "flush" any scheduled tasks (they will abort). - CompletionEvent completion_event; - worker_task_runner_->PostTask( - FROM_HERE, base::BindOnce([](CompletionEvent* event) { event->Signal(); }, - base::Unretained(&completion_event))); - completion_event.Wait(); + DCHECK_EQ(worker_state_->task_state, WorkerTaskState::kNoTask); // Reset the abort flag so that new tasks can be scheduled. - { - base::AutoLock hold(lock_); - abort_tasks_ = false; - } - - // Now that we flushed everything, if there was a task running and it - // finished, it would have posted a completion callback back to the compositor - // thread. We don't want that, so invalidate the weak ptrs again. Note that - // nothing can start running between wait and this invalidate, since it would - // only run on the current (compositor) thread. - weak_ptr_factory_.InvalidateWeakPtrs(); - weak_ptr_ = weak_ptr_factory_.GetWeakPtr(); + worker_state_->abort_task = false; // Now, begin cleanup. @@ -76,7 +91,7 @@ void ImageController::StopWorkerTasks() { // Now, complete the tasks that already ran but haven't completed. These would // be posted in the run loop, but since we invalidated the weak ptrs, we need // to run everything manually. - for (auto& request_to_complete : requests_needing_completion_) { + for (auto& request_to_complete : worker_state_->requests_needing_completion) { ImageDecodeRequest& request = request_to_complete.second; // The task (if one exists) would have run already, we just need to make @@ -95,12 +110,12 @@ void ImageController::StopWorkerTasks() { request.need_unref = false; orphaned_decode_requests_.push_back(std::move(request)); } - requests_needing_completion_.clear(); + worker_state_->requests_needing_completion.clear(); // Finally, complete all of the tasks that never started running. This is // similar to the |requests_needing_completion_|, but happens at a different // stage in the pipeline. - for (auto& request_pair : image_decode_queue_) { + for (auto& request_pair : worker_state_->image_decode_queue) { ImageDecodeRequest& request = request_pair.second; if (request.task) { @@ -124,7 +139,7 @@ void ImageController::StopWorkerTasks() { request.need_unref = false; orphaned_decode_requests_.push_back(std::move(request)); } - image_decode_queue_.clear(); + worker_state_->image_decode_queue.clear(); } void ImageController::SetImageDecodeCache(ImageDecodeCache* cache) { @@ -238,20 +253,11 @@ ImageController::ImageDecodeRequestId ImageController::QueueImageDecode( DCHECK(result.need_unref || !result.task); // Schedule the task and signal that there is more work. - base::AutoLock hold(lock_); - image_decode_queue_[id] = + base::AutoLock hold(worker_state_->lock); + worker_state_->image_decode_queue[id] = ImageDecodeRequest(id, draw_image, std::move(callback), std::move(result.task), result.need_unref); - - // If this is the only image decode request, schedule a task to run. - // Otherwise, the task will be scheduled in the previou task's completion. - if (image_decode_queue_.size() == 1) { - // Post a worker task. - worker_task_runner_->PostTask( - FROM_HERE, - base::BindOnce(&ImageController::ProcessNextImageDecodeOnWorkerThread, - base::Unretained(this))); - } + ScheduleImageDecodeOnWorkerIfNeeded(); return id; } @@ -266,60 +272,71 @@ void ImageController::UnlockImageDecode(ImageDecodeRequestId id) { requested_locked_images_.erase(it); } -void ImageController::ProcessNextImageDecodeOnWorkerThread() { +// static +void ImageController::ProcessNextImageDecodeOnWorkerThread( + WorkerState* worker_state) { TRACE_EVENT0("cc", "ImageController::ProcessNextImageDecodeOnWorkerThread"); - scoped_refptr<TileTask> decode_task; - ImageDecodeRequestId decode_id; - { - base::AutoLock hold(lock_); - // If we don't have any work, abort. - if (image_decode_queue_.empty() || abort_tasks_) - return; + base::AutoLock hold(worker_state->lock); + DCHECK_EQ(worker_state->task_state, WorkerTaskState::kQueuedTask); + worker_state->task_state = WorkerTaskState::kRunningTask; - // Take the next request from the queue. - auto decode_it = image_decode_queue_.begin(); - DCHECK(decode_it != image_decode_queue_.end()); - decode_task = decode_it->second.task; - decode_id = decode_it->second.id; - - // Notify that the task will need completion. Note that there are two cases - // where we process this. First, we might complete this task as a response - // to the posted task below. Second, we might complete it in - // StopWorkerTasks(). In either case, the task would have already run - // (either post task happens after running, or the thread was already joined - // which means the task ran). This means that we can put the decode into - // |requests_needing_completion_| here before actually running the task. - requests_needing_completion_[decode_id] = std::move(decode_it->second); - - image_decode_queue_.erase(decode_it); + // If we don't have any work, abort. + if (worker_state->image_decode_queue.empty() || worker_state->abort_task) { + worker_state->task_state = WorkerTaskState::kNoTask; + return; } - // Run the task if we need to run it. If the task state isn't new, then - // there is another task that is responsible for finishing it and cleaning - // up (and it already ran); we just need to post a completion callback. - // Note that the other tasks's completion will also run first, since the - // requests are ordered. So, when we process this task's completion, we - // won't actually do anything with the task and simply issue the callback. + // Take the next request from the queue. + auto decode_it = worker_state->image_decode_queue.begin(); + DCHECK(decode_it != worker_state->image_decode_queue.end()); + scoped_refptr<TileTask> decode_task = decode_it->second.task; + ImageDecodeRequestId decode_id = decode_it->second.id; + + // Notify that the task will need completion. Note that there are two cases + // where we process this. First, we might complete this task as a response to + // the posted task below. Second, we might complete it in StopWorkerTasks(). + // In either case, the task would have already run (either post task happens + // after running, or the thread was already joined which means the task ran). + // This means that we can put the decode into |requests_needing_completion_| + // here before actually running the task. + worker_state->requests_needing_completion[decode_id] = + std::move(decode_it->second); + + worker_state->image_decode_queue.erase(decode_it); + + // Run the task if we need to run it. If the task state isn't new, then there + // is another task that is responsible for finishing it and cleaning up (and + // it already ran); we just need to post a completion callback. Note that the + // other tasks's completion will also run first, since the requests are + // ordered. So, when we process this task's completion, we won't actually do + // anything with the task and simply issue the callback. if (decode_task && decode_task->state().IsNew()) { + base::AutoUnlock release(worker_state->lock); decode_task->state().DidSchedule(); decode_task->state().DidStart(); decode_task->RunOnWorkerThread(); decode_task->state().DidFinish(); } - origin_task_runner_->PostTask( + + worker_state->origin_task_runner->PostTask( FROM_HERE, base::BindOnce(&ImageController::ImageDecodeCompleted, - weak_ptr_, decode_id)); + worker_state->weak_ptr, decode_id)); + + DCHECK_EQ(worker_state->task_state, WorkerTaskState::kRunningTask); + worker_state->task_state = WorkerTaskState::kNoTask; } void ImageController::ImageDecodeCompleted(ImageDecodeRequestId id) { ImageDecodedCallback callback; ImageDecodeResult result = ImageDecodeResult::SUCCESS; { - base::AutoLock hold(lock_); + base::AutoLock hold(worker_state_->lock); - auto request_it = requests_needing_completion_.find(id); - DCHECK(request_it != requests_needing_completion_.end()); + auto request_it = worker_state_->requests_needing_completion.find(id); + // The request may have been completed by StopWorkerTasks(). + if (request_it == worker_state_->requests_needing_completion.end()) + return; id = request_it->first; ImageDecodeRequest& request = request_it->second; @@ -350,23 +367,19 @@ void ImageController::ImageDecodeCompleted(ImageDecodeRequestId id) { // Finally, save the callback so we can run it without the lock, and erase // the request from |requests_needing_completion_|. callback = std::move(request.callback); - requests_needing_completion_.erase(request_it); - } + worker_state_->requests_needing_completion.erase(request_it); - // Post another task to run. - worker_task_runner_->PostTask( - FROM_HERE, - base::BindOnce(&ImageController::ProcessNextImageDecodeOnWorkerThread, - base::Unretained(this))); + ScheduleImageDecodeOnWorkerIfNeeded(); + } // Finally run the requested callback. std::move(callback).Run(id, result); } void ImageController::GenerateTasksForOrphanedRequests() { - base::AutoLock hold(lock_); - DCHECK_EQ(0u, image_decode_queue_.size()); - DCHECK_EQ(0u, requests_needing_completion_.size()); + base::AutoLock hold(worker_state_->lock); + DCHECK_EQ(0u, worker_state_->image_decode_queue.size()); + DCHECK_EQ(0u, worker_state_->requests_needing_completion.size()); DCHECK(cache_); for (auto& request : orphaned_decode_requests_) { @@ -379,16 +392,24 @@ void ImageController::GenerateTasksForOrphanedRequests() { request.need_unref = result.need_unref; request.task = result.task; } - image_decode_queue_[request.id] = std::move(request); + worker_state_->image_decode_queue[request.id] = std::move(request); } orphaned_decode_requests_.clear(); - if (!image_decode_queue_.empty()) { - // Post a worker task. + ScheduleImageDecodeOnWorkerIfNeeded(); +} + +void ImageController::ScheduleImageDecodeOnWorkerIfNeeded() { + if (worker_state_->task_state == WorkerTaskState::kNoTask && + !worker_state_->image_decode_queue.empty()) { + worker_state_->task_state = WorkerTaskState::kQueuedTask; + // base::Unretained is safe because `worker_state_` is guaranteed to be + // deleted from a task posted to `worker_task_runner_` after this (see + // ~ImageController). worker_task_runner_->PostTask( FROM_HERE, base::BindOnce(&ImageController::ProcessNextImageDecodeOnWorkerThread, - base::Unretained(this))); + base::Unretained(worker_state_.get()))); } } diff --git a/chromium/cc/tiles/image_controller.h b/chromium/cc/tiles/image_controller.h index 20c4a0dc249..a93aca54925 100644 --- a/chromium/cc/tiles/image_controller.h +++ b/chromium/cc/tiles/image_controller.h @@ -5,7 +5,8 @@ #ifndef CC_TILES_IMAGE_CONTROLLER_H_ #define CC_TILES_IMAGE_CONTROLLER_H_ -#include <set> +#include <map> +#include <memory> #include <vector> #include "base/callback.h" @@ -13,8 +14,9 @@ #include "base/memory/raw_ptr.h" #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" +#include "base/synchronization/lock.h" #include "base/task/sequenced_task_runner.h" -#include "base/threading/simple_thread.h" +#include "base/thread_annotations.h" #include "cc/base/unique_notifier.h" #include "cc/cc_export.h" #include "cc/paint/draw_image.h" @@ -31,7 +33,7 @@ class CC_EXPORT ImageController { using ImageDecodedCallback = base::OnceCallback<void(ImageDecodeRequestId, ImageDecodeResult)>; explicit ImageController( - base::SequencedTaskRunner* origin_task_runner, + scoped_refptr<base::SequencedTaskRunner> origin_task_runner, scoped_refptr<base::SequencedTaskRunner> worker_task_runner); ImageController(const ImageController&) = delete; virtual ~ImageController(); @@ -105,32 +107,51 @@ class CC_EXPORT ImageController { bool need_unref; }; + enum class WorkerTaskState { + kNoTask, + kQueuedTask, + kRunningTask, + }; + + // State accessible from the worker thread. Held in a isolated struct so it + // can be deleted asynchronously on the worker thread after the + // ImageController is deleted. + struct WorkerState { + WorkerState(scoped_refptr<base::SequencedTaskRunner> origin_task_runner, + base::WeakPtr<ImageController> weak_ptr); + ~WorkerState(); + + base::Lock lock; + std::map<ImageDecodeRequestId, ImageDecodeRequest> image_decode_queue + GUARDED_BY(lock); + std::map<ImageDecodeRequestId, ImageDecodeRequest> + requests_needing_completion GUARDED_BY(lock); + WorkerTaskState task_state GUARDED_BY(lock) = WorkerTaskState::kNoTask; + bool abort_task GUARDED_BY(lock) = false; + + const scoped_refptr<base::SequencedTaskRunner> origin_task_runner; + const base::WeakPtr<ImageController> weak_ptr; + }; + void StopWorkerTasks(); - // Called from the worker thread. - void ProcessNextImageDecodeOnWorkerThread(); + static void ProcessNextImageDecodeOnWorkerThread(WorkerState* worker_state); void ImageDecodeCompleted(ImageDecodeRequestId id); void GenerateTasksForOrphanedRequests(); - base::WeakPtr<ImageController> weak_ptr_; + void ScheduleImageDecodeOnWorkerIfNeeded() + EXCLUSIVE_LOCKS_REQUIRED(worker_state_->lock); raw_ptr<ImageDecodeCache> cache_ = nullptr; std::vector<DrawImage> predecode_locked_images_; static ImageDecodeRequestId s_next_image_decode_queue_id_; base::flat_map<ImageDecodeRequestId, DrawImage> requested_locked_images_; - - raw_ptr<base::SequencedTaskRunner> origin_task_runner_ = nullptr; size_t image_cache_max_limit_bytes_ = 0u; - // The variables defined below this lock (aside from weak_ptr_factory_) can - // only be accessed when the lock is acquired. - base::Lock lock_; - std::map<ImageDecodeRequestId, ImageDecodeRequest> image_decode_queue_; - std::map<ImageDecodeRequestId, ImageDecodeRequest> - requests_needing_completion_; - bool abort_tasks_ = false; + std::unique_ptr<WorkerState> worker_state_; + // Orphaned requests are requests that were either in queue or needed a // completion callback when we set the decode cache to be nullptr. When a new // decode cache is set, these requests are re-enqueued again with tasks diff --git a/chromium/cc/tiles/image_controller_unittest.cc b/chromium/cc/tiles/image_controller_unittest.cc index 386cb0e45f1..42bb926aad4 100644 --- a/chromium/cc/tiles/image_controller_unittest.cc +++ b/chromium/cc/tiles/image_controller_unittest.cc @@ -11,10 +11,13 @@ #include "base/callback_helpers.h" #include "base/run_loop.h" #include "base/synchronization/condition_variable.h" -#include "base/test/test_simple_task_runner.h" +#include "base/task/thread_pool.h" #include "base/threading/sequenced_task_runner_handle.h" +#include "base/threading/simple_thread.h" #include "base/threading/thread_checker_impl.h" +#include "base/threading/thread_restrictions.h" #include "cc/paint/paint_image_builder.h" +#include "cc/test/cc_test_suite.h" #include "cc/test/skia_common.h" #include "cc/test/stub_decode_cache.h" #include "cc/test/test_paint_worklet_input.h" @@ -25,78 +28,6 @@ namespace cc { namespace { -class TestWorkerThread : public base::SimpleThread { - public: - TestWorkerThread() - : base::SimpleThread("test_worker_thread"), condition_(&lock_) {} - - void Run() override { - for (;;) { - base::OnceClosure task; - { - base::AutoLock hold(lock_); - if (shutdown_) - break; - - if (queue_.empty()) { - condition_.Wait(); - continue; - } - - task = std::move(queue_.front()); - queue_.erase(queue_.begin()); - } - std::move(task).Run(); - } - } - - void Shutdown() { - base::AutoLock hold(lock_); - shutdown_ = true; - condition_.Signal(); - } - - void PostTask(base::OnceClosure task) { - base::AutoLock hold(lock_); - queue_.push_back(std::move(task)); - condition_.Signal(); - } - - private: - base::Lock lock_; - base::ConditionVariable condition_; - std::vector<base::OnceClosure> queue_; - bool shutdown_ = false; -}; - -class WorkerTaskRunner : public base::SequencedTaskRunner { - public: - WorkerTaskRunner() { thread_.Start(); } - - bool PostNonNestableDelayedTask(const base::Location& from_here, - base::OnceClosure task, - base::TimeDelta delay) override { - return PostDelayedTask(from_here, std::move(task), delay); - } - - bool PostDelayedTask(const base::Location& from_here, - base::OnceClosure task, - base::TimeDelta delay) override { - thread_.PostTask(std::move(task)); - return true; - } - - bool RunsTasksInCurrentSequence() const override { return false; } - - protected: - ~WorkerTaskRunner() override { - thread_.Shutdown(); - thread_.Join(); - } - - TestWorkerThread thread_; -}; - // Image decode cache with introspection! class TestableCache : public StubDecodeCache { public: @@ -208,6 +139,7 @@ class BlockingTask : public TileTask { EXPECT_FALSE(HasCompleted()); EXPECT_FALSE(thread_checker_.CalledOnValidThread()); base::AutoLock hold(lock_); + base::ScopedAllowBaseSyncPrimitivesForTesting allow_base_sync_primitives; while (!can_run_) run_cv_.Wait(); has_run_ = true; @@ -262,16 +194,16 @@ class ImageControllerTest : public testing::Test { ~ImageControllerTest() override = default; void SetUp() override { - worker_task_runner_ = base::MakeRefCounted<WorkerTaskRunner>(); - controller_ = std::make_unique<ImageController>(task_runner_.get(), - worker_task_runner_); + controller_ = std::make_unique<ImageController>( + task_runner_, + base::ThreadPool::CreateSequencedTaskRunner(base::TaskTraits())); cache_ = TestableCache(); controller_->SetImageDecodeCache(&cache_); } void TearDown() override { controller_.reset(); - worker_task_runner_ = nullptr; + CCTestSuite::RunUntilIdle(); weak_ptr_factory_.InvalidateWeakPtrs(); } @@ -318,7 +250,6 @@ class ImageControllerTest : public testing::Test { private: scoped_refptr<base::SequencedTaskRunner> task_runner_; - scoped_refptr<WorkerTaskRunner> worker_task_runner_; TestableCache cache_; std::unique_ptr<ImageController> controller_; DrawImage image_; @@ -474,7 +405,9 @@ TEST_F(ImageControllerTest, QueueImageDecodeMultipleImagesSameTask) { EXPECT_TRUE(task->HasCompleted()); } -TEST_F(ImageControllerTest, QueueImageDecodeChangeControllerWithTaskQueued) { +// TODO(crbug.com/1336053): Re-enable this test +TEST_F(ImageControllerTest, + DISABLED_QueueImageDecodeChangeControllerWithTaskQueued) { scoped_refptr<BlockingTask> task_one(new BlockingTask); cache()->SetTaskToUse(task_one); diff --git a/chromium/cc/tiles/image_decode_cache_utils.cc b/chromium/cc/tiles/image_decode_cache_utils.cc index b730d060a54..b6a1faef323 100644 --- a/chromium/cc/tiles/image_decode_cache_utils.cc +++ b/chromium/cc/tiles/image_decode_cache_utils.cc @@ -15,6 +15,7 @@ namespace cc { +// static bool ImageDecodeCacheUtils::ScaleToHalfFloatPixmapUsingN32Intermediate( const SkPixmap& source_pixmap, SkPixmap* scaled_pixmap, @@ -51,6 +52,20 @@ bool ImageDecodeCacheUtils::ScaleToHalfFloatPixmapUsingN32Intermediate( return n32_resized_bitmap.readPixels(*scaled_pixmap, 0, 0); } +// static +bool ImageDecodeCacheUtils::ShouldEvictCaches( + base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) { + switch (memory_pressure_level) { + case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE: + case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE: + return false; + case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL: + return true; + } + NOTREACHED(); + return false; +} + } // namespace cc #endif // CC_TILES_IMAGE_DECODE_CACHE_UTILS_CC_ diff --git a/chromium/cc/tiles/image_decode_cache_utils.h b/chromium/cc/tiles/image_decode_cache_utils.h index 6bfac749287..cbc64b2d3fb 100644 --- a/chromium/cc/tiles/image_decode_cache_utils.h +++ b/chromium/cc/tiles/image_decode_cache_utils.h @@ -5,6 +5,7 @@ #ifndef CC_TILES_IMAGE_DECODE_CACHE_UTILS_H_ #define CC_TILES_IMAGE_DECODE_CACHE_UTILS_H_ +#include "base/memory/memory_pressure_listener.h" #include "build/build_config.h" #include "cc/paint/paint_flags.h" #include "third_party/skia/include/core/SkPixmap.h" @@ -35,6 +36,9 @@ class ImageDecodeCacheUtils { const SkPixmap& source_pixmap, SkPixmap* scaled_pixmap, PaintFlags::FilterQuality filter_quality); + + static bool ShouldEvictCaches( + base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level); }; } // namespace cc diff --git a/chromium/cc/tiles/software_image_decode_cache.cc b/chromium/cc/tiles/software_image_decode_cache.cc index 28b6a196983..3cbe0a52a20 100644 --- a/chromium/cc/tiles/software_image_decode_cache.cc +++ b/chromium/cc/tiles/software_image_decode_cache.cc @@ -20,6 +20,7 @@ #include "base/threading/thread_task_runner_handle.h" #include "base/trace_event/memory_dump_manager.h" #include "cc/base/devtools_instrumentation.h" +#include "cc/base/features.h" #include "cc/base/histograms.h" #include "cc/raster/tile_task.h" #include "cc/tiles/mipmap_util.h" @@ -65,7 +66,10 @@ class SoftwareImageDecodeTaskImpl : public TileTask { SoftwareImageDecodeCache::DecodeTaskType task_type, const ImageDecodeCache::TracingInfo& tracing_info) : TileTask(TileTask::SupportsConcurrentExecution::kYes, - TileTask::SupportsBackgroundThreadPriority::kYes), + (base::FeatureList::IsEnabled( + features::kNormalPriorityImageDecoding) + ? TileTask::SupportsBackgroundThreadPriority::kNo + : TileTask::SupportsBackgroundThreadPriority::kYes)), cache_(cache), image_key_(image_key), paint_image_(paint_image), @@ -103,6 +107,13 @@ class SoftwareImageDecodeTaskImpl : public TileTask { cache_->OnImageDecodeTaskCompleted(image_key_, task_type_); } + // Overridden from TileTask: + bool TaskContainsLCPCandidateImages() const override { + if (!HasCompleted() && paint_image_.may_be_lcp_candidate()) + return true; + return TileTask::TaskContainsLCPCandidateImages(); + } + protected: ~SoftwareImageDecodeTaskImpl() override = default; @@ -142,12 +153,11 @@ PaintFlags::FilterQuality GetDecodedFilterQuality( SoftwareImageDecodeCache::SoftwareImageDecodeCache( SkColorType color_type, - size_t locked_memory_limit_bytes, - PaintImage::GeneratorClientId generator_client_id) + size_t locked_memory_limit_bytes) : decoded_images_(ImageLRUCache::NO_AUTO_EVICT), locked_images_budget_(locked_memory_limit_bytes), color_type_(color_type), - generator_client_id_(generator_client_id), + generator_client_id_(PaintImage::GetNextGeneratorClientId()), max_items_in_cache_(kNormalMaxItemsInCacheForSoftware) { DCHECK_NE(generator_client_id_, PaintImage::kDefaultGeneratorClientId); // In certain cases, ThreadTaskRunnerHandle isn't set (Android Webview). @@ -634,6 +644,8 @@ void SoftwareImageDecodeCache::OnImageDecodeTaskCompleted( auto image_it = decoded_images_.Peek(key); DCHECK(image_it != decoded_images_.end()); CacheEntry* cache_entry = image_it->second.get(); + UMA_HISTOGRAM_BOOLEAN("Compositing.DecodeLCPCandidateImage.Software", + key.may_be_lcp_candidate()); auto& task = task_type == DecodeTaskType::USE_IN_RASTER_TASKS ? cache_entry->in_raster_task : cache_entry->out_of_raster_task; diff --git a/chromium/cc/tiles/software_image_decode_cache.h b/chromium/cc/tiles/software_image_decode_cache.h index e88adfe1ef6..dfc55b72965 100644 --- a/chromium/cc/tiles/software_image_decode_cache.h +++ b/chromium/cc/tiles/software_image_decode_cache.h @@ -38,8 +38,7 @@ class CC_EXPORT SoftwareImageDecodeCache enum class TaskProcessingResult { kFullDecode, kLockOnly, kCancelled }; SoftwareImageDecodeCache(SkColorType color_type, - size_t locked_memory_limit_bytes, - PaintImage::GeneratorClientId generator_client_id); + size_t locked_memory_limit_bytes); ~SoftwareImageDecodeCache() override; // ImageDecodeCache overrides. diff --git a/chromium/cc/tiles/software_image_decode_cache_unittest.cc b/chromium/cc/tiles/software_image_decode_cache_unittest.cc index 90abb8dc920..5600eb023a7 100644 --- a/chromium/cc/tiles/software_image_decode_cache_unittest.cc +++ b/chromium/cc/tiles/software_image_decode_cache_unittest.cc @@ -27,9 +27,7 @@ size_t kLockedMemoryLimitBytes = 128 * 1024 * 1024; class TestSoftwareImageDecodeCache : public SoftwareImageDecodeCache { public: TestSoftwareImageDecodeCache() - : SoftwareImageDecodeCache(kN32_SkColorType, - kLockedMemoryLimitBytes, - PaintImage::GetNextGeneratorClientId()) {} + : SoftwareImageDecodeCache(kN32_SkColorType, kLockedMemoryLimitBytes) {} }; SkM44 CreateMatrix(const SkSize& scale, bool is_decomposable) { diff --git a/chromium/cc/tiles/software_image_decode_cache_unittest_combinations.cc b/chromium/cc/tiles/software_image_decode_cache_unittest_combinations.cc index 0a89147856e..6f1fefff476 100644 --- a/chromium/cc/tiles/software_image_decode_cache_unittest_combinations.cc +++ b/chromium/cc/tiles/software_image_decode_cache_unittest_combinations.cc @@ -81,27 +81,24 @@ class BaseTest : public testing::Test { class N32Cache : public virtual BaseTest { protected: std::unique_ptr<SoftwareImageDecodeCache> CreateCache() override { - return std::make_unique<SoftwareImageDecodeCache>( - kN32_SkColorType, kLockedMemoryLimitBytes, - PaintImage::GetNextGeneratorClientId()); + return std::make_unique<SoftwareImageDecodeCache>(kN32_SkColorType, + kLockedMemoryLimitBytes); } }; class RGBA4444Cache : public virtual BaseTest { protected: std::unique_ptr<SoftwareImageDecodeCache> CreateCache() override { - return std::make_unique<SoftwareImageDecodeCache>( - kARGB_4444_SkColorType, kLockedMemoryLimitBytes, - PaintImage::GetNextGeneratorClientId()); + return std::make_unique<SoftwareImageDecodeCache>(kARGB_4444_SkColorType, + kLockedMemoryLimitBytes); } }; class RGBA_F16Cache : public virtual BaseTest { protected: std::unique_ptr<SoftwareImageDecodeCache> CreateCache() override { - return std::make_unique<SoftwareImageDecodeCache>( - kRGBA_F16_SkColorType, kLockedMemoryLimitBytes, - PaintImage::GetNextGeneratorClientId()); + return std::make_unique<SoftwareImageDecodeCache>(kRGBA_F16_SkColorType, + kLockedMemoryLimitBytes); } }; diff --git a/chromium/cc/tiles/software_image_decode_cache_utils.cc b/chromium/cc/tiles/software_image_decode_cache_utils.cc index 43f02627797..a9bdd7ae99c 100644 --- a/chromium/cc/tiles/software_image_decode_cache_utils.cc +++ b/chromium/cc/tiles/software_image_decode_cache_utils.cc @@ -196,7 +196,8 @@ SoftwareImageDecodeCacheUtils::CacheKey::FromDrawImage(const DrawImage& image, // If the target size is empty, then we'll be skipping the decode anyway, so // the filter quality doesn't matter. Early out instead. if (target_size.IsEmpty()) { - return CacheKey(frame_key, stable_id, kSubrectAndScale, false, src_rect, + return CacheKey(frame_key, stable_id, kSubrectAndScale, false, + image.paint_image().may_be_lcp_candidate(), src_rect, target_size, image.target_color_params()); } @@ -250,7 +251,8 @@ SoftwareImageDecodeCacheUtils::CacheKey::FromDrawImage(const DrawImage& image, } } - return CacheKey(frame_key, stable_id, type, is_nearest_neighbor, src_rect, + return CacheKey(frame_key, stable_id, type, is_nearest_neighbor, + image.paint_image().may_be_lcp_candidate(), src_rect, target_size, image.target_color_params()); } @@ -259,6 +261,7 @@ SoftwareImageDecodeCacheUtils::CacheKey::CacheKey( PaintImage::Id stable_id, ProcessingType type, bool is_nearest_neighbor, + bool may_be_lcp_candidate, const gfx::Rect& src_rect, const gfx::Size& target_size, const TargetColorParams& target_color_params) @@ -266,6 +269,7 @@ SoftwareImageDecodeCacheUtils::CacheKey::CacheKey( stable_id_(stable_id), type_(type), is_nearest_neighbor_(is_nearest_neighbor), + may_be_lcp_candidate_(may_be_lcp_candidate), src_rect_(src_rect), target_size_(target_size), target_color_params_(target_color_params) { diff --git a/chromium/cc/tiles/software_image_decode_cache_utils.h b/chromium/cc/tiles/software_image_decode_cache_utils.h index 51aaa1acf0f..c11d05b65ea 100644 --- a/chromium/cc/tiles/software_image_decode_cache_utils.h +++ b/chromium/cc/tiles/software_image_decode_cache_utils.h @@ -74,6 +74,7 @@ class SoftwareImageDecodeCacheUtils { PaintImage::Id stable_id() const { return stable_id_; } ProcessingType type() const { return type_; } bool is_nearest_neighbor() const { return is_nearest_neighbor_; } + bool may_be_lcp_candidate() const { return may_be_lcp_candidate_; } gfx::Rect src_rect() const { return src_rect_; } gfx::Size target_size() const { return target_size_; } const TargetColorParams& target_color_params() const { @@ -99,6 +100,7 @@ class SoftwareImageDecodeCacheUtils { PaintImage::Id stable_id, ProcessingType type, bool is_nearest_neighbor, + bool may_be_lcp_candidate, const gfx::Rect& src_rect, const gfx::Size& size, const TargetColorParams& target_color_params); @@ -110,6 +112,7 @@ class SoftwareImageDecodeCacheUtils { PaintImage::Id stable_id_; ProcessingType type_; bool is_nearest_neighbor_; + bool may_be_lcp_candidate_; gfx::Rect src_rect_; gfx::Size target_size_; TargetColorParams target_color_params_; diff --git a/chromium/cc/tiles/tile.cc b/chromium/cc/tiles/tile.cc index a8cb3fd52e5..1d9edab34e8 100644 --- a/chromium/cc/tiles/tile.cc +++ b/chromium/cc/tiles/tile.cc @@ -83,6 +83,10 @@ void Tile::AsValueInto(base::trace_event::TracedValue* value) const { base::saturated_cast<int>(GPUMemoryUsageInBytes())); } +bool Tile::HasMissingLCPCandidateImages() const { + return HasRasterTask() && raster_task_->TaskContainsLCPCandidateImages(); +} + size_t Tile::GPUMemoryUsageInBytes() const { if (draw_info_.resource_) { // We can use UncheckedSizeInBytes, since the tile size is determined by the diff --git a/chromium/cc/tiles/tile.h b/chromium/cc/tiles/tile.h index cde7850f9e4..60afa44eee1 100644 --- a/chromium/cc/tiles/tile.h +++ b/chromium/cc/tiles/tile.h @@ -112,6 +112,8 @@ class CC_EXPORT Tile { bool HasRasterTask() const { return !!raster_task_.get(); } + bool HasMissingLCPCandidateImages() const; + void set_solid_color_analysis_performed(bool performed) { is_solid_color_analysis_performed_ = performed; } diff --git a/chromium/cc/tiles/tile_draw_info.cc b/chromium/cc/tiles/tile_draw_info.cc index 4b4bac2eb82..e73bb0b7ede 100644 --- a/chromium/cc/tiles/tile_draw_info.cc +++ b/chromium/cc/tiles/tile_draw_info.cc @@ -19,7 +19,7 @@ TileDrawInfo::~TileDrawInfo() { void TileDrawInfo::AsValueInto(base::trace_event::TracedValue* state) const { state->SetBoolean("is_solid_color", mode_ == SOLID_COLOR_MODE); state->SetBoolean("is_transparent", - mode_ == SOLID_COLOR_MODE && !SkColorGetA(solid_color_)); + mode_ == SOLID_COLOR_MODE && !solid_color_.isOpaque()); } void TileDrawInfo::SetResource(ResourcePool::InUsePoolResource resource, diff --git a/chromium/cc/tiles/tile_draw_info.h b/chromium/cc/tiles/tile_draw_info.h index e0fbe4fe598..2e4d35535ba 100644 --- a/chromium/cc/tiles/tile_draw_info.h +++ b/chromium/cc/tiles/tile_draw_info.h @@ -65,7 +65,7 @@ class CC_EXPORT TileDrawInfo { return resource_.format(); } - SkColor solid_color() const { + SkColor4f solid_color() const { DCHECK(mode_ == SOLID_COLOR_MODE); return solid_color_; } @@ -85,7 +85,7 @@ class CC_EXPORT TileDrawInfo { return resource_is_checker_imaged_; } - void SetSolidColorForTesting(SkColor color) { set_solid_color(color); } + void SetSolidColorForTesting(SkColor4f color) { set_solid_color(color); } void AsValueInto(base::trace_event::TracedValue* state) const; @@ -102,7 +102,7 @@ class CC_EXPORT TileDrawInfo { is_resource_ready_to_draw_ = true; } - void set_solid_color(const SkColor& color) { + void set_solid_color(const SkColor4f& color) { DCHECK(!resource_); mode_ = SOLID_COLOR_MODE; solid_color_ = color; @@ -111,7 +111,7 @@ class CC_EXPORT TileDrawInfo { void set_oom() { mode_ = OOM_MODE; } Mode mode_ = RESOURCE_MODE; - SkColor solid_color_ = SK_ColorWHITE; + SkColor4f solid_color_ = SkColors::kWhite; ResourcePool::InUsePoolResource resource_; bool is_premultiplied_ = false; bool is_resource_ready_to_draw_ = false; diff --git a/chromium/cc/tiles/tile_manager.cc b/chromium/cc/tiles/tile_manager.cc index 000c0c5a02d..ea2ddbf8283 100644 --- a/chromium/cc/tiles/tile_manager.cc +++ b/chromium/cc/tiles/tile_manager.cc @@ -176,7 +176,9 @@ class RasterTaskImpl : public TileTask { TileResolution tile_resolution_; int layer_id_; uint64_t source_prepare_tiles_id_; - raw_ptr<void> tile_tracing_id_; + // TODO(crbug.com/1298696): views_unittests breaks with MTECheckedPtr + // enabled. Triage. + raw_ptr<void, DanglingUntriagedDegradeToNoOpWhenMTE> tile_tracing_id_; uint64_t new_content_id_; int source_frame_number_; std::unique_ptr<RasterBuffer> raster_buffer_; @@ -368,7 +370,7 @@ class DidFinishRunningAllTilesTask : public TileTask { private: raw_ptr<base::SequencedTaskRunner> task_runner_; - raw_ptr<RasterQueryQueue> pending_raster_queries_; + raw_ptr<RasterQueryQueue, DanglingUntriaged> pending_raster_queries_; CompletionCb completion_cb_; }; @@ -459,6 +461,7 @@ void TileManager::FinishTasksAndCleanUp() { tile_task_manager_ = nullptr; resource_pool_ = nullptr; + pending_raster_queries_ = nullptr; more_tiles_need_prepare_check_notifier_.Cancel(); signals_check_notifier_.Cancel(); task_set_finished_weak_ptr_factory_.InvalidateWeakPtrs(); @@ -753,7 +756,7 @@ TileManager::PrioritizedWorkToSchedule TileManager::AssignGpuMemoryToTiles() { prioritized_tile.raster_source()->PerformSolidColorAnalysis( tile->enclosing_layer_rect(), &color); if (is_solid_color) { - tile->draw_info().set_solid_color(color.toSkColor()); + tile->draw_info().set_solid_color(color); client_->NotifyTileStateChanged(tile); continue; } @@ -1527,7 +1530,9 @@ void TileManager::IssueSignals() { if (signals_.activate_tile_tasks_completed && signals_.activate_gpu_work_completed && !signals_.did_notify_ready_to_activate) { - if (!client_->HasPendingTree() || IsReadyToActivate()) { + // If commit_to_active_tree is true(no pending tree), NotifyReadyToActivate + // isn't sent to client, so don't call IsReadyToActivate() to save CPU time + if (client_->HasPendingTree() && IsReadyToActivate()) { TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), "TileManager::IssueSignals - ready to activate"); signals_.did_notify_ready_to_activate = true; @@ -1538,7 +1543,7 @@ void TileManager::IssueSignals() { // Ready to draw. if (signals_.draw_tile_tasks_completed && signals_.draw_gpu_work_completed && !signals_.did_notify_ready_to_draw) { - if (IsReadyToDraw()) { + if (tile_manager_settings_.needs_notify_ready_to_draw && IsReadyToDraw()) { TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), "TileManager::IssueSignals - ready to draw"); signals_.did_notify_ready_to_draw = true; @@ -1561,6 +1566,12 @@ void TileManager::IssueSignals() { } } + bool notify_ready_to_activate_pending = + client_->HasPendingTree() && !signals_.did_notify_ready_to_activate; + bool notify_ready_to_draw_pending = + tile_manager_settings_.needs_notify_ready_to_draw && + !signals_.did_notify_ready_to_draw; + // Allow decodes for rasterized tiles if all required for draw/activate tiles // are done. And pre-decode tiles once all tile tasks are done. // Note that the order is important here, since all signals could have become @@ -1568,8 +1579,8 @@ void TileManager::IssueSignals() { if (signals_.did_notify_all_tile_tasks_completed) { checker_image_tracker_.SetMaxDecodePriorityAllowed( CheckerImageTracker::DecodeType::kPreDecode); - } else if (signals_.did_notify_ready_to_activate && - signals_.did_notify_ready_to_draw) { + } else if (!notify_ready_to_activate_pending && + !notify_ready_to_draw_pending) { checker_image_tracker_.SetMaxDecodePriorityAllowed( CheckerImageTracker::DecodeType::kRaster); } diff --git a/chromium/cc/tiles/tile_manager.h b/chromium/cc/tiles/tile_manager.h index ebac463ac34..680fa77b7ce 100644 --- a/chromium/cc/tiles/tile_manager.h +++ b/chromium/cc/tiles/tile_manager.h @@ -441,11 +441,11 @@ class CC_EXPORT TileManager : CheckerImageTrackerClient { bool ShouldRasterOccludedTiles() const; - raw_ptr<TileManagerClient> client_; + raw_ptr<TileManagerClient, DanglingUntriaged> client_; raw_ptr<base::SequencedTaskRunner> task_runner_; - raw_ptr<ResourcePool> resource_pool_; + raw_ptr<ResourcePool, DanglingUntriaged> resource_pool_; std::unique_ptr<TileTaskManager> tile_task_manager_; - raw_ptr<RasterBufferProvider> raster_buffer_provider_; + raw_ptr<RasterBufferProvider, DanglingUntriaged> raster_buffer_provider_; GlobalStateThatImpactsTilePriority global_state_; size_t scheduled_raster_task_limit_; diff --git a/chromium/cc/tiles/tile_manager_settings.h b/chromium/cc/tiles/tile_manager_settings.h index 6a7f7fb1f0c..7c1d81ab409 100644 --- a/chromium/cc/tiles/tile_manager_settings.h +++ b/chromium/cc/tiles/tile_manager_settings.h @@ -13,6 +13,7 @@ struct CC_EXPORT TileManagerSettings { bool use_partial_raster = false; bool enable_checker_imaging = false; size_t min_image_bytes_to_checker = 1 * 1024 * 1024; + bool needs_notify_ready_to_draw = true; }; } // namespace cc diff --git a/chromium/cc/tiles/tile_manager_unittest.cc b/chromium/cc/tiles/tile_manager_unittest.cc index 3086adbf21e..9c329f50969 100644 --- a/chromium/cc/tiles/tile_manager_unittest.cc +++ b/chromium/cc/tiles/tile_manager_unittest.cc @@ -1236,7 +1236,7 @@ TEST_F(TileManagerTilePriorityQueueTest, // On the second iteration, mark everything as ready to draw (solid color). if (i == 1) { TileDrawInfo& draw_info = last_tile.tile()->draw_info(); - draw_info.SetSolidColorForTesting(SK_ColorRED); + draw_info.SetSolidColorForTesting(SkColors::kRed); } queue->Pop(); int eventually_bin_order_correct_count = 0; @@ -1272,7 +1272,7 @@ TEST_F(TileManagerTilePriorityQueueTest, // color). if (i == 1) { TileDrawInfo& draw_info = last_tile.tile()->draw_info(); - draw_info.SetSolidColorForTesting(SK_ColorRED); + draw_info.SetSolidColorForTesting(SkColors::kRed); } } @@ -1519,13 +1519,13 @@ TEST_F(TileManagerTilePriorityQueueTest, NoRasterTasksforSolidColorTiles) { FakeRecordingSource::CreateFilledRecordingSource(layer_bounds); PaintFlags solid_flags; - SkColor solid_color = SkColorSetARGB(255, 12, 23, 34); + SkColor4f solid_color{0.1f, 0.2f, 0.3f, 1.0f}; solid_flags.setColor(solid_color); recording_source->add_draw_rect_with_flags(gfx::Rect(layer_bounds), solid_flags); // Create non solid tile as well, otherwise tilings wouldnt be created. - SkColor non_solid_color = SkColorSetARGB(128, 45, 56, 67); + SkColor4f non_solid_color{0.2f, 0.3f, 0.4f, 0.5f}; PaintFlags non_solid_flags; non_solid_flags.setColor(non_solid_color); @@ -1682,7 +1682,6 @@ TEST_F(TileManagerTest, AllWorkFinished) { base::RunLoop run_loop; EXPECT_FALSE( host_impl()->tile_manager()->HasScheduledTileTasksForTesting()); - EXPECT_CALL(MockHostImpl(), NotifyReadyToActivate()); EXPECT_CALL(MockHostImpl(), NotifyReadyToDraw()); EXPECT_CALL(MockHostImpl(), NotifyAllTileTasksCompleted()) .WillOnce(testing::Invoke([&run_loop]() { run_loop.Quit(); })); @@ -1697,7 +1696,6 @@ TEST_F(TileManagerTest, AllWorkFinished) { base::RunLoop run_loop; EXPECT_FALSE( host_impl()->tile_manager()->HasScheduledTileTasksForTesting()); - EXPECT_CALL(MockHostImpl(), NotifyReadyToActivate()); EXPECT_CALL(MockHostImpl(), NotifyReadyToDraw()); EXPECT_CALL(MockHostImpl(), NotifyAllTileTasksCompleted()) .WillOnce(testing::Invoke([&run_loop]() { run_loop.Quit(); })); @@ -1713,7 +1711,6 @@ TEST_F(TileManagerTest, AllWorkFinished) { base::RunLoop run_loop; EXPECT_FALSE( host_impl()->tile_manager()->HasScheduledTileTasksForTesting()); - EXPECT_CALL(MockHostImpl(), NotifyReadyToActivate()); EXPECT_CALL(MockHostImpl(), NotifyReadyToDraw()); EXPECT_CALL(MockHostImpl(), NotifyAllTileTasksCompleted()) .WillOnce(testing::Invoke([&run_loop]() { run_loop.Quit(); })); @@ -1728,7 +1725,6 @@ TEST_F(TileManagerTest, AllWorkFinished) { base::RunLoop run_loop; EXPECT_FALSE( host_impl()->tile_manager()->HasScheduledTileTasksForTesting()); - EXPECT_CALL(MockHostImpl(), NotifyReadyToActivate()); EXPECT_CALL(MockHostImpl(), NotifyReadyToDraw()); EXPECT_CALL(MockHostImpl(), NotifyAllTileTasksCompleted()) .WillOnce(testing::Invoke([&run_loop]() { run_loop.Quit(); })); @@ -3261,7 +3257,7 @@ TEST_F(CheckerImagingTileManagerTest, ->tile_manager() ->checker_image_tracker() .no_decodes_allowed_for_testing()); - while (!host_impl()->client()->ready_to_draw()) { + while (!host_impl()->tile_manager()->IsReadyToDraw()) { static_cast<SynchronousTaskGraphRunner*>(task_graph_runner()) ->RunSingleTaskForTesting(); base::RunLoop().RunUntilIdle(); diff --git a/chromium/cc/trees/clip_expander.cc b/chromium/cc/trees/clip_expander.cc deleted file mode 100644 index ee3b2560371..00000000000 --- a/chromium/cc/trees/clip_expander.cc +++ /dev/null @@ -1,46 +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. - -#include "cc/trees/clip_expander.h" -#include "cc/trees/effect_node.h" -#include "cc/trees/property_tree.h" -#include "ui/gfx/geometry/transform.h" - -namespace cc { - -ClipExpander::ClipExpander(int filter_effect_id) - : target_effect_id_(filter_effect_id) {} - -ClipExpander::ClipExpander(const ClipExpander& other) = default; - -ClipExpander& ClipExpander::operator=(const ClipExpander& other) = default; - -bool ClipExpander::operator==(const ClipExpander& other) const { - return target_effect_id_ == other.target_effect_id_; -} - -gfx::Rect ClipExpander::MapRect(const gfx::Rect& rect, - const PropertyTrees* property_trees) const { - const EffectNode* effect_node = - property_trees->effect_tree().Node(target_effect_id_); - gfx::Transform filter_draw_transform; - filter_draw_transform.Scale(effect_node->surface_contents_scale.x(), - effect_node->surface_contents_scale.y()); - return effect_node->filters.MapRect(rect, - filter_draw_transform.matrix().asM33()); -} - -gfx::Rect ClipExpander::MapRectReverse( - const gfx::Rect& rect, - const PropertyTrees* property_trees) const { - const EffectNode* effect_node = - property_trees->effect_tree().Node(target_effect_id_); - gfx::Transform filter_draw_transform; - filter_draw_transform.Scale(effect_node->surface_contents_scale.x(), - effect_node->surface_contents_scale.y()); - return effect_node->filters.MapRectReverse( - rect, filter_draw_transform.matrix().asM33()); -} - -} // namespace cc diff --git a/chromium/cc/trees/clip_expander.h b/chromium/cc/trees/clip_expander.h deleted file mode 100644 index fef2ad5ecda..00000000000 --- a/chromium/cc/trees/clip_expander.h +++ /dev/null @@ -1,44 +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_TREES_CLIP_EXPANDER_H_ -#define CC_TREES_CLIP_EXPANDER_H_ - -#include "cc/cc_export.h" -#include "ui/gfx/geometry/rect.h" - -namespace cc { - -class PropertyTrees; - -class CC_EXPORT ClipExpander { - public: - explicit ClipExpander(int filter_effect_id); - ClipExpander(const ClipExpander& other); - ClipExpander& operator=(const ClipExpander& other); - - bool operator==(const ClipExpander& other) const; - - bool operator!=(const ClipExpander& other) const { return !(*this == other); } - - // Maps "forward" to determine which pixels in a destination rect are affected - // by pixels in the given source rect. - gfx::Rect MapRect(const gfx::Rect& rect, - const PropertyTrees* property_trees) const; - - // Maps "backward" to determine which pixels in the source affect the pixels - // in the given destination rect. - gfx::Rect MapRectReverse(const gfx::Rect& rect, - const PropertyTrees* property_trees) const; - - // The id of the effect node in whose transform space the expansion happens. - int target_effect_id() const { return target_effect_id_; } - - private: - int target_effect_id_; -}; - -} // namespace cc - -#endif // CC_TREES_CLIP_EXPANDER_H_ diff --git a/chromium/cc/trees/clip_node.cc b/chromium/cc/trees/clip_node.cc index 9815ce36019..ab9140bf909 100644 --- a/chromium/cc/trees/clip_node.cc +++ b/chromium/cc/trees/clip_node.cc @@ -4,18 +4,17 @@ #include "cc/trees/clip_node.h" +#include "base/trace_event/traced_value.h" #include "cc/base/math_util.h" #include "cc/layers/layer.h" #include "cc/trees/property_tree.h" -#include "base/trace_event/traced_value.h" - namespace cc { ClipNode::ClipNode() : id(kInvalidPropertyNodeId), parent_id(kInvalidPropertyNodeId), - clip_type(ClipType::APPLIES_LOCAL_CLIP), + pixel_moving_filter_id(kInvalidPropertyNodeId), transform_id(kInvalidPropertyNodeId) {} ClipNode::ClipNode(const ClipNode& other) = default; @@ -24,11 +23,14 @@ ClipNode& ClipNode::operator=(const ClipNode& other) = default; ClipNode::~ClipNode() = default; +bool ClipNode::AppliesLocalClip() const { + return pixel_moving_filter_id == kInvalidPropertyNodeId; +} + #if DCHECK_IS_ON() bool ClipNode::operator==(const ClipNode& other) const { - return id == other.id && parent_id == other.parent_id && - clip_type == other.clip_type && clip == other.clip && - clip_expander == other.clip_expander && + return id == other.id && parent_id == other.parent_id && clip == other.clip && + pixel_moving_filter_id == other.pixel_moving_filter_id && transform_id == other.transform_id; } #endif @@ -36,8 +38,8 @@ bool ClipNode::operator==(const ClipNode& other) const { void ClipNode::AsValueInto(base::trace_event::TracedValue* value) const { value->SetInteger("id", id); value->SetInteger("parent_id", parent_id); - value->SetInteger("clip_type", static_cast<int>(clip_type)); MathUtil::AddToTracedValue("clip", clip, value); + value->SetInteger("pixel_moving_filter_id", pixel_moving_filter_id); value->SetInteger("transform_id", transform_id); } diff --git a/chromium/cc/trees/clip_node.h b/chromium/cc/trees/clip_node.h index 16fa425da8c..c187b582c30 100644 --- a/chromium/cc/trees/clip_node.h +++ b/chromium/cc/trees/clip_node.h @@ -7,8 +7,6 @@ #include "base/containers/stack_container.h" #include "cc/cc_export.h" -#include "cc/trees/clip_expander.h" -#include "third_party/abseil-cpp/absl/types/optional.h" #include "ui/gfx/geometry/rect_f.h" namespace base { @@ -37,28 +35,17 @@ struct CC_EXPORT ClipNode { ~ClipNode(); + // Returns true if we should apply |clip|. Otherwise we should map the + // accumulated clip by the filter specified by |pixel_moving_filter_id|. + bool AppliesLocalClip() const; + // The node index of this node in the clip tree node vector. int id; // The node index of the parent node in the clip tree node vector. int parent_id; - enum class ClipType { - // The node contributes a new clip (that is, |clip| needs to be applied). - APPLIES_LOCAL_CLIP, - - // This node represents a space expansion. When computing visible rects, - // the accumulated clip inherited by this node gets expanded. Similarly, - // when mapping a rect in descendant space to the rect in ancestor space - // that depends on the descendant rect's contents, this node expands the - // descendant rect. This is used for effects like pixel-moving filters, - // where clipped-out content can affect visible output. - EXPANDS_CLIP - }; - - ClipType clip_type; - // The clip rect that this node contributes, expressed in the space of its - // transform node. + // transform node. This field is ignored if AppliesLocalClip() is false. gfx::RectF clip; // Each element of this cache stores the accumulated clip from this clip @@ -74,8 +61,11 @@ struct CC_EXPORT ClipNode { // It is used in the computation of layer's visible rect. gfx::RectF cached_accumulated_rect_in_screen_space; - // For nodes that expand, this represents the amount of expansion. - absl::optional<ClipExpander> clip_expander; + // If valid, it's the id of a pixel-moving filter in the effect tree. + // Instead of applying |clip|, this clip node expands the accumulated clip + // to include any pixels in the contents that can affect the rendering result + // with the filter. + int pixel_moving_filter_id; // The id of the transform node that defines the clip node's local space. int transform_id; diff --git a/chromium/cc/trees/commit_state.h b/chromium/cc/trees/commit_state.h index 85f2ad29805..676bfbf1664 100644 --- a/chromium/cc/trees/commit_state.h +++ b/chromium/cc/trees/commit_state.h @@ -102,7 +102,7 @@ struct CC_EXPORT CommitState { LayerSelection selection; LayerTreeDebugState debug_state; OverscrollBehavior overscroll_behavior; - SkColor background_color = SK_ColorWHITE; + SkColor4f background_color = SkColors::kWhite; ViewportPropertyIds viewport_property_ids; viz::LocalSurfaceId local_surface_id_from_parent; base::TimeDelta previous_surfaces_visual_update_duration; diff --git a/chromium/cc/trees/draw_properties_unittest.cc b/chromium/cc/trees/draw_properties_unittest.cc index 6534341fdfa..016ed515758 100644 --- a/chromium/cc/trees/draw_properties_unittest.cc +++ b/chromium/cc/trees/draw_properties_unittest.cc @@ -976,6 +976,8 @@ TEST_F(DrawPropertiesTest, DrawableContentRectForReferenceFilter) { auto& child_effect_node = CreateEffectNode(child); child_effect_node.render_surface_reason = RenderSurfaceReason::kTest; child_effect_node.filters = filters; + auto& child_clip_node = CreateClipNode(child); + child_clip_node.pixel_moving_filter_id = child_effect_node.id; UpdateActiveTreeDrawProperties(); @@ -1005,6 +1007,8 @@ TEST_F(DrawPropertiesTest, DrawableContentRectForReferenceFilterHighDpi) { auto& child_effect_node = CreateEffectNode(child); child_effect_node.render_surface_reason = RenderSurfaceReason::kTest; child_effect_node.filters = filters; + auto& child_clip_node = CreateClipNode(child); + child_clip_node.pixel_moving_filter_id = child_effect_node.id; UpdateActiveTreeDrawProperties(device_scale_factor); @@ -1017,6 +1021,53 @@ TEST_F(DrawPropertiesTest, DrawableContentRectForReferenceFilterHighDpi) { GetRenderSurface(child)->DrawableContentRect()); } +TEST_F(DrawPropertiesTest, VisibleLayerRectForBlurFilterUnderClip) { + LayerImpl* root = root_layer(); + LayerImpl* child = AddLayer<LayerImpl>(); + + root->SetBounds(gfx::Size(100, 100)); + child->SetBounds(gfx::Size(300, 300)); + child->SetDrawsContent(true); + FilterOperations filters; + filters.Append(FilterOperation::CreateBlurFilter(10)); + + CreateClipNode(root); + CopyProperties(root, child); + child->SetOffsetToTransformParent(gfx::Vector2dF(-100, -100)); + auto& filter_node = CreateEffectNode(child); + filter_node.render_surface_reason = RenderSurfaceReason::kFilter; + filter_node.filters = filters; + auto& clip_node = CreateClipNode(child); + clip_node.pixel_moving_filter_id = filter_node.id; + + UpdateActiveTreeDrawProperties(); + EXPECT_EQ(gfx::Rect(70, 70, 160, 160), child->visible_layer_rect()); +} + +TEST_F(DrawPropertiesTest, VisibleLayerRectForReferenceFilterUnderClip) { + LayerImpl* root = root_layer(); + LayerImpl* child = AddLayer<LayerImpl>(); + + root->SetBounds(gfx::Size(100, 100)); + child->SetBounds(gfx::Size(300, 300)); + child->SetDrawsContent(true); + FilterOperations filters; + filters.Append(FilterOperation::CreateReferenceFilter( + sk_make_sp<OffsetPaintFilter>(50, 50, nullptr))); + + CreateClipNode(root); + CopyProperties(root, child); + child->SetOffsetToTransformParent(gfx::Vector2dF(-100, -100)); + auto& filter_node = CreateEffectNode(child); + filter_node.render_surface_reason = RenderSurfaceReason::kFilter; + filter_node.filters = filters; + auto& clip_node = CreateClipNode(child); + clip_node.pixel_moving_filter_id = filter_node.id; + + UpdateActiveTreeDrawProperties(); + EXPECT_EQ(gfx::Rect(100, 100, 150, 150), child->visible_layer_rect()); +} + TEST_F(DrawPropertiesTest, RenderSurfaceForBlendMode) { LayerImpl* root = root_layer(); LayerImpl* child = AddLayer<LayerImpl>(); @@ -2410,6 +2461,32 @@ TEST_F(DrawPropertiesTest, EXPECT_EQ(gfx::Rect(), grand_child->visible_layer_rect()); } +TEST_F(DrawPropertiesTest, ClipExpanderWithUninvertibleTransform) { + LayerImpl* root = root_layer(); + LayerImpl* child = AddLayer<LayerImpl>(); + + root->SetBounds(gfx::Size(100, 100)); + child->SetBounds(gfx::Size(50, 50)); + child->SetDrawsContent(true); + + gfx::Transform uninvertible_matrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); + ASSERT_FALSE(uninvertible_matrix.IsInvertible()); + + CopyProperties(root, child); + CreateTransformNode(child).local = uninvertible_matrix; + FilterOperations filters; + auto& filter_node = CreateEffectNode(child); + filter_node.render_surface_reason = RenderSurfaceReason::kFilter; + filter_node.filters.Append(FilterOperation::CreateBlurFilter(10)); + auto& clip_node = CreateClipNode(child); + clip_node.pixel_moving_filter_id = filter_node.id; + + UpdateActiveTreeDrawProperties(); + + EXPECT_TRUE(child->visible_layer_rect().IsEmpty()); + EXPECT_TRUE(child->visible_drawable_content_rect().IsEmpty()); +} + // Needs layer tree mode: mask layer. TEST_F(DrawPropertiesTestWithLayerTree, OcclusionBySiblingOfTarget) { auto root = Layer::Create(); @@ -5411,6 +5488,171 @@ TEST_F(DrawPropertiesStickyPositionTest, StickyPositionNested) { inner_sticky_impl->ScreenSpaceTransform().To2dTranslation()); } +class DrawPropertiesAnchorScrollTest : public DrawPropertiesTest { + protected: + void CreateRoot() { + root_ = Layer::Create(); + root_->SetBounds(gfx::Size(100, 100)); + host()->SetRootLayer(root_); + SetupRootProperties(root_.get()); + } + + std::pair<scoped_refptr<Layer>, scoped_refptr<Layer>> CreateScroller( + Layer* parent) { + scoped_refptr<Layer> container = Layer::Create(); + scoped_refptr<Layer> scroller = Layer::Create(); + scroller->SetElementId(LayerIdToElementIdForTesting(scroller->id())); + + container->SetBounds(gfx::Size(100, 100)); + CopyProperties(parent, container.get()); + root_->AddChild(container); + + scroller->SetBounds(gfx::Size(1000, 1000)); + CopyProperties(container.get(), scroller.get()); + CreateTransformNode(scroller.get()); + CreateScrollNode(scroller.get(), container->bounds()); + root_->AddChild(scroller); + + return std::make_pair(std::move(container), std::move(scroller)); + } + + scoped_refptr<Layer> CreateAnchored(Layer* parent, + Layer* inner_most_scroller, + Layer* outer_most_scroller) { + scoped_refptr<Layer> anchored = Layer::Create(); + anchored->SetBounds(gfx::Size(10, 10)); + CopyProperties(parent, anchored.get()); + CreateTransformNode(anchored.get()); + SetAnchorScrollContainers(anchored.get(), + inner_most_scroller->scroll_tree_index(), + outer_most_scroller->scroll_tree_index()); + root_->AddChild(anchored); + return anchored; + } + + void Commit() { + UpdateMainDrawProperties(); + host_impl()->CreatePendingTree(); + host()->CommitAndCreatePendingTree(); + host_impl()->ActivateSyncTree(); + } + + LayerImpl* GetImpl(Layer* layer) { + LayerTreeImpl* layer_tree_impl = host_impl()->active_tree(); + return layer_tree_impl->LayerById(layer->id()); + } + + void SetAnchorScrollContainers(Layer* anchored, + int inner_most_scroll_container_id, + int outer_most_scroll_container_id) { + auto& data = + GetPropertyTrees(anchored) + ->transform_tree_mutable() + .EnsureAnchorScrollContainersData(anchored->transform_tree_index()); + data.inner_most_scroll_container_id = inner_most_scroll_container_id; + data.outer_most_scroll_container_id = outer_most_scroll_container_id; + } + + scoped_refptr<Layer> root_; +}; + +TEST_F(DrawPropertiesAnchorScrollTest, Basics) { + // Virtual layer hierarchy: + // + root + // + container + // + scroller <-- anchor + // + anchored + CreateRoot(); + + scoped_refptr<Layer> container; + scoped_refptr<Layer> scroller; + std::tie(container, scroller) = CreateScroller(root_.get()); + + scoped_refptr<Layer> anchored = + CreateAnchored(root_.get(), scroller.get(), scroller.get()); + + SetPostTranslation(anchored.get(), gfx::Vector2dF(10, 20)); + Commit(); + + EXPECT_VECTOR2DF_EQ( + gfx::Vector2dF(10, 20), + GetImpl(anchored.get())->ScreenSpaceTransform().To2dTranslation()); + + // Scroll the scroller. Anchored element should always move with it. + + SetScrollOffsetDelta(GetImpl(scroller.get()), gfx::Vector2dF(5, 5)); + + UpdateActiveTreeDrawProperties(); + EXPECT_VECTOR2DF_EQ( + gfx::Vector2dF(5, 15), + GetImpl(anchored.get())->ScreenSpaceTransform().To2dTranslation()); + + SetScrollOffsetDelta(GetImpl(scroller.get()), gfx::Vector2dF(15, 25)); + + UpdateActiveTreeDrawProperties(); + EXPECT_VECTOR2DF_EQ( + gfx::Vector2dF(-5, -5), + GetImpl(anchored.get())->ScreenSpaceTransform().To2dTranslation()); +} + +TEST_F(DrawPropertiesAnchorScrollTest, NestedScrollers) { + // Virtual layer hierarchy: + // + root + // + container1 + // + scroller1 + // + container2 + // + scroller2 + // + container3 + // + scroller3 <-- anchor + // + anchored + CreateRoot(); + + scoped_refptr<Layer> container1; + scoped_refptr<Layer> scroller1; + std::tie(container1, scroller1) = CreateScroller(root_.get()); + + scoped_refptr<Layer> container2; + scoped_refptr<Layer> scroller2; + std::tie(container2, scroller2) = CreateScroller(scroller1.get()); + + scoped_refptr<Layer> container3; + scoped_refptr<Layer> scroller3; + std::tie(container3, scroller3) = CreateScroller(scroller2.get()); + + scoped_refptr<Layer> anchored = + CreateAnchored(scroller1.get(), scroller3.get(), scroller2.get()); + + SetPostTranslation(anchored.get(), gfx::Vector2dF(10, 20)); + Commit(); + + EXPECT_VECTOR2DF_EQ( + gfx::Vector2dF(10, 20), + GetImpl(anchored.get())->ScreenSpaceTransform().To2dTranslation()); + + // Scrolling scroller3 will apply the same translation offset to the anchored + // element even if it's not a descendant of scroller 3. + SetScrollOffsetDelta(GetImpl(scroller3.get()), gfx::Vector2dF(5, 5)); + UpdateActiveTreeDrawProperties(); + EXPECT_VECTOR2DF_EQ( + gfx::Vector2dF(5, 15), + GetImpl(anchored.get())->ScreenSpaceTransform().To2dTranslation()); + + // Same to scroller 2. + SetScrollOffsetDelta(GetImpl(scroller2.get()), gfx::Vector2dF(10, 15)); + UpdateActiveTreeDrawProperties(); + EXPECT_VECTOR2DF_EQ( + gfx::Vector2dF(-5, 0), + GetImpl(anchored.get())->ScreenSpaceTransform().To2dTranslation()); + + // Scrolling scroller1 natually moves the anchored element because it's + // already a descendant. Note that we should not apply a double translation + // offset to it. + SetScrollOffsetDelta(GetImpl(scroller1.get()), gfx::Vector2dF(15, 20)); + UpdateActiveTreeDrawProperties(); + EXPECT_VECTOR2DF_EQ( + gfx::Vector2dF(-20, -20), + GetImpl(anchored.get())->ScreenSpaceTransform().To2dTranslation()); +} class AnimationScaleFactorTrackingLayerImpl : public LayerImpl { public: static std::unique_ptr<AnimationScaleFactorTrackingLayerImpl> Create( diff --git a/chromium/cc/trees/draw_property_utils.cc b/chromium/cc/trees/draw_property_utils.cc index b9d99c69cb4..5c592fac0c0 100644 --- a/chromium/cc/trees/draw_property_utils.cc +++ b/chromium/cc/trees/draw_property_utils.cc @@ -161,58 +161,65 @@ ConditionalClip ComputeCurrentClip(const ClipNode* clip_node, return ConditionalClip{true /* is_clipped */, current_clip}; } +bool ExpandClipForPixelMovingFilter(const PropertyTrees* property_trees, + int target_id, + const EffectNode* filter_node, + gfx::RectF* clip_rect) { + // Bring the accumulated clip to the space of the pixel-moving filter. + gfx::RectF clip_rect_in_mapping_space; + bool success = ConvertRectBetweenSurfaceSpaces(property_trees, target_id, + filter_node->id, *clip_rect, + &clip_rect_in_mapping_space); + // If transform is not invertible, no clip will be applied. + if (!success) + return false; + + // Do the expansion. + SkMatrix filter_draw_matrix = + SkMatrix::Scale(filter_node->surface_contents_scale.x(), + filter_node->surface_contents_scale.y()); + gfx::RectF mapped_clip_in_mapping_space(filter_node->filters.MapRect( + ToEnclosingClipRect(clip_rect_in_mapping_space), filter_draw_matrix)); + + // Put the expanded clip back into the original target space. + gfx::RectF original_clip_rect = *clip_rect; + success = ConvertRectBetweenSurfaceSpaces( + property_trees, filter_node->id, target_id, mapped_clip_in_mapping_space, + clip_rect); + // If transform is not invertible, no clip will be applied. + if (!success) + return false; + + // Ensure the clip is expanded in the target space, in case that the + // mapped accumulated_clip doesn't contain the original. + clip_rect->Union(original_clip_rect); + return true; +} + bool ApplyClipNodeToAccumulatedClip(const PropertyTrees* property_trees, bool include_expanding_clips, int target_id, int target_transform_id, const ClipNode* clip_node, gfx::RectF* accumulated_clip) { - switch (clip_node->clip_type) { - case ClipNode::ClipType::APPLIES_LOCAL_CLIP: { - ConditionalClip current_clip = ComputeCurrentClip( - clip_node, property_trees, target_transform_id, target_id); - - // If transform is not invertible, no clip will be applied. - if (!current_clip.is_clipped) - return false; - - *accumulated_clip = - gfx::IntersectRects(*accumulated_clip, current_clip.clip_rect); + if (!clip_node->AppliesLocalClip()) { + if (!include_expanding_clips) return true; - } - case ClipNode::ClipType::EXPANDS_CLIP: { - if (!include_expanding_clips) - return true; - - // Bring the accumulated clip to the space of the expanding effect. - const EffectNode* expanding_effect_node = - property_trees->effect_tree().Node( - clip_node->clip_expander->target_effect_id()); - gfx::RectF accumulated_clip_rect_in_expanding_space; - bool success = ConvertRectBetweenSurfaceSpaces( - property_trees, target_id, expanding_effect_node->id, - *accumulated_clip, &accumulated_clip_rect_in_expanding_space); - // If transform is not invertible, no clip will be applied. - if (!success) - return false; - - // Do the expansion. - gfx::RectF expanded_clip_in_expanding_space = - gfx::RectF(clip_node->clip_expander->MapRect( - ToEnclosingClipRect(accumulated_clip_rect_in_expanding_space), - property_trees)); - - // Put the expanded clip back into the original target space. - success = ConvertRectBetweenSurfaceSpaces( - property_trees, expanding_effect_node->id, target_id, - expanded_clip_in_expanding_space, accumulated_clip); - // If transform is not invertible, no clip will be applied. - if (!success) - return false; - return true; - } + const EffectNode* filter_node = + property_trees->effect_tree().Node(clip_node->pixel_moving_filter_id); + DCHECK(filter_node); + return ExpandClipForPixelMovingFilter(property_trees, target_id, + filter_node, accumulated_clip); } - NOTREACHED(); + + ConditionalClip current_clip = ComputeCurrentClip( + clip_node, property_trees, target_transform_id, target_id); + + // If transform is not invertible, no clip will be applied. + if (!current_clip.is_clipped) + return false; + + accumulated_clip->Intersect(current_clip.clip_rect); return true; } @@ -293,13 +300,12 @@ ConditionalClip ComputeAccumulatedClip(PropertyTrees* property_trees, } else { // No cache hit or the cached clip has no clip to apply. We need to find // the first clip that applies clip as there is no clip to expand. - while (clip_node->clip_type != ClipNode::ClipType::APPLIES_LOCAL_CLIP && - parent_chain.size() > 0) { + while (!clip_node->AppliesLocalClip() && parent_chain.size() > 0) { clip_node = parent_chain.top(); parent_chain.pop(); } - if (clip_node->clip_type != ClipNode::ClipType::APPLIES_LOCAL_CLIP) { + if (!clip_node->AppliesLocalClip()) { // No clip to apply. cached_data->clip = unclipped; return unclipped; @@ -555,7 +561,7 @@ void SetSurfaceIsClipped(const ClipTree& clip_tree, // If the clips between the render surface and its target only expand the // clips and do not apply any new clip, we need not clip the render surface. const ClipNode* clip_node = clip_tree.Node(render_surface->ClipTreeIndex()); - is_clipped = clip_node->clip_type != ClipNode::ClipType::EXPANDS_CLIP; + is_clipped = clip_node->AppliesLocalClip(); } render_surface->SetIsClipped(is_clipped); } @@ -847,8 +853,8 @@ void ComputeClips(PropertyTrees* property_trees) { bool success = ApplyClipNodeToAccumulatedClip( property_trees, include_expanding_clips, target_effect_id, target_transform_id, clip_node, &accumulated_clip); - DCHECK(success); - clip_node->cached_accumulated_rect_in_screen_space = accumulated_clip; + if (success) + clip_node->cached_accumulated_rect_in_screen_space = accumulated_clip; } clip_tree->set_needs_update(false); } diff --git a/chromium/cc/trees/effect_node.cc b/chromium/cc/trees/effect_node.cc index ffaddbaecc2..4397d4178e1 100644 --- a/chromium/cc/trees/effect_node.cc +++ b/chromium/cc/trees/effect_node.cc @@ -189,7 +189,8 @@ void EffectNode::AsValueInto(base::trace_event::TracedValue* value) const { } if (mask_filter_info.HasGradientMask()) { MathUtil::AddToTracedValue("mask_filter_gradient_mask", - mask_filter_info.gradient_mask(), value); + mask_filter_info.gradient_mask().value(), + value); } } value->SetString("blend_mode", SkBlendMode_Name(blend_mode)); diff --git a/chromium/cc/trees/layer_tree_host.cc b/chromium/cc/trees/layer_tree_host.cc index 9636bffb09f..e40fd58b7ef 100644 --- a/chromium/cc/trees/layer_tree_host.cc +++ b/chromium/cc/trees/layer_tree_host.cc @@ -650,10 +650,12 @@ bool LayerTreeHost::IsDeferringCommits() const { return proxy_->IsDeferringCommits(); } -void LayerTreeHost::OnDeferCommitsChanged(bool defer_status, - PaintHoldingReason reason) { +void LayerTreeHost::OnDeferCommitsChanged( + bool defer_status, + PaintHoldingReason reason, + absl::optional<PaintHoldingCommitTrigger> trigger) { DCHECK(IsMainThread()); - client_->OnDeferCommitsChanged(defer_status, reason); + client_->OnDeferCommitsChanged(defer_status, reason, trigger); } DISABLE_CFI_PERF @@ -1978,9 +1980,9 @@ LayerTreeHost::TakeDocumentTransitionCallbacksForTesting() { return result; } -uint32_t LayerTreeHost::GetAverageThroughput() const { +double LayerTreeHost::GetPercentDroppedFrames() const { DCHECK(IsMainThread()); - return proxy_->GetAverageThroughput(); + return proxy_->GetPercentDroppedFrames(); } void LayerTreeHost::IncrementVisualUpdateDuration( diff --git a/chromium/cc/trees/layer_tree_host.h b/chromium/cc/trees/layer_tree_host.h index abe129ddb22..cdba296d01c 100644 --- a/chromium/cc/trees/layer_tree_host.h +++ b/chromium/cc/trees/layer_tree_host.h @@ -317,7 +317,9 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient { bool IsDeferringCommits() const; // Notification that the proxy started or stopped deferring commits. - void OnDeferCommitsChanged(bool defer_status, PaintHoldingReason reason); + void OnDeferCommitsChanged(bool defer_status, + PaintHoldingReason reason, + absl::optional<PaintHoldingCommitTrigger> trigger); // Returns whether there are any outstanding ScopedDeferMainFrameUpdate, // though commits may be deferred also when the local_surface_id_from_parent() @@ -498,10 +500,10 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient { return pending_commit_state()->max_page_scale_factor; } - void set_background_color(SkColor color) { + void set_background_color(SkColor4f color) { pending_commit_state()->background_color = color; } - SkColor background_color() const { + SkColor4f background_color() const { return pending_commit_state()->background_color; } @@ -837,8 +839,8 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient { std::vector<base::OnceClosure> TakeDocumentTransitionCallbacksForTesting(); - // Returns a percentage representing average throughput of last X seconds. - uint32_t GetAverageThroughput() const; + // Returns a percentage of dropped frames of the last second. + double GetPercentDroppedFrames() const; // TODO(szager): Remove these once threaded compositing is enabled for all // web_tests. diff --git a/chromium/cc/trees/layer_tree_host_client.h b/chromium/cc/trees/layer_tree_host_client.h index f52db87ce1a..63415aad372 100644 --- a/chromium/cc/trees/layer_tree_host_client.h +++ b/chromium/cc/trees/layer_tree_host_client.h @@ -13,6 +13,7 @@ #include "cc/input/browser_controls_state.h" #include "cc/metrics/event_latency_tracker.h" #include "cc/metrics/frame_sequence_tracker_collection.h" +#include "cc/trees/paint_holding_commit_trigger.h" #include "cc/trees/paint_holding_reason.h" #include "cc/trees/property_tree.h" #include "ui/gfx/geometry/vector2d_f.h" @@ -125,9 +126,13 @@ class LayerTreeHostClient { virtual void OnDeferMainFrameUpdatesChanged(bool) = 0; // Notification that the proxy started or stopped deferring commits. |reason| - // indicates why commits are/were deferred. - virtual void OnDeferCommitsChanged(bool defer_status, - PaintHoldingReason reason) = 0; + // indicates why commits are/were deferred. |trigger| indicates why the commit + // restarted. |trigger| is always provided on restarts, when |defer_status| + // switches to false. + virtual void OnDeferCommitsChanged( + bool defer_status, + PaintHoldingReason reason, + absl::optional<PaintHoldingCommitTrigger> trigger) = 0; // Visual frame-based updates to the state of the LayerTreeHost are expected // to happen only in calls to LayerTreeHostClient::UpdateLayerTreeHost, which diff --git a/chromium/cc/trees/layer_tree_host_impl.cc b/chromium/cc/trees/layer_tree_host_impl.cc index aca208728d0..5542037a338 100644 --- a/chromium/cc/trees/layer_tree_host_impl.cc +++ b/chromium/cc/trees/layer_tree_host_impl.cc @@ -257,6 +257,26 @@ void RecordSourceIdConsistency(bool all_valid, bool all_unique) { consistency); } +// Dump verbose log with +// --vmodule=layer_tree_host_impl=3 for renderer only, or +// --vmodule=layer_tree_host_impl=4 for all clients. +bool VerboseLogEnabled() { + if (!VLOG_IS_ON(3)) + return false; + if (VLOG_IS_ON(4)) + return true; + const char* client_name = GetClientNameForMetrics(); + return client_name && strcmp(client_name, "Renderer") == 0; +} + +const char* ClientNameForVerboseLog() { + const char* client_name = GetClientNameForMetrics(); + return client_name ? client_name : "<unknown client>"; +} + +#define VERBOSE_LOG() \ + VLOG_IF(3, VerboseLogEnabled()) << ClientNameForVerboseLog() << ": " + } // namespace void LayerTreeHostImpl::DidUpdateScrollAnimationCurve() { @@ -385,12 +405,6 @@ LayerTreeHostImpl::LayerTreeHostImpl( scheduling_client_(scheduling_client), task_runner_provider_(task_runner_provider), current_begin_frame_tracker_(FROM_HERE), - compositor_frame_reporting_controller_( - std::make_unique<CompositorFrameReportingController>( - /*should_report_histograms=*/!settings - .single_thread_proxy_scheduler, - /*should_report_ukm=*/!settings.single_thread_proxy_scheduler, - id)), settings_(settings), is_synchronous_single_threaded_(!task_runner_provider->HasImplThread() && !settings_.single_thread_proxy_scheduler), @@ -418,7 +432,12 @@ LayerTreeHostImpl::LayerTreeHostImpl( image_animation_controller_(GetTaskRunner(), this, settings_.enable_image_animation_resync), - paint_image_generator_client_id_(PaintImage::GetNextGeneratorClientId()), + compositor_frame_reporting_controller_( + std::make_unique<CompositorFrameReportingController>( + /*should_report_histograms=*/!settings + .single_thread_proxy_scheduler, + /*should_report_ukm=*/!settings.single_thread_proxy_scheduler, + id)), frame_trackers_(settings.single_thread_proxy_scheduler, compositor_frame_reporting_controller_.get()), lcd_text_metrics_reporter_(LCDTextMetricsReporter::CreateIfNeeded(this)), @@ -463,11 +482,11 @@ LayerTreeHostImpl::LayerTreeHostImpl( if (is_ui) { compositor_frame_reporting_controller_->set_event_latency_tracker(this); -#if BUILDFLAG(IS_CHROMEOS_ASH) +#if BUILDFLAG(IS_CHROMEOS) dropped_frame_counter_.EnableReporForUI(); compositor_frame_reporting_controller_->SetThreadAffectsSmoothness( FrameInfo::SmoothEffectDrivingThread::kMain, true); -#endif // BUILDFLAG(IS_CHROMEOS_ASH) +#endif // BUILDFLAG(IS_CHROMEOS) } dropped_frame_counter_.set_total_counter(&total_frame_counter_); @@ -513,19 +532,27 @@ LayerTreeHostImpl::~LayerTreeHostImpl() { // Clear the UKM Manager so that we do not try to report when the // UKM System has shut down. compositor_frame_reporting_controller_->SetUkmManager(nullptr); - compositor_frame_reporting_controller_ = nullptr; + // `frame_trackers_` holds a pointer to + // `compositor_frame_reporting_controller_`. Setting + // `compositor_frame_reporting_controller_` to nullptr here leads to + // `frame_trackers_` holding a dangling ptr. Don't set to null here and let + // members be destroyed in reverse order of declaration. + // Since `frame_trackers_` is destroyed first, we need to clear the ptr that + // `compositor_frame_reporting_controller_` holds. + compositor_frame_reporting_controller_->SetFrameSequenceTrackerCollection( + nullptr); } -ThreadedInputHandler& LayerTreeHostImpl::GetInputHandler() { +InputHandler& LayerTreeHostImpl::GetInputHandler() { DCHECK(input_delegate_) << "Requested InputHandler when one wasn't bound. " "Call BindToInputHandler to bind to one"; - return static_cast<ThreadedInputHandler&>(*input_delegate_.get()); + return static_cast<InputHandler&>(*input_delegate_.get()); } -const ThreadedInputHandler& LayerTreeHostImpl::GetInputHandler() const { +const InputHandler& LayerTreeHostImpl::GetInputHandler() const { DCHECK(input_delegate_) << "Requested InputHandler when one wasn't bound. " "Call BindToInputHandler to bind to one"; - return static_cast<const ThreadedInputHandler&>(*input_delegate_.get()); + return static_cast<const InputHandler&>(*input_delegate_.get()); } void LayerTreeHostImpl::DidSendBeginMainFrame(const viz::BeginFrameArgs& args) { @@ -547,8 +574,10 @@ void LayerTreeHostImpl::BeginMainFrameAborted( // If the begin frame data was handled, then scroll and scale set was applied // by the main thread, so the active tree needs to be updated as if these sent // values were applied and committed. - if (CommitEarlyOutHandledCommit(reason)) { - active_tree_->ApplySentScrollAndScaleDeltasFromAbortedCommit(); + bool main_frame_applied_deltas = MainFrameAppliedDeltas(reason); + active_tree_->ApplySentScrollAndScaleDeltasFromAbortedCommit( + main_frame_applied_deltas); + if (main_frame_applied_deltas) { if (pending_tree_) { pending_tree_->AppendSwapPromises(std::move(swap_promises)); } else { @@ -627,19 +656,12 @@ void LayerTreeHostImpl::FinishCommit( for (auto& benchmark : state.benchmarks) ScheduleMicroBenchmark(std::move(benchmark)); - // Dump property trees and layers if run with: - // --vmodule=layer_tree_host=3 - if (VLOG_IS_ON(3)) { - const char* client_name = GetClientNameForMetrics(); - if (!client_name) - client_name = "<unknown client>"; - VLOG(3) << "After finishing (" << client_name - << ") commit on impl, the sync tree:" - << "\nproperty_trees:\n" - << tree->property_trees()->ToString() << "\n" - << "cc::LayerImpls:\n" - << tree->LayerListAsJson(); - } + // Dump property trees and layers if VerboseLogEnabled(). + VERBOSE_LOG() << "After finishing commit on impl, the sync tree:" + << "\nproperty_trees:\n" + << tree->property_trees()->ToString() << "\n" + << "cc::LayerImpls:\n" + << tree->LayerListAsJson(); } void LayerTreeHostImpl::PullLayerTreeHostPropertiesFrom( @@ -701,7 +723,7 @@ void LayerTreeHostImpl::CommitComplete() { // Start animations before UpdateDrawProperties and PrepareTiles, as they can // change the results. When doing commit to the active tree, this must happen - // after ActivateAnimations() in order for this ticking to be propogated + // after ActivateAnimations() in order for this ticking to be propagated // to layers on the active tree. if (CommitToActiveTree()) Animate(); @@ -1081,7 +1103,7 @@ void LayerTreeHostImpl::FrameData::AsValueInto( // Quad data can be quite large, so only dump render passes if we are // logging verbosely or viz.quads tracing category is enabled. - bool quads_enabled = VLOG_IS_ON(3); + bool quads_enabled = VerboseLogEnabled(); if (!quads_enabled) { TRACE_EVENT_CATEGORY_GROUP_ENABLED(TRACE_DISABLED_BY_DEFAULT("viz.quads"), &quads_enabled); @@ -1116,9 +1138,9 @@ DrawMode LayerTreeHostImpl::GetDrawMode() const { static void AppendQuadsToFillScreen( viz::CompositorRenderPass* target_render_pass, const RenderSurfaceImpl* root_render_surface, - SkColor screen_background_color, + SkColor4f screen_background_color, const Region& fill_region) { - if (!root_render_surface || !SkColorGetA(screen_background_color)) + if (!root_render_surface || !screen_background_color.fA) return; if (fill_region.IsEmpty()) return; @@ -1131,13 +1153,12 @@ static void AppendQuadsToFillScreen( gfx::Rect root_target_rect = root_render_surface->content_rect(); float opacity = 1.f; int sorting_context_id = 0; - bool are_contents_opaque = SkColorGetA(screen_background_color) == 0xFF; viz::SharedQuadState* shared_quad_state = target_render_pass->CreateAndAppendSharedQuadState(); shared_quad_state->SetAll(gfx::Transform(), root_target_rect, root_target_rect, gfx::MaskFilterInfo(), - absl::nullopt, are_contents_opaque, opacity, - SkBlendMode::kSrcOver, sorting_context_id); + absl::nullopt, screen_background_color.isOpaque(), + opacity, SkBlendMode::kSrcOver, sorting_context_id); for (gfx::Rect screen_space_rect : fill_region) { gfx::Rect visible_screen_space_rect = screen_space_rect; @@ -1442,7 +1463,7 @@ DrawResult LayerTreeHostImpl::CalculateRenderPasses(FrameData* frame) { active_tree_->GetDeviceViewport().origin()); #endif bool has_transparent_background = - SkColorGetA(active_tree_->background_color()) != SK_AlphaOPAQUE; + !active_tree_->background_color().isOpaque(); auto* root_render_surface = active_tree_->RootRenderSurface(); if (root_render_surface && !has_transparent_background) { frame->render_passes.back()->has_transparent_background = false; @@ -1539,36 +1560,41 @@ DrawResult LayerTreeHostImpl::PrepareToDraw(FrameData* frame) { if (input_delegate_) input_delegate_->WillDraw(); - // |client_name| is used for various UMA histograms below. - // GetClientNameForMetrics only returns one non-null value over the lifetime - // of the process, so the histogram names are runtime constant. - const char* client_name = GetClientNameForMetrics(); - if (client_name) { - size_t total_gpu_memory_for_tilings_in_bytes = 0; - for (const PictureLayerImpl* layer : active_tree()->picture_layers()) - total_gpu_memory_for_tilings_in_bytes += layer->GPUMemoryUsageInBytes(); - - UMA_HISTOGRAM_CUSTOM_COUNTS( - base::StringPrintf("Compositing.%s.NumActiveLayers", client_name), - base::saturated_cast<int>(active_tree_->NumLayers()), 1, 1000, 20); - - UMA_HISTOGRAM_CUSTOM_COUNTS( - base::StringPrintf("Compositing.%s.NumActivePictureLayers", - client_name), - base::saturated_cast<int>(active_tree_->picture_layers().size()), 1, - 1000, 20); - - // TODO(pdr): Instead of skipping empty picture layers, maybe we should - // accumulate layer->GetRasterSource()->GetMemoryUsage() above and skip - // recording when the accumulated memory usage is 0. - if (!active_tree()->picture_layers().empty()) { + // No need to record metrics each time we draw, 1% is enough. + constexpr double kSamplingFrequency = .01; + if (!downsample_metrics_ || + metrics_subsampler_.ShouldSample(kSamplingFrequency)) { + // |client_name| is used for various UMA histograms below. + // GetClientNameForMetrics only returns one non-null value over the lifetime + // of the process, so the histogram names are runtime constant. + const char* client_name = GetClientNameForMetrics(); + if (client_name) { + size_t total_gpu_memory_for_tilings_in_bytes = 0; + for (const PictureLayerImpl* layer : active_tree()->picture_layers()) + total_gpu_memory_for_tilings_in_bytes += layer->GPUMemoryUsageInBytes(); + UMA_HISTOGRAM_CUSTOM_COUNTS( - base::StringPrintf("Compositing.%s.GPUMemoryForTilingsInKb", + base::StringPrintf("Compositing.%s.NumActiveLayers", client_name), + base::saturated_cast<int>(active_tree_->NumLayers()), 1, 1000, 20); + + UMA_HISTOGRAM_CUSTOM_COUNTS( + base::StringPrintf("Compositing.%s.NumActivePictureLayers", client_name), - base::saturated_cast<int>(total_gpu_memory_for_tilings_in_bytes / - 1024), - 1, kGPUMemoryForTilingsLargestBucketKb, - kGPUMemoryForTilingsBucketCount); + base::saturated_cast<int>(active_tree_->picture_layers().size()), 1, + 1000, 20); + + // TODO(pdr): Instead of skipping empty picture layers, maybe we should + // accumulate layer->GetRasterSource()->GetMemoryUsage() above and skip + // recording when the accumulated memory usage is 0. + if (!active_tree()->picture_layers().empty()) { + UMA_HISTOGRAM_CUSTOM_COUNTS( + base::StringPrintf("Compositing.%s.GPUMemoryForTilingsInKb", + client_name), + base::saturated_cast<int>(total_gpu_memory_for_tilings_in_bytes / + 1024), + 1, kGPUMemoryForTilingsLargestBucketKb, + kGPUMemoryForTilingsBucketCount); + } } } @@ -1601,13 +1627,8 @@ DrawResult LayerTreeHostImpl::PrepareToDraw(FrameData* frame) { DrawResult draw_result = CalculateRenderPasses(frame); - // Dump render passes and draw quads if run with: - // --vmodule=layer_tree_host_impl=3 - if (VLOG_IS_ON(3)) { - VLOG(3) << "Prepare to draw (" - << (client_name ? client_name : "<unknown client>") << ")\n" - << frame->ToString(); - } + // Dump render passes and draw quads if VerboseLogEnabled(). + VERBOSE_LOG() << "Prepare to draw\n" << frame->ToString(); if (draw_result != DRAW_SUCCESS) { DCHECK(!resourceless_software_draw_); @@ -1937,10 +1958,10 @@ size_t LayerTreeHostImpl::GetFrameIndexForImage(const PaintImage& paint_image, int LayerTreeHostImpl::GetMSAASampleCountForRaster( const scoped_refptr<DisplayItemList>& display_list) { - constexpr int kMinNumberOfSlowPathsForMSAA = 6; - if (display_list->num_slow_paths() < kMinNumberOfSlowPathsForMSAA) + if (display_list->num_slow_paths_up_to_min_for_MSAA() < + kMinNumberOfSlowPathsForMSAA) { return 0; - + } if (!can_use_msaa_) return 0; @@ -2258,6 +2279,7 @@ viz::CompositorFrameMetadata LayerTreeHostImpl::MakeCompositorFrameMetadata() { metadata.page_scale_factor = active_tree_->current_page_scale_factor(); metadata.scrollable_viewport_size = active_tree_->ScrollableViewportSize(); + metadata.root_background_color = active_tree_->background_color(); metadata.may_throttle_if_undrawn_frames = may_throttle_if_undrawn_frames_; @@ -2459,13 +2481,10 @@ absl::optional<LayerTreeHostImpl::SubmitInfo> LayerTreeHostImpl::DrawLayers( lag_tracking_manager_.CollectScrollEventsFromFrame(frame_token, events_metrics); - // Dump property trees and layers if run with: - // --vmodule=layer_tree_host_impl=3 - if (VLOG_IS_ON(3)) { - VLOG(3) << "Submitting a frame:\n" - << viz::TransitionUtils::RenderPassListToString( - compositor_frame.render_pass_list); - } + // Dump property trees and layers if VerboseLogEnabled(). + VERBOSE_LOG() << "Submitting a frame:\n" + << viz::TransitionUtils::RenderPassListToString( + compositor_frame.render_pass_list); base::TimeTicks submit_time = base::TimeTicks::Now(); { @@ -2936,11 +2955,16 @@ bool LayerTreeHostImpl::WillBeginImplFrame(const viz::BeginFrameArgs& args) { // it will push the updated viz::LocalSurfaceId. Begin Impl Frame production // if it has already become activated, or is on the |pending_tree| to be // activated during this frame's production. - const viz::LocalSurfaceId& upcoming_lsid = - pending_tree() ? pending_tree()->local_surface_id_from_parent() - : active_tree()->local_surface_id_from_parent(); - if (target_local_surface_id_.IsNewerThan(upcoming_lsid)) { - return false; + // + // However when using a synchronous compositor we skip this throttling + // completely. + if (!settings_.using_synchronous_renderer_compositor) { + const viz::LocalSurfaceId& upcoming_lsid = + pending_tree() ? pending_tree()->local_surface_id_from_parent() + : active_tree()->local_surface_id_from_parent(); + if (target_local_surface_id_.IsNewerThan(upcoming_lsid)) { + return false; + } } if (is_likely_to_require_a_draw_) { @@ -3388,19 +3412,12 @@ void LayerTreeHostImpl::ActivateSyncTree() { AllocateLocalSurfaceId(); } - // Dump property trees and layers if run with: - // --vmodule=layer_tree_host_impl=3 - if (VLOG_IS_ON(3)) { - const char* client_name = GetClientNameForMetrics(); - if (!client_name) - client_name = "<unknown client>"; - VLOG(3) << "After activating (" << client_name - << ") sync tree, the active tree:" - << "\nproperty_trees:\n" - << active_tree_->property_trees()->ToString() << "\n" - << "cc::LayerImpls:\n" - << active_tree_->LayerListAsJson(); - } + // Dump property trees and layers if VerboseLogEnabled(). + VERBOSE_LOG() << "After activating sync tree, the active tree:" + << "\nproperty_trees:\n" + << active_tree_->property_trees()->ToString() << "\n" + << "cc::LayerImpls:\n" + << active_tree_->LayerListAsJson(); } void LayerTreeHostImpl::ActivateStateForImages() { @@ -3414,7 +3431,7 @@ void LayerTreeHostImpl::OnMemoryPressure( if (!base::SysInfo::IsLowEndDevice()) return; - if (level != base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL) + if (!ImageDecodeCacheUtils::ShouldEvictCaches(level)) return; // TODO(crbug.com/1189208): Unlocking decoded-image-tracker images causes @@ -3432,14 +3449,15 @@ void LayerTreeHostImpl::OnMemoryPressure( recycle_tree_->OnPurgeMemory(); EvictAllUIResources(); - if (image_decode_cache_) { - image_decode_cache_->SetShouldAggressivelyFreeResources(true); - image_decode_cache_->SetShouldAggressivelyFreeResources(false); - } if (resource_pool_) resource_pool_->OnMemoryPressure(level); tile_manager_.decoded_image_tracker().UnlockAllImages(); + + // There is no need to notify the |image_decode_cache| about the memory + // pressure as it (the gpu one as the software one doesn't keep outstanding + // images pinned) listens to memory pressure events and purges memory base on + // the ImageDecodeCacheUtils::ShouldEvictCaches' return value. } void LayerTreeHostImpl::SetVisible(bool visible) { @@ -3552,7 +3570,7 @@ void LayerTreeHostImpl::CreateTileManagerResources() { viz::ResourceFormatToClosestSkColorType(/*gpu_compositing=*/true, tile_format), settings_.decoded_image_working_set_budget_bytes, max_texture_size_, - paint_image_generator_client_id_, dark_mode_filter_); + dark_mode_filter_); pending_raster_queries_ = std::make_unique<RasterQueryQueue>( layer_tree_frame_sink_->worker_context_provider()); @@ -3561,8 +3579,7 @@ void LayerTreeHostImpl::CreateTileManagerResources() { bool gpu_compositing = !!layer_tree_frame_sink_->context_provider(); image_decode_cache_ = std::make_unique<SoftwareImageDecodeCache>( viz::ResourceFormatToClosestSkColorType(gpu_compositing, tile_format), - settings_.decoded_image_working_set_budget_bytes, - paint_image_generator_client_id_); + settings_.decoded_image_working_set_budget_bytes); } raster_buffer_provider_ = CreateRasterBufferProvider(); @@ -4103,8 +4120,12 @@ void LayerTreeHostImpl::CollectScrollbarUpdatesForCommit( CompositorCommitData* commit_data) const { commit_data->scrollbars.reserve(scrollbar_animation_controllers_.size()); for (auto& pair : scrollbar_animation_controllers_) { - commit_data->scrollbars.push_back( - {pair.first, pair.second->ScrollbarsHidden()}); + if (pair.second->visibility_changed() || + !settings_.enable_scroll_update_optimizations) { + commit_data->scrollbars.push_back( + {pair.first, pair.second->ScrollbarsHidden()}); + pair.second->ClearVisibilityChanged(); + } } } diff --git a/chromium/cc/trees/layer_tree_host_impl.h b/chromium/cc/trees/layer_tree_host_impl.h index 1b985be80c6..c2176e110cd 100644 --- a/chromium/cc/trees/layer_tree_host_impl.h +++ b/chromium/cc/trees/layer_tree_host_impl.h @@ -20,6 +20,7 @@ #include "base/containers/lru_cache.h" #include "base/memory/memory_pressure_listener.h" #include "base/memory/shared_memory_mapping.h" +#include "base/rand_util.h" #include "base/task/sequenced_task_runner.h" #include "base/time/time.h" #include "cc/benchmarks/micro_benchmark_controller_impl.h" @@ -28,7 +29,6 @@ #include "cc/input/browser_controls_offset_manager_client.h" #include "cc/input/input_handler.h" #include "cc/input/scrollbar_animation_controller.h" -#include "cc/input/threaded_input_handler.h" #include "cc/layers/layer_collections.h" #include "cc/metrics/average_lag_tracking_manager.h" #include "cc/metrics/dropped_frame_counter.h" @@ -278,8 +278,8 @@ class CC_EXPORT LayerTreeHostImpl : public TileManagerClient, // TODO(bokan): This getter is an escape-hatch for code that hasn't yet been // cleaned up to decouple input from graphics. Callers should be cleaned up // to avoid calling it and it should be removed. - ThreadedInputHandler& GetInputHandler(); - const ThreadedInputHandler& GetInputHandler() const; + InputHandler& GetInputHandler(); + const InputHandler& GetInputHandler() const; void StartPageScaleAnimation(const gfx::Point& target_offset, bool anchor_point, @@ -350,7 +350,7 @@ class CC_EXPORT LayerTreeHostImpl : public TileManagerClient, void SetFullViewportDamage(); void SetViewportDamage(const gfx::Rect& damage_rect); - // Interface for ThreadedInputHandler + // Interface for InputHandler void BindToInputHandler( std::unique_ptr<InputDelegateForCompositor> delegate) override; ScrollTree& GetScrollTree() const override; @@ -672,7 +672,7 @@ class CC_EXPORT LayerTreeHostImpl : public TileManagerClient, // TODO(bokan): These input-related methods shouldn't be part of // LayerTreeHostImpl's interface. bool IsPinchGestureActive() const; - // See comment in equivalent ThreadedInputHandler method for what this means. + // See comment in equivalent InputHandler method for what this means. ActivelyScrollingType GetActivelyScrollingType() const; bool ScrollAffectsScrollHandler() const; bool CurrentScrollCheckerboardsDueToNoRecording() const { @@ -899,6 +899,10 @@ class CC_EXPORT LayerTreeHostImpl : public TileManagerClient, void RequestImplSideInvalidationForRerasterTiling(); + void SetDownsampleMetricsForTesting(bool value) { + downsample_metrics_ = value; + } + protected: LayerTreeHostImpl( const LayerTreeSettings& settings, @@ -930,9 +934,6 @@ class CC_EXPORT LayerTreeHostImpl : public TileManagerClient, BeginFrameTracker current_begin_frame_tracker_; - std::unique_ptr<CompositorFrameReportingController> - compositor_frame_reporting_controller_; - private: viz::CompositorFrame GenerateCompositorFrame(FrameData* frame); @@ -1127,8 +1128,12 @@ class CC_EXPORT LayerTreeHostImpl : public TileManagerClient, std::unique_ptr<PageScaleAnimation> page_scale_animation_; - DroppedFrameCounter dropped_frame_counter_; + base::WritableSharedMemoryMapping ukm_smoothness_mapping_; + TotalFrameCounter total_frame_counter_; + // `dropped_frame_counter_` holds a pointer `to ukm_smoothness_mapping_` so + // it must be declared last and deleted first; + DroppedFrameCounter dropped_frame_counter_; std::unique_ptr<MemoryHistory> memory_history_; std::unique_ptr<DebugRectHistory> debug_rect_history_; @@ -1239,8 +1244,10 @@ class CC_EXPORT LayerTreeHostImpl : public TileManagerClient, PresentationTimeCallbackBuffer presentation_time_callbacks_; - const PaintImage::GeneratorClientId paint_image_generator_client_id_; - + // `compositor_frame_reporting_controller_` has a dependency on + // `dropped_frame_counter_` so it must be declared last and deleted first; + std::unique_ptr<CompositorFrameReportingController> + compositor_frame_reporting_controller_; FrameSequenceTrackerCollection frame_trackers_; // PaintWorklet painting is controlled from the LayerTreeHostImpl, dispatched @@ -1291,8 +1298,6 @@ class CC_EXPORT LayerTreeHostImpl : public TileManagerClient, // DroppedFrameCounter. Currently true when first contentful paint is done. bool is_measuring_smoothness_ = false; - base::WritableSharedMemoryMapping ukm_smoothness_mapping_; - // Cache for the results of calls to gfx::ColorSpace::Contains() on sRGB. This // computation is deterministic for a given color space, can be called // multiple times per frame, and incurs a non-trivial cost. @@ -1305,6 +1310,9 @@ class CC_EXPORT LayerTreeHostImpl : public TileManagerClient, std::vector<uint32_t> finished_transition_request_sequence_ids_; + bool downsample_metrics_ = true; + base::MetricsSubSampler metrics_subsampler_; + // 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 3b1e8aa5ed6..213f1231961 100644 --- a/chromium/cc/trees/layer_tree_host_impl_unittest.cc +++ b/chromium/cc/trees/layer_tree_host_impl_unittest.cc @@ -28,11 +28,11 @@ #include "cc/base/histograms.h" #include "cc/document_transition/document_transition_request.h" #include "cc/input/browser_controls_offset_manager.h" +#include "cc/input/input_handler.h" #include "cc/input/main_thread_scrolling_reason.h" #include "cc/input/page_scale_animation.h" #include "cc/input/scroll_utils.h" #include "cc/input/scrollbar_controller.h" -#include "cc/input/threaded_input_handler.h" #include "cc/layers/append_quads_data.h" #include "cc/layers/layer_impl.h" #include "cc/layers/painted_overlay_scrollbar_layer_impl.h" @@ -138,6 +138,11 @@ struct TestFrameData : public LayerTreeHostImpl::FrameData { } }; +void ClearMainThreadDeltasForTesting(LayerTreeHostImpl* host) { + host->active_tree()->ApplySentScrollAndScaleDeltasFromAbortedCommit( + /*main_frame_applied_deltas=*/false); +} + } // namespace class LayerTreeHostImplTest : public testing::Test, @@ -360,6 +365,21 @@ class LayerTreeHostImplTest : public testing::Test, size, host_impl_->active_tree()->device_scale_factor()); } + void PushScrollOffsetsToPendingTree( + const base::flat_map<ElementId, gfx::PointF>& offsets) { + PropertyTrees property_trees(*host_impl_); + for (auto& entry : offsets) { + property_trees.scroll_tree_mutable().SetBaseScrollOffset(entry.first, + entry.second); + } + host_impl_->sync_tree() + ->property_trees() + ->scroll_tree_mutable() + .PushScrollUpdatesFromMainThread( + property_trees, host_impl_->sync_tree(), + host_impl_->settings().commit_fractional_scroll_deltas); + } + static void ExpectClearedScrollDeltasRecursive(LayerImpl* root) { for (auto* layer : *root->layer_tree_impl()) ASSERT_EQ(ScrollDelta(layer), gfx::Vector2d()); @@ -831,9 +851,7 @@ class LayerTreeHostImplTest : public testing::Test, } } - ThreadedInputHandler& GetInputHandler() { - return host_impl_->GetInputHandler(); - } + InputHandler& GetInputHandler() { return host_impl_->GetInputHandler(); } FakeImplTaskRunnerProvider task_runner_provider_; DebugScopedSetMainThreadBlocked always_main_thread_blocked_; @@ -1097,8 +1115,6 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollDeltaTreeButNoChanges) { TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollDeltaRepeatedScrolls) { gfx::PointF scroll_offset(20, 30); - gfx::Vector2dF scroll_delta(11, -15); - auto* root = SetupDefaultRootLayer(gfx::Size(110, 110)); root->SetHitTestable(true); CreateScrollNode(root, gfx::Size(10, 10)); @@ -1108,31 +1124,128 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollDeltaRepeatedScrolls) { .UpdateScrollOffsetBaseForTesting(root->element_id(), scroll_offset); UpdateDrawProperties(host_impl_->active_tree()); - std::unique_ptr<CompositorCommitData> commit_data; - + gfx::Vector2dF scroll_delta(11, -15); + std::unique_ptr<CompositorCommitData> commit_data1; root->ScrollBy(scroll_delta); - commit_data = host_impl_->ProcessCompositorDeltas(); - ASSERT_EQ(commit_data->scrolls.size(), 1u); + commit_data1 = host_impl_->ProcessCompositorDeltas(); + ASSERT_EQ(commit_data1->scrolls.size(), 1u); EXPECT_TRUE( - ScrollInfoContains(*commit_data, root->element_id(), scroll_delta)); + ScrollInfoContains(*commit_data1, root->element_id(), scroll_delta)); + std::unique_ptr<CompositorCommitData> commit_data2; gfx::Vector2dF scroll_delta2(-5, 27); root->ScrollBy(scroll_delta2); - commit_data = host_impl_->ProcessCompositorDeltas(); - ASSERT_EQ(commit_data->scrolls.size(), 1u); - EXPECT_TRUE(ScrollInfoContains(*commit_data, root->element_id(), - scroll_delta + scroll_delta2)); + commit_data2 = host_impl_->ProcessCompositorDeltas(); + ASSERT_EQ(commit_data2->scrolls.size(), 1u); + EXPECT_TRUE( + ScrollInfoContains(*commit_data2, root->element_id(), scroll_delta2)); + + // Simulate first commit by pushing base scroll offsets to pending tree + PushScrollOffsetsToPendingTree( + {{root->element_id(), gfx::PointAtOffsetFromOrigin(scroll_delta)}}); + EXPECT_EQ(host_impl_->sync_tree() + ->property_trees() + ->scroll_tree() + .GetScrollOffsetDeltaForTesting(root->element_id()), + scroll_delta2); + + // Simulate second commit by pushing base scroll offsets to pending tree + PushScrollOffsetsToPendingTree( + {{root->element_id(), gfx::PointAtOffsetFromOrigin(scroll_delta2)}}); + EXPECT_EQ(host_impl_->sync_tree() + ->property_trees() + ->scroll_tree() + .GetScrollOffsetDeltaForTesting(root->element_id()), + gfx::Vector2dF(0, 0)); +} + +TEST_P(ScrollUnifiedLayerTreeHostImplTest, SyncedScrollAbortedCommit) { + LayerTreeSettings settings = DefaultSettings(); + settings.commit_to_active_tree = false; + CreateHostImpl(settings, CreateLayerTreeFrameSink()); + CreatePendingTree(); + gfx::PointF scroll_offset(20, 30); + auto* root = SetupDefaultRootLayer(gfx::Size(110, 110)); + auto& scroll_tree = + root->layer_tree_impl()->property_trees()->scroll_tree_mutable(); + root->SetHitTestable(true); - root->ScrollBy(gfx::Vector2d()); - commit_data = host_impl_->ProcessCompositorDeltas(); - EXPECT_TRUE(ScrollInfoContains(*commit_data, root->element_id(), - scroll_delta + scroll_delta2)); + // SyncedProperty should be created on the pending tree and then pushed to the + // active tree, to avoid bifurcation. Simulate commit by pushing base scroll + // offsets to pending tree. + PushScrollOffsetsToPendingTree({{root->element_id(), gfx::PointF(0, 0)}}); + host_impl_->active_tree() + ->property_trees() + ->scroll_tree_mutable() + .PushScrollUpdatesFromPendingTree( + host_impl_->pending_tree()->property_trees(), + host_impl_->active_tree()); + + CreateScrollNode(root, gfx::Size(10, 10)); + auto* synced_scroll = scroll_tree.GetSyncedScrollOffset(root->element_id()); + ASSERT_TRUE(synced_scroll); + scroll_tree.UpdateScrollOffsetBaseForTesting(root->element_id(), + scroll_offset); + UpdateDrawProperties(host_impl_->active_tree()); + + gfx::Vector2dF scroll_delta(11, -15); + root->ScrollBy(scroll_delta); + EXPECT_EQ(scroll_delta, synced_scroll->UnsentDelta()); + host_impl_->ProcessCompositorDeltas(); + EXPECT_TRUE(synced_scroll->reflected_delta_in_main_tree().has_value()); + EXPECT_FALSE(synced_scroll->next_reflected_delta_in_main_tree().has_value()); + EXPECT_EQ(scroll_delta, + synced_scroll->reflected_delta_in_main_tree().value()); + + gfx::Vector2dF scroll_delta2(-5, 27); + root->ScrollBy(scroll_delta2); + EXPECT_EQ(scroll_delta2, synced_scroll->UnsentDelta()); + host_impl_->ProcessCompositorDeltas(); + EXPECT_TRUE(synced_scroll->reflected_delta_in_main_tree().has_value()); + EXPECT_TRUE(synced_scroll->next_reflected_delta_in_main_tree().has_value()); + EXPECT_EQ(scroll_delta, + synced_scroll->reflected_delta_in_main_tree().value()); + EXPECT_EQ(scroll_delta2, + synced_scroll->next_reflected_delta_in_main_tree().value()); + + // Simulate aborting the second main frame. Scroll deltas applied by the + // second frame should be combined with delta from first frame. + root->layer_tree_impl()->ApplySentScrollAndScaleDeltasFromAbortedCommit( + /*main_frame_applied_deltas=*/true); + EXPECT_TRUE(synced_scroll->reflected_delta_in_main_tree().has_value()); + EXPECT_FALSE(synced_scroll->next_reflected_delta_in_main_tree().has_value()); + EXPECT_EQ(scroll_delta + scroll_delta2, + synced_scroll->reflected_delta_in_main_tree().value()); + + // Send a third main frame, pipelined behind the first. + gfx::Vector2dF scroll_delta3(-2, -13); + root->ScrollBy(scroll_delta3); + EXPECT_EQ(scroll_delta3, synced_scroll->UnsentDelta()); + host_impl_->ProcessCompositorDeltas(); + EXPECT_TRUE(synced_scroll->reflected_delta_in_main_tree().has_value()); + EXPECT_TRUE(synced_scroll->next_reflected_delta_in_main_tree().has_value()); + EXPECT_EQ(scroll_delta + scroll_delta2, + synced_scroll->reflected_delta_in_main_tree().value()); + EXPECT_EQ(scroll_delta3, + synced_scroll->next_reflected_delta_in_main_tree().value()); + + // Simulate commit of the first frame + PushScrollOffsetsToPendingTree( + {{root->element_id(), scroll_offset + scroll_delta + scroll_delta2}}); + EXPECT_EQ(scroll_offset + scroll_delta + scroll_delta2, + synced_scroll->PendingBase()); + EXPECT_EQ(scroll_delta3, synced_scroll->PendingDelta()); + EXPECT_TRUE(synced_scroll->reflected_delta_in_main_tree().has_value()); + EXPECT_FALSE(synced_scroll->next_reflected_delta_in_main_tree().has_value()); + EXPECT_EQ(scroll_delta3, + synced_scroll->reflected_delta_in_main_tree().value()); } TEST_F(CommitToPendingTreeLayerTreeHostImplTest, GPUMemoryForSmallLayerHistogramTest) { base::HistogramTester histogram_tester; SetClientNameForMetrics("Renderer"); + host_impl_->SetDownsampleMetricsForTesting(false); // With default tile size being set to 256 * 256, the following layer needs // one tile only which costs 256 * 256 * 4 / 1024 = 256KB memory. TestGPUMemoryForTilings(gfx::Size(200, 200)); @@ -1146,6 +1259,7 @@ TEST_F(CommitToPendingTreeLayerTreeHostImplTest, GPUMemoryForLargeLayerHistogramTest) { base::HistogramTester histogram_tester; SetClientNameForMetrics("Renderer"); + host_impl_->SetDownsampleMetricsForTesting(false); // With default tile size being set to 256 * 256, the following layer needs // 4 tiles which cost 256 * 256 * 4 * 4 / 1024 = 1024KB memory. TestGPUMemoryForTilings(gfx::Size(500, 500)); @@ -3602,6 +3716,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ImplPinchZoom) { EXPECT_EQ(commit_data->page_scale_delta, page_scale_delta); EXPECT_EQ(gfx::PointF(75.0, 75.0), MaxScrollOffset(scroll_layer)); + ClearMainThreadDeltasForTesting(host_impl_.get()); } // Scrolling after a pinch gesture should always be in local space. The @@ -4193,6 +4308,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, PinchGesture) { std::unique_ptr<CompositorCommitData> commit_data = host_impl_->ProcessCompositorDeltas(); EXPECT_EQ(commit_data->page_scale_delta, page_scale_delta); + ClearMainThreadDeltasForTesting(host_impl_.get()); } // Zoom-in clamping @@ -4216,6 +4332,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, PinchGesture) { std::unique_ptr<CompositorCommitData> commit_data = host_impl_->ProcessCompositorDeltas(); EXPECT_EQ(commit_data->page_scale_delta, max_page_scale); + ClearMainThreadDeltasForTesting(host_impl_.get()); } // Zoom-out clamping @@ -4227,6 +4344,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, PinchGesture) { ->property_trees() ->scroll_tree_mutable() .CollectScrollDeltasForTesting(); + ClearMainThreadDeltasForTesting(host_impl_.get()); scroll_layer->layer_tree_impl() ->property_trees() ->scroll_tree_mutable() @@ -4249,6 +4367,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, PinchGesture) { EXPECT_EQ(commit_data->page_scale_delta, min_page_scale); EXPECT_TRUE(commit_data->scrolls.empty()); + ClearMainThreadDeltasForTesting(host_impl_.get()); } // Two-finger panning should not happen based on pinch events only @@ -4260,6 +4379,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, PinchGesture) { ->property_trees() ->scroll_tree_mutable() .CollectScrollDeltasForTesting(); + ClearMainThreadDeltasForTesting(host_impl_.get()); scroll_layer->layer_tree_impl() ->property_trees() ->scroll_tree_mutable() @@ -4283,6 +4403,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, PinchGesture) { host_impl_->ProcessCompositorDeltas(); EXPECT_EQ(commit_data->page_scale_delta, page_scale_delta); EXPECT_TRUE(commit_data->scrolls.empty()); + ClearMainThreadDeltasForTesting(host_impl_.get()); } // Two-finger panning should work with interleaved scroll events @@ -4294,6 +4415,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, PinchGesture) { ->property_trees() ->scroll_tree_mutable() .CollectScrollDeltasForTesting(); + ClearMainThreadDeltasForTesting(host_impl_.get()); scroll_layer->layer_tree_impl() ->property_trees() ->scroll_tree_mutable() @@ -4322,6 +4444,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, PinchGesture) { EXPECT_EQ(commit_data->page_scale_delta, page_scale_delta); EXPECT_TRUE(ScrollInfoContains(*commit_data, scroll_layer->element_id(), gfx::Vector2dF(-10, -10))); + ClearMainThreadDeltasForTesting(host_impl_.get()); } // Two-finger panning should work when starting fully zoomed out. @@ -4332,6 +4455,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, PinchGesture) { ->property_trees() ->scroll_tree_mutable() .CollectScrollDeltasForTesting(); + ClearMainThreadDeltasForTesting(host_impl_.get()); scroll_layer->layer_tree_impl() ->property_trees() ->scroll_tree_mutable() @@ -4383,6 +4507,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, SyncSubpixelScrollDelta) { ->property_trees() ->scroll_tree_mutable() .CollectScrollDeltasForTesting(); + ClearMainThreadDeltasForTesting(host_impl_.get()); scroll_layer->layer_tree_impl() ->property_trees() ->scroll_tree_mutable() @@ -4435,6 +4560,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ->property_trees() ->scroll_tree_mutable() .CollectScrollDeltasForTesting(); + ClearMainThreadDeltasForTesting(host_impl_.get()); scroll_layer->layer_tree_impl() ->property_trees() ->scroll_tree_mutable() @@ -4530,6 +4656,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, std::unique_ptr<CompositorCommitData> commit_data = host_impl_->ProcessCompositorDeltas(); EXPECT_EQ(commit_data->page_scale_delta, 1); + ClearMainThreadDeltasForTesting(host_impl_.get()); } start_time += base::Seconds(10); @@ -4661,6 +4788,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, PageScaleAnimation) { EXPECT_EQ(commit_data->page_scale_delta, 2); EXPECT_TRUE(ScrollInfoContains(*commit_data, scroll_layer->element_id(), gfx::Vector2dF(-50, -50))); + ClearMainThreadDeltasForTesting(host_impl_.get()); } start_time += base::Seconds(10); @@ -5657,6 +5785,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, settings.scrollbar_animator = LayerTreeSettings::AURA_OVERLAY; settings.scrollbar_fade_delay = base::Milliseconds(20); settings.scrollbar_fade_duration = base::Milliseconds(20); + settings.enable_scroll_update_optimizations = true; gfx::Size viewport_size(50, 50); gfx::Size content_size(100, 100); @@ -5680,6 +5809,14 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, // Scrollbars will flash shown but we should have a fade out animation // queued. Run it and fade out the scrollbars. + ASSERT_FALSE(scrollbar_controller->ScrollbarsHidden()); + EXPECT_TRUE(scrollbar_controller->visibility_changed()); + ClearMainThreadDeltasForTesting(host_impl_.get()); + auto commit_data = host_impl_->ProcessCompositorDeltas(); + using ScrollbarsInfo = CompositorCommitData::ScrollbarsUpdateInfo; + EXPECT_THAT(commit_data->scrollbars, testing::ElementsAre(ScrollbarsInfo{ + scroll->element_id(), false})); + EXPECT_FALSE(scrollbar_controller->visibility_changed()); { ASSERT_FALSE(animation_task_.is_null()); ASSERT_FALSE(animation_task_.IsCancelled()); @@ -5691,6 +5828,11 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, scrollbar_controller->Animate(fake_now); ASSERT_TRUE(scrollbar_controller->ScrollbarsHidden()); + ClearMainThreadDeltasForTesting(host_impl_.get()); + commit_data = host_impl_->ProcessCompositorDeltas(); + EXPECT_THAT(commit_data->scrollbars, testing::ElementsAre(ScrollbarsInfo{ + scroll->element_id(), true})); + EXPECT_FALSE(scrollbar_controller->visibility_changed()); } // Move the mouse over the scrollbar region. This should post a delayed fade @@ -5708,6 +5850,10 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, did_request_redraw_ = false; did_request_commit_ = false; ASSERT_TRUE(scrollbar_controller->ScrollbarsHidden()); + EXPECT_FALSE(scrollbar_controller->visibility_changed()); + ClearMainThreadDeltasForTesting(host_impl_.get()); + commit_data = host_impl_->ProcessCompositorDeltas(); + EXPECT_TRUE(commit_data->scrollbars.empty()); std::move(animation_task_).Run(); base::TimeTicks fake_now = base::TimeTicks::Now(); @@ -5716,6 +5862,12 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, scrollbar_controller->Animate(fake_now); ASSERT_FALSE(scrollbar_controller->ScrollbarsHidden()); + EXPECT_TRUE(scrollbar_controller->visibility_changed()); + ClearMainThreadDeltasForTesting(host_impl_.get()); + commit_data = host_impl_->ProcessCompositorDeltas(); + EXPECT_THAT(commit_data->scrollbars, testing::ElementsAre(ScrollbarsInfo{ + scroll->element_id(), false})); + EXPECT_FALSE(scrollbar_controller->visibility_changed()); EXPECT_TRUE(did_request_redraw_); EXPECT_TRUE(did_request_commit_); } @@ -5869,8 +6021,9 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollBeforeMouseMove) { const float kDistanceToTriggerThumb = vert_scrollbar->ComputeThumbQuadRect().height() + - SingleScrollbarAnimationControllerThinning:: - kMouseMoveDistanceToTriggerExpand; + scrollbar_controller + ->GetScrollbarAnimationController(ScrollbarOrientation::VERTICAL) + .MouseMoveDistanceToTriggerExpand(); // Move the mouse near the thumb while its at the viewport top. auto near_thumb_at_top = gfx::Point(295, kDistanceToTriggerThumb - 1); @@ -5924,9 +6077,10 @@ void LayerTreeHostImplTest::SetupMouseMoveAtWithDeviceScale( settings.scrollbar_fade_duration = base::Milliseconds(300); settings.scrollbar_animator = LayerTreeSettings::AURA_OVERLAY; + const int thumb_thickness = 15; gfx::Size viewport_size(300, 200); gfx::Size content_size(1000, 1000); - gfx::Size scrollbar_size(gfx::Size(15, viewport_size.height())); + gfx::Size scrollbar_size(gfx::Size(thumb_thickness, viewport_size.height())); CreateHostImpl(settings, CreateLayerTreeFrameSink()); host_impl_->active_tree()->SetDeviceScaleFactor(device_scale_factor); @@ -5934,7 +6088,8 @@ void LayerTreeHostImplTest::SetupMouseMoveAtWithDeviceScale( LayerImpl* root_scroll = OuterViewportScrollLayer(); // The scrollbar is on the left side. auto* scrollbar = AddLayer<SolidColorScrollbarLayerImpl>( - host_impl_->active_tree(), ScrollbarOrientation::VERTICAL, 15, 0, true); + host_impl_->active_tree(), ScrollbarOrientation::VERTICAL, + thumb_thickness, 0, true); SetupScrollbarLayer(root_scroll, scrollbar); scrollbar->SetBounds(scrollbar_size); TouchActionRegion touch_action_region; @@ -5949,28 +6104,31 @@ void LayerTreeHostImplTest::SetupMouseMoveAtWithDeviceScale( root_scroll->element_id()); const float kMouseMoveDistanceToTriggerFadeIn = - ScrollbarAnimationController::kMouseMoveDistanceToTriggerFadeIn; + scrollbar_animation_controller + ->GetScrollbarAnimationController(ScrollbarOrientation::VERTICAL) + .MouseMoveDistanceToTriggerFadeIn(); const float kMouseMoveDistanceToTriggerExpand = - SingleScrollbarAnimationControllerThinning:: - kMouseMoveDistanceToTriggerExpand; + scrollbar_animation_controller + ->GetScrollbarAnimationController(ScrollbarOrientation::VERTICAL) + .MouseMoveDistanceToTriggerExpand(); GetInputHandler().MouseMoveAt( - gfx::Point(15 + kMouseMoveDistanceToTriggerFadeIn, 1)); + gfx::Point(thumb_thickness + kMouseMoveDistanceToTriggerFadeIn + 1, 1)); EXPECT_FALSE(scrollbar_animation_controller->MouseIsNearScrollbar( ScrollbarOrientation::VERTICAL)); EXPECT_FALSE(scrollbar_animation_controller->MouseIsNearScrollbarThumb( ScrollbarOrientation::VERTICAL)); GetInputHandler().MouseMoveAt( - gfx::Point(15 + kMouseMoveDistanceToTriggerExpand - 1, 10)); + gfx::Point(thumb_thickness + kMouseMoveDistanceToTriggerExpand, 10)); EXPECT_TRUE(scrollbar_animation_controller->MouseIsNearScrollbar( ScrollbarOrientation::VERTICAL)); EXPECT_TRUE(scrollbar_animation_controller->MouseIsNearScrollbarThumb( ScrollbarOrientation::VERTICAL)); GetInputHandler().MouseMoveAt( - gfx::Point(15 + kMouseMoveDistanceToTriggerFadeIn, 100)); + gfx::Point(thumb_thickness + kMouseMoveDistanceToTriggerFadeIn + 1, 100)); EXPECT_FALSE(scrollbar_animation_controller->MouseIsNearScrollbar( ScrollbarOrientation::VERTICAL)); EXPECT_FALSE(scrollbar_animation_controller->MouseIsNearScrollbarThumb( @@ -6283,7 +6441,8 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, // The background is default to transparent. If the background is opaque, we // would fill the frame with background colour when no layers are contributing // quads. This means we would end up with 0 quad. - EXPECT_EQ(host_impl_->active_tree()->background_color(), SK_ColorTRANSPARENT); + EXPECT_EQ(host_impl_->active_tree()->background_color(), + SkColors::kTransparent); { TestFrameData frame; @@ -8417,16 +8576,19 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollWithoutBubbling) { UpdateDrawProperties(host_impl_->active_tree()); host_impl_->active_tree()->DidBecomeActive(); + gfx::PointF grand_child_base(0, 2); + gfx::Vector2dF grand_child_delta; grand_child_layer->layer_tree_impl() ->property_trees() ->scroll_tree_mutable() .UpdateScrollOffsetBaseForTesting(grand_child_layer->element_id(), - gfx::PointF(0, 2)); + grand_child_base); + gfx::PointF child_base(0, 3); + gfx::Vector2dF child_delta; child_layer->layer_tree_impl() ->property_trees() ->scroll_tree_mutable() - .UpdateScrollOffsetBaseForTesting(child_layer->element_id(), - gfx::PointF(0, 3)); + .UpdateScrollOffsetBaseForTesting(child_layer->element_id(), child_base); DrawFrame(); { @@ -8448,13 +8610,20 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollWithoutBubbling) { host_impl_->ProcessCompositorDeltas(); // The grand child should have scrolled up to its limit. + grand_child_delta = gfx::Vector2dF(0, -2); EXPECT_TRUE(ScrollInfoContains(*commit_data.get(), grand_child_layer->element_id(), - gfx::Vector2dF(0, -2))); + grand_child_delta)); // The child should not have scrolled. ExpectNone(*commit_data.get(), child_layer->element_id()); + grand_child_base += grand_child_delta; + child_base += child_delta; + PushScrollOffsetsToPendingTree( + {{child_layer->element_id(), child_base}, + {grand_child_layer->element_id(), grand_child_base}}); + // The next time we scroll we should only scroll the parent. scroll_delta = gfx::Vector2d(0, -3); EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, @@ -8474,16 +8643,23 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollWithoutBubbling) { child_layer->scroll_tree_index()); GetInputHandler().ScrollEnd(); + ClearMainThreadDeltasForTesting(host_impl_.get()); commit_data = host_impl_->ProcessCompositorDeltas(); // The child should have scrolled up to its limit. - EXPECT_TRUE(ScrollInfoContains( - *commit_data.get(), child_layer->element_id(), gfx::Vector2dF(0, -3))); + child_delta = gfx::Vector2dF(0, -3); + EXPECT_TRUE(ScrollInfoContains(*commit_data.get(), + child_layer->element_id(), child_delta)); // The grand child should not have scrolled. - EXPECT_TRUE(ScrollInfoContains(*commit_data.get(), - grand_child_layer->element_id(), - gfx::Vector2dF(0, -2))); + grand_child_delta = gfx::Vector2dF(); + ExpectNone(*commit_data.get(), grand_child_layer->element_id()); + + child_base += child_delta; + grand_child_base += grand_child_delta; + PushScrollOffsetsToPendingTree( + {{grand_child_layer->element_id(), grand_child_base}, + {child_layer->element_id(), child_base}}); // After scrolling the parent, another scroll on the opposite direction // should still scroll the child. @@ -8505,16 +8681,24 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollWithoutBubbling) { grand_child_layer->scroll_tree_index()); GetInputHandler().ScrollEnd(); + ClearMainThreadDeltasForTesting(host_impl_.get()); commit_data = host_impl_->ProcessCompositorDeltas(); // The grand child should have scrolled. + grand_child_delta = gfx::Vector2dF(0, 7); EXPECT_TRUE(ScrollInfoContains(*commit_data.get(), grand_child_layer->element_id(), - gfx::Vector2dF(0, 5))); + grand_child_delta)); // The child should not have scrolled. - EXPECT_TRUE(ScrollInfoContains( - *commit_data.get(), child_layer->element_id(), gfx::Vector2dF(0, -3))); + child_delta = gfx::Vector2dF(); + ExpectNone(*commit_data.get(), child_layer->element_id()); + + grand_child_base += grand_child_delta; + child_base += child_delta; + PushScrollOffsetsToPendingTree( + {{grand_child_layer->element_id(), grand_child_base}, + {child_layer->element_id(), child_base}}); // Scrolling should be adjusted from viewport space. host_impl_->active_tree()->PushPageScaleFromMainThread(2, 2, 2); @@ -8536,12 +8720,13 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollWithoutBubbling) { .get()); GetInputHandler().ScrollEnd(); + ClearMainThreadDeltasForTesting(host_impl_.get()); commit_data = host_impl_->ProcessCompositorDeltas(); - // Should have scrolled by half the amount in layer space (5 - 2/2) + // Should have scrolled by half the amount in layer space (-2/2) EXPECT_TRUE(ScrollInfoContains(*commit_data.get(), grand_child_layer->element_id(), - gfx::Vector2dF(0, 4))); + gfx::Vector2dF(0, -1))); } } @@ -8684,6 +8869,10 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollAxisAlignedRotatedLayer) { EXPECT_TRUE(ScrollInfoContains(*commit_data.get(), scroll_layer->element_id(), gfx::Vector2dF(0, gesture_scroll_delta.x()))); + // Push scrolls to pending tree + PushScrollOffsetsToPendingTree( + {{scroll_layer->element_id(), gfx::PointF(10, 0)}}); + // Reset and scroll down with the wheel. SetScrollOffsetDelta(scroll_layer, gfx::Vector2dF()); gfx::Vector2dF wheel_scroll_delta(0, 10); @@ -8766,6 +8955,10 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollNonAxisAlignedRotatedLayer) { // The root scroll layer should not have scrolled, because the input delta // was close to the layer's axis of movement. EXPECT_EQ(commit_data->scrolls.size(), 1u); + + PushScrollOffsetsToPendingTree( + {{child_scroll_id, + gfx::PointAtOffsetFromOrigin(expected_scroll_delta)}}); } { // Now reset and scroll the same amount horizontally. @@ -8797,6 +8990,10 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollNonAxisAlignedRotatedLayer) { // The root scroll layer shouldn't have scrolled. ExpectNone(*commit_data.get(), scroll_layer->element_id()); + + PushScrollOffsetsToPendingTree( + {{child_scroll_id, + gfx::PointAtOffsetFromOrigin(expected_scroll_delta)}}); } } @@ -8876,6 +9073,11 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollPerspectiveTransformedLayer) { // The root scroll layer should not have scrolled, because the input delta // was close to the layer's axis of movement. EXPECT_EQ(commit_data->scrolls.size(), 1u); + + PushScrollOffsetsToPendingTree( + {{child->element_id(), + gfx::PointAtOffsetFromOrigin(expected_scroll_deltas[i])}}); + ClearMainThreadDeltasForTesting(host_impl_.get()); } } @@ -8911,6 +9113,9 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollScaledLayer) { host_impl_->ProcessCompositorDeltas(); EXPECT_TRUE(ScrollInfoContains(*commit_data.get(), scroll_layer->element_id(), gfx::Vector2dF(0, scroll_delta.y() / scale))); + PushScrollOffsetsToPendingTree( + {{scroll_layer->element_id(), gfx::PointAtOffsetFromOrigin(gfx::Vector2dF( + 0, scroll_delta.y() / scale))}}); // Reset and scroll down with the wheel. SetScrollOffsetDelta(scroll_layer, gfx::Vector2dF()); @@ -10726,7 +10931,7 @@ class LayerTreeHostImplViewportCoveredTest : public LayerTreeHostImplTest { } void SetupActiveTreeLayers() { - host_impl_->active_tree()->set_background_color(SK_ColorGRAY); + host_impl_->active_tree()->set_background_color(SkColors::kGray); LayerImpl* root = SetupDefaultRootLayer(viewport_size_); child_ = AddLayer<BlendStateCheckLayer>(host_impl_->active_tree(), host_impl_->resource_provider()); @@ -11098,7 +11303,7 @@ class FakeLayerWithQuads : public LayerImpl { render_pass->CreateAndAppendSharedQuadState(); PopulateSharedQuadState(shared_quad_state, contents_opaque()); - SkColor gray = SkColorSetRGB(100, 100, 100); + SkColor4f gray = SkColors::kGray; gfx::Rect quad_rect(bounds()); gfx::Rect visible_quad_rect(quad_rect); auto* my_quad = @@ -11148,7 +11353,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, LayersFreeTextures) { TEST_P(ScrollUnifiedLayerTreeHostImplTest, HasTransparentBackground) { SetupDefaultRootLayer(gfx::Size(10, 10)); - host_impl_->active_tree()->set_background_color(SK_ColorWHITE); + host_impl_->active_tree()->set_background_color(SkColors::kWhite); UpdateDrawProperties(host_impl_->active_tree()); // Verify one quad is drawn when transparent background set is not set. @@ -11172,7 +11377,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, HasTransparentBackground) { host_impl_->SetFullViewportDamage(); // Verify no quads are drawn when transparent background is set. - host_impl_->active_tree()->set_background_color(SK_ColorTRANSPARENT); + host_impl_->active_tree()->set_background_color(SkColors::kTransparent); host_impl_->SetFullViewportDamage(); args = viz::CreateBeginFrameArgsForTesting( BEGINFRAME_FROM_HERE, viz::BeginFrameArgs::kManualSourceId, 1, @@ -11191,7 +11396,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, HasTransparentBackground) { host_impl_->SetFullViewportDamage(); // Verify no quads are drawn when semi-transparent background is set. - host_impl_->active_tree()->set_background_color(SkColorSetARGB(5, 255, 0, 0)); + host_impl_->active_tree()->set_background_color({1.0f, 0.0f, 0.0f, 0.1f}); host_impl_->SetFullViewportDamage(); host_impl_->WillBeginImplFrame(viz::CreateBeginFrameArgsForTesting( BEGINFRAME_FROM_HERE, viz::BeginFrameArgs::kManualSourceId, 1, @@ -14812,7 +15017,13 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, // Verify no jump. float y = CurrentScrollOffset(scrolling_layer).y(); - EXPECT_TRUE(y > 1 && y < 49); + if (features::IsImpulseScrollAnimationEnabled()) { + // Impulse scroll animation is faster than non-impulse, which results in a + // traveled distance larger than the original 50px. + EXPECT_TRUE(y > 50 && y < 100); + } else { + EXPECT_TRUE(y > 1 && y < 49); + } } TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollAnimatedWithDelay) { @@ -15914,11 +16125,14 @@ void LayerTreeHostImplTest::SetupMouseMoveAtTestScrollbarStates( settings.scrollbar_fade_duration = base::Milliseconds(300); settings.scrollbar_animator = LayerTreeSettings::AURA_OVERLAY; + const int thumb_thickness = 15; gfx::Size viewport_size(300, 200); gfx::Size content_size(1000, 1000); gfx::Size child_layer_size(250, 150); - gfx::Size scrollbar_size_1(gfx::Size(15, viewport_size.height())); - gfx::Size scrollbar_size_2(gfx::Size(15, child_layer_size.height())); + gfx::Size scrollbar_size_1( + gfx::Size(thumb_thickness, viewport_size.height())); + gfx::Size scrollbar_size_2( + gfx::Size(thumb_thickness, child_layer_size.height())); CreateHostImpl(settings, CreateLayerTreeFrameSink()); host_impl_->active_tree()->SetDeviceScaleFactor(1); @@ -15932,7 +16146,8 @@ void LayerTreeHostImplTest::SetupMouseMoveAtTestScrollbarStates( // scrollbar_1 on root scroll. auto* scrollbar_1 = AddLayer<SolidColorScrollbarLayerImpl>( - host_impl_->active_tree(), ScrollbarOrientation::VERTICAL, 15, 0, true); + host_impl_->active_tree(), ScrollbarOrientation::VERTICAL, + thumb_thickness, 0, true); SetupScrollbarLayer(root_scroll, scrollbar_1); scrollbar_1->SetBounds(scrollbar_size_1); TouchActionRegion touch_action_region; @@ -15950,16 +16165,19 @@ void LayerTreeHostImplTest::SetupMouseMoveAtTestScrollbarStates( EXPECT_TRUE(scrollbar_1_animation_controller); const float kMouseMoveDistanceToTriggerFadeIn = - ScrollbarAnimationController::kMouseMoveDistanceToTriggerFadeIn; + scrollbar_1_animation_controller + ->GetScrollbarAnimationController(ScrollbarOrientation::VERTICAL) + .MouseMoveDistanceToTriggerFadeIn(); const float kMouseMoveDistanceToTriggerExpand = - SingleScrollbarAnimationControllerThinning:: - kMouseMoveDistanceToTriggerExpand; + scrollbar_1_animation_controller + ->GetScrollbarAnimationController(ScrollbarOrientation::VERTICAL) + .MouseMoveDistanceToTriggerExpand(); // Mouse moves close to the scrollbar, goes over the scrollbar, and // moves back to where it was. GetInputHandler().MouseMoveAt( - gfx::Point(15 + kMouseMoveDistanceToTriggerFadeIn, 0)); + gfx::Point(thumb_thickness + kMouseMoveDistanceToTriggerFadeIn + 1, 0)); EXPECT_FALSE(scrollbar_1_animation_controller->MouseIsNearScrollbar( ScrollbarOrientation::VERTICAL)); EXPECT_FALSE(scrollbar_1_animation_controller->MouseIsNearScrollbarThumb( @@ -15968,7 +16186,7 @@ void LayerTreeHostImplTest::SetupMouseMoveAtTestScrollbarStates( ScrollbarOrientation::VERTICAL)); GetInputHandler().MouseMoveAt( - gfx::Point(15 + kMouseMoveDistanceToTriggerExpand, 0)); + gfx::Point(thumb_thickness + kMouseMoveDistanceToTriggerExpand + 1, 0)); EXPECT_TRUE(scrollbar_1_animation_controller->MouseIsNearScrollbar( ScrollbarOrientation::VERTICAL)); EXPECT_FALSE(scrollbar_1_animation_controller->MouseIsNearScrollbarThumb( @@ -15977,7 +16195,7 @@ void LayerTreeHostImplTest::SetupMouseMoveAtTestScrollbarStates( ScrollbarOrientation::VERTICAL)); GetInputHandler().MouseMoveAt( - gfx::Point(14 + kMouseMoveDistanceToTriggerExpand, 0)); + gfx::Point(thumb_thickness + kMouseMoveDistanceToTriggerExpand, 0)); EXPECT_TRUE(scrollbar_1_animation_controller->MouseIsNearScrollbar( ScrollbarOrientation::VERTICAL)); EXPECT_TRUE(scrollbar_1_animation_controller->MouseIsNearScrollbarThumb( @@ -15994,7 +16212,7 @@ void LayerTreeHostImplTest::SetupMouseMoveAtTestScrollbarStates( ScrollbarOrientation::VERTICAL)); GetInputHandler().MouseMoveAt( - gfx::Point(14 + kMouseMoveDistanceToTriggerExpand, 0)); + gfx::Point(thumb_thickness + kMouseMoveDistanceToTriggerExpand, 0)); EXPECT_TRUE(scrollbar_1_animation_controller->MouseIsNearScrollbar( ScrollbarOrientation::VERTICAL)); EXPECT_TRUE(scrollbar_1_animation_controller->MouseIsNearScrollbarThumb( @@ -16003,7 +16221,7 @@ void LayerTreeHostImplTest::SetupMouseMoveAtTestScrollbarStates( ScrollbarOrientation::VERTICAL)); GetInputHandler().MouseMoveAt( - gfx::Point(15 + kMouseMoveDistanceToTriggerExpand, 0)); + gfx::Point(thumb_thickness + kMouseMoveDistanceToTriggerExpand + 1, 0)); EXPECT_TRUE(scrollbar_1_animation_controller->MouseIsNearScrollbar( ScrollbarOrientation::VERTICAL)); EXPECT_FALSE(scrollbar_1_animation_controller->MouseIsNearScrollbarThumb( @@ -16012,7 +16230,7 @@ void LayerTreeHostImplTest::SetupMouseMoveAtTestScrollbarStates( ScrollbarOrientation::VERTICAL)); GetInputHandler().MouseMoveAt( - gfx::Point(15 + kMouseMoveDistanceToTriggerFadeIn, 0)); + gfx::Point(thumb_thickness + kMouseMoveDistanceToTriggerFadeIn + 1, 0)); EXPECT_FALSE(scrollbar_1_animation_controller->MouseIsNearScrollbar( ScrollbarOrientation::VERTICAL)); EXPECT_FALSE(scrollbar_1_animation_controller->MouseIsNearScrollbarThumb( @@ -16022,7 +16240,8 @@ void LayerTreeHostImplTest::SetupMouseMoveAtTestScrollbarStates( // scrollbar_2 on child. auto* scrollbar_2 = AddLayer<SolidColorScrollbarLayerImpl>( - host_impl_->active_tree(), ScrollbarOrientation::VERTICAL, 15, 0, true); + host_impl_->active_tree(), ScrollbarOrientation::VERTICAL, + thumb_thickness, 0, true); LayerImpl* child = AddScrollableLayer(root_scroll, gfx::Size(100, 100), child_layer_size); child->SetOffsetToTransformParent(gfx::Vector2dF(50, 50)); @@ -16060,7 +16279,7 @@ void LayerTreeHostImplTest::SetupMouseMoveAtTestScrollbarStates( ScrollbarOrientation::VERTICAL)); GetInputHandler().MouseMoveAt( - gfx::Point(64 + kMouseMoveDistanceToTriggerExpand, 50)); + gfx::Point(50 + thumb_thickness + kMouseMoveDistanceToTriggerExpand, 50)); EXPECT_FALSE(scrollbar_1_animation_controller->MouseIsNearScrollbar( ScrollbarOrientation::VERTICAL)); EXPECT_FALSE(scrollbar_1_animation_controller->MouseIsNearScrollbarThumb( @@ -16074,7 +16293,7 @@ void LayerTreeHostImplTest::SetupMouseMoveAtTestScrollbarStates( EXPECT_FALSE(scrollbar_2_animation_controller->MouseIsOverScrollbarThumb( ScrollbarOrientation::VERTICAL)); GetInputHandler().MouseMoveAt( - gfx::Point(14 + kMouseMoveDistanceToTriggerExpand, 0)); + gfx::Point(thumb_thickness + kMouseMoveDistanceToTriggerExpand, 0)); EXPECT_TRUE(scrollbar_1_animation_controller->MouseIsNearScrollbar( ScrollbarOrientation::VERTICAL)); EXPECT_TRUE(scrollbar_1_animation_controller->MouseIsNearScrollbarThumb( diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_filters.cc b/chromium/cc/trees/layer_tree_host_pixeltest_filters.cc index e4b55f0fee0..4bd1a342b88 100644 --- a/chromium/cc/trees/layer_tree_host_pixeltest_filters.cc +++ b/chromium/cc/trees/layer_tree_host_pixeltest_filters.cc @@ -898,7 +898,7 @@ TEST_P(LayerTreeHostFiltersPixelTest, RotatedDropShadowFilter) { child->SetTransform(transform); FilterOperations filters; filters.Append(FilterOperation::CreateDropShadowFilter( - gfx::Point(10.0f, 10.0f), 0.0f, SK_ColorBLACK)); + gfx::Point(10.0f, 10.0f), 0.0f, SkColors::kBlack)); child->SetFilters(filters); background->AddChild(child); diff --git a/chromium/cc/trees/layer_tree_host_unittest.cc b/chromium/cc/trees/layer_tree_host_unittest.cc index c66f51e04c6..fca6e54e486 100644 --- a/chromium/cc/trees/layer_tree_host_unittest.cc +++ b/chromium/cc/trees/layer_tree_host_unittest.cc @@ -389,7 +389,7 @@ class LayerTreeHostTestReadyToActivateEmpty : public LayerTreeHostTest { size_t required_for_activation_count_; }; -SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestReadyToActivateEmpty); +MULTI_THREAD_TEST_F(LayerTreeHostTestReadyToActivateEmpty); // Test if the LTHI receives ReadyToActivate notifications from the TileManager // when some raster tasks flagged as REQUIRED_FOR_ACTIVATION got scheduled. @@ -461,7 +461,7 @@ class LayerTreeHostTestReadyToDrawEmpty : public LayerTreeHostTest { size_t required_for_draw_count_; }; -SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestReadyToDrawEmpty); +SINGLE_THREAD_TEST_F(LayerTreeHostTestReadyToDrawEmpty); // Test if the LTHI receives ReadyToDraw notifications from the TileManager when // some raster tasks flagged as REQUIRED_FOR_DRAW got scheduled. @@ -544,14 +544,28 @@ class LayerTreeHostTestReadyToDrawVisibility : public LayerTreeHostTest { void DidFinishImplFrameOnThread(LayerTreeHostImpl* host_impl) override { if (!host_impl->visible()) { - { - DebugScopedSetMainThread main(task_runner_provider()); - layer_tree_host()->SetVisible(true); - } - EXPECT_TRUE(host_impl->visible()); + // DidFinishImplFrameOnThread is called from Scheduler::FinishImplFrame() + // with inside_scheduled_action_ being true, so if SetVisible is called + // directly here without PostTask, ProcessScheduledActions() triggered by + // SetVisible() is ignored, so no more action will be executed. + // Therefore, this TC will be succeed only if there is another events like + // - RasterTaskImpl's OnTaskCompleted : this is flaky depending on tile + // task threads when SetVisible(false) is called. + // - NotifyReadytoActivate : this is not called in single threaded + // To remove flaky, PostTask is required here. + ImplThreadTaskRunner()->PostTask( + FROM_HERE, + base::BindOnce(&LayerTreeHostTestReadyToDrawVisibility::SetVisible, + base::Unretained(this), host_impl)); } } + void SetVisible(LayerTreeHostImpl* host_impl) { + DebugScopedSetMainThread main(task_runner_provider()); + layer_tree_host()->SetVisible(true); + EXPECT_TRUE(host_impl->visible()); + } + void AfterTest() override { EXPECT_TRUE(did_notify_ready_to_draw_); EXPECT_TRUE(did_draw_); @@ -1573,7 +1587,8 @@ class LayerTreeHostTestNoDamageCausesNoInvalidate : public LayerTreeHostTest { // This behavior is specific to Android WebView, which only uses // multi-threaded compositor. -MULTI_THREAD_TEST_F(LayerTreeHostTestNoDamageCausesNoInvalidate); +// TODO(https://crbug.com/1345000): Flaky. +// MULTI_THREAD_TEST_F(LayerTreeHostTestNoDamageCausesNoInvalidate); // When settings->enable_early_damage_check is true, verify that the early // damage check is turned off after |settings->damaged_frame_limit| frames @@ -3194,7 +3209,7 @@ class LayerTreeHostTestCommit : public LayerTreeHostTest { void BeginTest() override { layer_tree_host()->SetViewportRectAndScale(gfx::Rect(20, 20), 1.f, viz::LocalSurfaceId()); - layer_tree_host()->set_background_color(SK_ColorGRAY); + layer_tree_host()->set_background_color(SkColors::kGray); layer_tree_host()->SetEventListenerProperties( EventListenerClass::kMouseWheel, EventListenerProperties::kPassive); layer_tree_host()->SetEventListenerProperties( @@ -3210,7 +3225,7 @@ class LayerTreeHostTestCommit : public LayerTreeHostTest { void DidActivateTreeOnThread(LayerTreeHostImpl* impl) override { EXPECT_EQ(gfx::Rect(20, 20), impl->active_tree()->GetDeviceViewport()); - EXPECT_EQ(SK_ColorGRAY, impl->active_tree()->background_color()); + EXPECT_EQ(SkColors::kGray, impl->active_tree()->background_color()); EXPECT_EQ(EventListenerProperties::kPassive, impl->active_tree()->event_listener_properties( EventListenerClass::kMouseWheel)); @@ -3240,7 +3255,7 @@ class LayerTreeHostTestFrameTimeUpdatesAfterActivationFails void BeginTest() override { layer_tree_host()->SetViewportRectAndScale(gfx::Rect(20, 20), 1.f, viz::LocalSurfaceId()); - layer_tree_host()->set_background_color(SK_ColorGRAY); + layer_tree_host()->set_background_color(SkColors::kGray); PostSetNeedsCommitToMainThread(); } @@ -3293,7 +3308,7 @@ class LayerTreeHostTestFrameTimeUpdatesAfterDraw : public LayerTreeHostTest { void BeginTest() override { layer_tree_host()->SetViewportRectAndScale(gfx::Rect(20, 20), 1.f, viz::LocalSurfaceId()); - layer_tree_host()->set_background_color(SK_ColorGRAY); + layer_tree_host()->set_background_color(SkColors::kGray); PostSetNeedsCommitToMainThread(); } @@ -7605,31 +7620,12 @@ class LayerTreeHostTestContinuousDrawWhenCreatingVisibleTiles } break; case 4: - ++step_; - break; - case 5: - // Waiting for NotifyReadyToDraw. - break; - case 6: - // NotifyReadyToDraw happened. - ++step_; + // NotifyReadyToDrawOnThread() is not called in multi threaded mode + EndTest(); break; } } - void NotifyReadyToDrawOnThread(LayerTreeHostImpl* host_impl) override { - if (step_ == 5) { - ++step_; - // NotifyReadyToDraw has happened, we may draw once more, but should not - // get any more draws after that. End the test after a timeout to watch - // for any extraneous draws. - // TODO(brianderson): We could remove this delay and instead wait until - // the viz::BeginFrameSource decides it doesn't need to send frames - // anymore, or test that it already doesn't here. - EndTestAfterDelayMs(16 * 4); - } - } - void NotifyTileStateChangedOnThread(LayerTreeHostImpl* host_impl, const Tile* tile) override { // On step_ == 2, we are preventing texture uploads from completing, @@ -7705,7 +7701,7 @@ class LayerTreeHostTestOneActivatePerPrepareTiles : public LayerTreeHostTest { size_t scheduled_prepare_tiles_count_; }; -SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestOneActivatePerPrepareTiles); +MULTI_THREAD_TEST_F(LayerTreeHostTestOneActivatePerPrepareTiles); class LayerTreeHostTestActivationCausesPrepareTiles : public LayerTreeHostTest { public: @@ -10334,5 +10330,155 @@ class LayerTreeHostTestInvalidateImplSideForRerasterTiling scoped_refptr<FakePictureLayer> layer_on_main_; }; MULTI_THREAD_TEST_F(LayerTreeHostTestInvalidateImplSideForRerasterTiling); + +class LayerTreeHostTestNeedsNotifyReadyToDrawAndActivate + : public LayerTreeHostTest { + public: + void BeginTest() override { PostSetNeedsCommitToMainThread(); } + + void DidCommitAndDrawFrame() override { EndTest(); } + + void NotifyReadyToDrawOnThread(LayerTreeHostImpl* host_impl) override { + // called if needs_notify_ready_to_draw_ is true + EXPECT_TRUE(needs_notify_ready_to_draw_); + } + + void NotifyReadyToActivateOnThread(LayerTreeHostImpl* host_impl) override { + // called if needs_notify_ready_to_activate_ is true + EXPECT_TRUE(needs_notify_ready_to_activate_); + } + + protected: + bool needs_notify_ready_to_draw_ = true; + bool needs_notify_ready_to_activate_ = true; +}; + +class LayerTreeHostTestNeedsNotifyReadyToDrawOnly + : public LayerTreeHostTestNeedsNotifyReadyToDrawAndActivate { + public: + LayerTreeHostTestNeedsNotifyReadyToDrawOnly() { + // expectation for Single Threaded LayerTreeHost + needs_notify_ready_to_draw_ = true; + needs_notify_ready_to_activate_ = false; + } +}; +SINGLE_THREAD_TEST_F(LayerTreeHostTestNeedsNotifyReadyToDrawOnly); + +class LayerTreeHostTestNeedsNotifyReadyToActivateOnly + : public LayerTreeHostTestNeedsNotifyReadyToDrawAndActivate { + public: + LayerTreeHostTestNeedsNotifyReadyToActivateOnly() { + // expectation for threaded LayerTreeHost + needs_notify_ready_to_draw_ = false; + needs_notify_ready_to_activate_ = true; + } +}; +MULTI_THREAD_TEST_F(LayerTreeHostTestNeedsNotifyReadyToActivateOnly); + +class LayerTreeHostTestDidCommitAndDrawFrame : public LayerTreeHostTest { + public: + void BeginTest() override { PostSetNeedsCommitToMainThread(); } + + void DidCommitAndDrawFrame() override { EXPECT_EQ(0, num_draws_); } + + void DidReceiveCompositorFrameAck() override { + ++num_draws_; + if (num_draws_ == 2) { + EndTest(); + } + } + + void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) override { + EXPECT_EQ(0, host_impl->active_tree()->source_frame_number()); + } + + void DidReceiveCompositorFrameAckOnThread( + LayerTreeHostImpl* host_impl) override { + host_impl->SetViewportDamage(gfx::Rect(1, 1)); + host_impl->RequestImplSideInvalidationForRerasterTiling(); + } + + protected: + int num_draws_ = 0; +}; + +MULTI_THREAD_TEST_F(LayerTreeHostTestDidCommitAndDrawFrame); + +class LayerTreeHostTestBeginFramePausedChanged : public LayerTreeHostTest { + protected: + void SetupTree() override { + scoped_refptr<Layer> root = Layer::Create(); + root->SetBounds(gfx::Size(10, 10)); + layer_tree_host()->SetRootLayer(std::move(root)); + LayerTreeHostTest::SetupTree(); + } + + void BeginTest() override { PostSetNeedsCommitToMainThread(); } + + void ReadyToCommitOnThread(LayerTreeHostImpl* host_impl) override { + switch (host_impl->active_tree()->source_frame_number()) { + // frame 1 is ready in main thread and main thread is waiting until + // commit complete in impl thread + case 0: + // this should trigger OnBeginFramePausedChanged(true) callback, so that + // scheduler can abort the pending commit job + layer_tree_frame_sink_->UnregisterBeginFrameSource(); + break; + } + } + + void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override { + switch (host_impl->pending_tree()->source_frame_number()) { + case 1: + // Scheduler abort the pending commit job successfully + EndTest(); + break; + } + } + + void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) override { + switch (host_impl->active_tree()->source_frame_number()) { + case 0: + // Block activation of pedning tree which is created by implside + // invalidation. This is for making ShouldCommit() return false. + host_impl->BlockNotifyReadyToActivateForTesting(true); + // next commit can start only if pending tree was created by implside + // invalidation. + host_impl->RequestImplSideInvalidationForRerasterTiling(); + break; + } + } + + void DidInvalidateContentOnImplSide(LayerTreeHostImpl* host_impl) override { + switch (host_impl->active_tree()->source_frame_number()) { + case 0: + // has_pending_tree_ is true now, so we can request next frame which + // will be stuck in main thread because has_pending_tree_ == true block + // commit process. + PostSetNeedsCommitToMainThread(); + break; + } + } + + std::unique_ptr<TestLayerTreeFrameSink> CreateLayerTreeFrameSink( + const viz::RendererSettings& renderer_settings, + double refresh_rate, + scoped_refptr<viz::ContextProvider> compositor_context_provider, + scoped_refptr<viz::RasterContextProvider> worker_context_provider) + override { + std::unique_ptr<TestLayerTreeFrameSink> frame_sink = + LayerTreeHostTest::CreateLayerTreeFrameSink( + renderer_settings, refresh_rate, + std::move(compositor_context_provider), + std::move(worker_context_provider)); + layer_tree_frame_sink_ = frame_sink.get(); + return frame_sink; + } + + private: + TestLayerTreeFrameSink* layer_tree_frame_sink_; +}; +MULTI_THREAD_TEST_F(LayerTreeHostTestBeginFramePausedChanged); + } // namespace } // namespace cc diff --git a/chromium/cc/trees/layer_tree_host_unittest_scroll.cc b/chromium/cc/trees/layer_tree_host_unittest_scroll.cc index e5b26bd899b..053b3fef26b 100644 --- a/chromium/cc/trees/layer_tree_host_unittest_scroll.cc +++ b/chromium/cc/trees/layer_tree_host_unittest_scroll.cc @@ -2922,5 +2922,125 @@ class LayerTreeHostScrollTestViewportAbortedCommit SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostScrollTestViewportAbortedCommit); +class PreventRecreatingTilingDuringScroll : public LayerTreeHostScrollTest { + public: + PreventRecreatingTilingDuringScroll() + : initial_scale_(2.0f, 1.0f), new_scale_(2.0f, 2.0f) {} + + void SetupTree() override { + LayerTreeHostScrollTest::SetupTree(); + + Layer* root_layer = layer_tree_host()->root_layer(); + Layer* root_scroll_layer = + layer_tree_host()->OuterViewportScrollLayerForTesting(); + + child_layer_ = FakePictureLayer::Create(&client_); + child_layer_->SetElementId( + LayerIdToElementIdForTesting(child_layer_->id())); + + child_layer_->SetIsDrawable(true); + child_layer_->SetHitTestable(true); + child_layer_->SetElementId( + LayerIdToElementIdForTesting(child_layer_->id())); + child_layer_->SetBounds(root_scroll_layer->bounds()); + root_layer->AddChild(child_layer_); + + CopyProperties(root_scroll_layer, child_layer_.get()); + CreateTransformNode(child_layer_.get()); + CreateScrollNode(child_layer_.get(), root_layer->bounds()); + client_.set_bounds(root_scroll_layer->bounds()); + client_.set_fill_with_nonsolid_color(true); + client_.set_has_draw_text_op(); + child_layer_id_ = child_layer_->id(); + } + + void BeginTest() override { PostSetNeedsCommitToMainThread(); } + + void WillCommit(const CommitState&) override { + TransformTree& transform_tree = + layer_tree_host()->property_trees()->transform_tree_mutable(); + + gfx::Transform transform; + switch (layer_tree_host()->SourceFrameNumber()) { + case 0: + transform.Scale(initial_scale_.x(), initial_scale_.y()); + transform_tree.OnTransformAnimated(child_layer_->element_id(), + transform); + break; + case 1: + transform.Scale(new_scale_.x(), new_scale_.y()); + transform_tree.OnTransformAnimated(child_layer_->element_id(), + transform); + break; + } + } + + void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override { + if (host_impl->active_tree()->source_frame_number() == 1) { + FakePictureLayerImpl* target_layer = static_cast<FakePictureLayerImpl*>( + host_impl->active_tree()->LayerById(child_layer_id_)); + gfx::AxisTransform2d tiling_transform = + target_layer->HighResTiling()->raster_transform(); + + if (host_impl->GetActivelyScrollingType() != + ActivelyScrollingType::kNone) { + // In active tree, recreating the delayed tiling should not happen + ASSERT_EQ(tiling_transform.scale(), initial_scale_); + + // stop scroll to check if recreating tiling happen in active tree + scroll_check_pending_ = false; + host_impl->GetInputHandler().ScrollEnd(); + // make sure redraw happen + host_impl->active_tree()->set_needs_update_draw_properties(); + host_impl->SetNeedsRedraw(); + } + } + } + + void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) override { + FakePictureLayerImpl* target_layer = static_cast<FakePictureLayerImpl*>( + host_impl->active_tree()->LayerById(child_layer_id_)); + gfx::AxisTransform2d tiling_transform = + target_layer->HighResTiling()->raster_transform(); + + switch (host_impl->active_tree()->source_frame_number()) { + case 0: + // start scroll + host_impl->GetInputHandler().ScrollBegin( + BeginState(gfx::Point(0, 0), gfx::Vector2dF(0, 10)).get(), + ui::ScrollInputType::kTouchscreen); + host_impl->GetInputHandler().ScrollUpdate( + UpdateState(gfx::Point(), gfx::Vector2dF(0, 10)).get()); + scroll_check_pending_ = true; + PostSetNeedsCommitToMainThread(); + break; + + case 1: + if (host_impl->GetActivelyScrollingType() != + ActivelyScrollingType::kNone) { + // In pending tree, recreating tiling should delayed during scroll + ASSERT_TRUE(scroll_check_pending_); + ASSERT_EQ(tiling_transform.scale(), initial_scale_); + host_impl->SetNeedsRedraw(); + } else { + // recreating tiling should happen after scroll finish + ASSERT_FALSE(scroll_check_pending_); + ASSERT_EQ(tiling_transform.scale(), new_scale_); + EndTest(); + } + break; + } + } + + private: + scoped_refptr<FakePictureLayer> child_layer_; + int child_layer_id_; + FakeContentLayerClient client_; + bool scroll_check_pending_ = true; + const gfx::Vector2dF initial_scale_; + const gfx::Vector2dF new_scale_; +}; + +MULTI_THREAD_TEST_F(PreventRecreatingTilingDuringScroll); } // namespace } // namespace cc diff --git a/chromium/cc/trees/layer_tree_impl.cc b/chromium/cc/trees/layer_tree_impl.cc index 13788d94cba..fcc078f346d 100644 --- a/chromium/cc/trees/layer_tree_impl.cc +++ b/chromium/cc/trees/layer_tree_impl.cc @@ -152,7 +152,7 @@ LayerTreeImpl::LayerTreeImpl( is_first_frame_after_commit_tracker_(-1), hud_layer_(nullptr), property_trees_(host_impl), - background_color_(0), + background_color_(SkColors::kTransparent), last_scrolled_scroll_node_index_(kInvalidPropertyNodeId), page_scale_factor_(page_scale_factor), min_page_scale_factor_(0), @@ -1483,19 +1483,21 @@ gfx::Rect LayerTreeImpl::RootScrollLayerDeviceViewportBounds() const { gfx::Rect(root_scroll_node->bounds)); } -void LayerTreeImpl::ApplySentScrollAndScaleDeltasFromAbortedCommit() { +void LayerTreeImpl::ApplySentScrollAndScaleDeltasFromAbortedCommit( + bool main_frame_applied_deltas) { DCHECK(IsActiveTree()); - page_scale_factor()->AbortCommit(); - top_controls_shown_ratio()->AbortCommit(); - elastic_overscroll()->AbortCommit(); + page_scale_factor()->AbortCommit(main_frame_applied_deltas); + top_controls_shown_ratio()->AbortCommit(main_frame_applied_deltas); + bottom_controls_shown_ratio()->AbortCommit(main_frame_applied_deltas); + elastic_overscroll()->AbortCommit(main_frame_applied_deltas); if (layer_list_.empty()) return; property_trees() ->scroll_tree_mutable() - .ApplySentScrollDeltasFromAbortedCommit(); + .ApplySentScrollDeltasFromAbortedCommit(main_frame_applied_deltas); } void LayerTreeImpl::SetViewportPropertyIds(const ViewportPropertyIds& ids) { @@ -2302,7 +2304,7 @@ static bool PointIsClippedByAncestorClipNode( for (const ClipNode* clip_node = clip_tree.Node(layer->clip_tree_index()); clip_node->id > kViewportPropertyNodeId; clip_node = clip_tree.parent(clip_node)) { - if (clip_node->clip_type == ClipNode::ClipType::APPLIES_LOCAL_CLIP) { + if (clip_node->AppliesLocalClip()) { clip = gfx::ToEnclosingRect(clip_node->clip); gfx::Transform screen_space_transform = @@ -2549,7 +2551,7 @@ LayerTreeImpl::FindAllLayersUpToAndIncludingFirstScrollable( if (!layer->HitTestable()) continue; - if (first_hit && + if (first_hit && layer->Is3dSorted() && layer->GetSortingContextId() != first_hit->GetSortingContextId()) continue; @@ -2568,7 +2570,7 @@ LayerTreeImpl::FindAllLayersUpToAndIncludingFirstScrollable( if (!first_hit) first_hit = layer; - if (first_hit->Is3dSorted()) { + if (first_hit->Is3dSorted() && layer->Is3dSorted()) { layers_3d.emplace_back( std::pair<const LayerImpl*, float>(layer, distance_to_intersection)); } else { @@ -2585,7 +2587,7 @@ LayerTreeImpl::FindAllLayersUpToAndIncludingFirstScrollable( } if (first_hit->Is3dSorted()) { - DCHECK(layers.empty()); + std::vector<const LayerImpl*> result; DCHECK(!layers_3d.empty()); // Since we hit a layer in a rendering context, we need to sort the layers @@ -2600,10 +2602,13 @@ LayerTreeImpl::FindAllLayersUpToAndIncludingFirstScrollable( for (const auto& pair : layers_3d) { const LayerImpl* layer = pair.first; - layers.push_back(layer); + result.push_back(layer); if (layer->IsScrollerOrScrollbar()) - break; + return result; } + // Append 2D layers if none of the 3D layers were scrollable. + result.insert(result.end(), layers.begin(), layers.end()); + return result; } else { DCHECK(!layers.empty()); DCHECK(layers_3d.empty()); diff --git a/chromium/cc/trees/layer_tree_impl.h b/chromium/cc/trees/layer_tree_impl.h index 172bcc07769..f1d2b0bb7c8 100644 --- a/chromium/cc/trees/layer_tree_impl.h +++ b/chromium/cc/trees/layer_tree_impl.h @@ -360,10 +360,11 @@ class CC_EXPORT LayerTreeImpl { void SetCurrentlyScrollingNode(const ScrollNode* node); void ClearCurrentlyScrollingNode(); - void ApplySentScrollAndScaleDeltasFromAbortedCommit(); + void ApplySentScrollAndScaleDeltasFromAbortedCommit( + bool main_frame_applied_deltas); - SkColor background_color() const { return background_color_; } - void set_background_color(SkColor color) { background_color_ = color; } + SkColor4f background_color() const { return background_color_; } + void set_background_color(SkColor4f color) { background_color_ = color; } gfx::OverlayTransform display_transform_hint() const { return display_transform_hint_; @@ -835,7 +836,7 @@ class CC_EXPORT LayerTreeImpl { int is_first_frame_after_commit_tracker_; raw_ptr<HeadsUpDisplayLayerImpl> hud_layer_; PropertyTrees property_trees_; - SkColor background_color_; + SkColor4f background_color_; int last_scrolled_scroll_node_index_; diff --git a/chromium/cc/trees/layer_tree_settings.cc b/chromium/cc/trees/layer_tree_settings.cc index b8168bd5b9e..43709401e46 100644 --- a/chromium/cc/trees/layer_tree_settings.cc +++ b/chromium/cc/trees/layer_tree_settings.cc @@ -36,6 +36,8 @@ TileManagerSettings LayerTreeSettings::ToTileManagerSettings() const { tile_manager_settings.use_partial_raster = use_partial_raster; tile_manager_settings.enable_checker_imaging = enable_checker_imaging; tile_manager_settings.min_image_bytes_to_checker = min_image_bytes_to_checker; + tile_manager_settings.needs_notify_ready_to_draw = + commit_to_active_tree | wait_for_all_pipeline_stages_before_draw; return tile_manager_settings; } diff --git a/chromium/cc/trees/layer_tree_settings.h b/chromium/cc/trees/layer_tree_settings.h index 8dfe968b631..809fb46cc34 100644 --- a/chromium/cc/trees/layer_tree_settings.h +++ b/chromium/cc/trees/layer_tree_settings.h @@ -55,7 +55,7 @@ class CC_EXPORT LayerTreeSettings { base::TimeDelta scrollbar_fade_duration; base::TimeDelta scrollbar_thinning_duration; bool scrollbar_flash_after_any_scroll_update = false; - SkColor solid_color_scrollbar_color = SK_ColorWHITE; + SkColor4f solid_color_scrollbar_color = SkColors::kWhite; base::TimeDelta scroll_animation_duration_for_testing; bool timeout_and_draw_when_animation_checkerboards = true; bool layers_always_allowed_lcd_text = false; @@ -212,6 +212,9 @@ class CC_EXPORT LayerTreeSettings { // Whether Fluent scrollbar is enabled. Please check https://crbug.com/1292117 // to find the link to the Fluent Scrollbar spec and related CLs. bool enable_fluent_scrollbar = false; + + // This corresponds to the ScrollUpdateOptimizations feature. + bool enable_scroll_update_optimizations = false; }; class CC_EXPORT LayerListSettings : public LayerTreeSettings { diff --git a/chromium/cc/trees/occlusion_tracker_unittest.cc b/chromium/cc/trees/occlusion_tracker_unittest.cc index 6fd08a276d2..58ffd40cba5 100644 --- a/chromium/cc/trees/occlusion_tracker_unittest.cc +++ b/chromium/cc/trees/occlusion_tracker_unittest.cc @@ -1459,8 +1459,8 @@ class OcclusionTrackerTestPixelsNeededForDropShadowBackdropFilter scale_by_half.Scale(0.5, 0.5); FilterOperations filters; - filters.Append(FilterOperation::CreateDropShadowFilter(gfx::Point(10, 10), - 5, SK_ColorBLACK)); + filters.Append(FilterOperation::CreateDropShadowFilter( + gfx::Point(10, 10), 5, SkColors::kBlack)); enum Direction { LEFT, diff --git a/chromium/cc/trees/property_tree.cc b/chromium/cc/trees/property_tree.cc index 7a3bb079487..c631c494fde 100644 --- a/chromium/cc/trees/property_tree.cc +++ b/chromium/cc/trees/property_tree.cc @@ -181,6 +181,7 @@ void TransformTree::UpdateTransforms( DCHECK(parent_node); // TODO(flackr): Only dirty when scroll offset changes. if (node->sticky_position_constraint_id >= 0 || + node->anchor_scroll_containers_data_id >= 0 || node->needs_local_transform_update || ShouldUndoOverscroll(node)) { UpdateLocalTransform(node, viewport_property_ids); } else { @@ -455,6 +456,47 @@ gfx::Vector2dF TransformTree::StickyPositionOffset(TransformNode* node) { return gfx::ToRoundedVector2d(sticky_offset); } +AnchorScrollContainersData& TransformTree::EnsureAnchorScrollContainersData( + int node_id) { + TransformNode* node = Node(node_id); + if (node->anchor_scroll_containers_data_id == -1) { + node->anchor_scroll_containers_data_id = + anchor_scroll_containers_data_.size(); + anchor_scroll_containers_data_.push_back(AnchorScrollContainersData()); + } + return anchor_scroll_containers_data_[node->anchor_scroll_containers_data_id]; +} + +const AnchorScrollContainersData* TransformTree::GetAnchorScrollContainersData( + int node_id) const { + const TransformNode* node = Node(node_id); + if (node->anchor_scroll_containers_data_id == -1) + return nullptr; + return &anchor_scroll_containers_data_ + [node->anchor_scroll_containers_data_id]; +} + +gfx::Vector2dF TransformTree::AnchorScrollOffset(TransformNode* node) { + const AnchorScrollContainersData* data = + GetAnchorScrollContainersData(node->id); + if (!data) + return gfx::Vector2dF(); + gfx::Vector2dF accumulated_scroll_offset(0, 0); + for (int scroller_id = data->inner_most_scroll_container_id; + scroller_id != kInvalidPropertyNodeId;) { + const ScrollNode* scroll_node = + property_trees()->scroll_tree().Node(scroller_id); + const TransformNode* transform_node = Node(scroll_node->transform_id); + accumulated_scroll_offset += + transform_node->scroll_offset.OffsetFromOrigin(); + + if (scroller_id == data->outer_most_scroll_container_id) + break; + scroller_id = scroll_node->parent_id; + } + return data->accumulated_scroll_origin - accumulated_scroll_offset; +} + bool TransformTree::ShouldUndoOverscroll(const TransformNode* node) const { return fixed_elements_dont_overscroll_ && node && node->is_fixed_to_viewport; } @@ -517,6 +559,7 @@ void TransformTree::UpdateLocalTransform( transform.Translate(fixed_position_adjustment - node->scroll_offset.OffsetFromOrigin()); transform.Translate(StickyPositionOffset(node)); + transform.Translate(AnchorScrollOffset(node)); transform.PreconcatTransform(node->local); transform.Translate3d(gfx::Point3F() - node->origin); @@ -1363,7 +1406,15 @@ bool ScrollTree::IsComposited(const ScrollNode& node) const { } bool ScrollTree::CanRealizeScrollsOnCompositor(const ScrollNode& node) const { - return node.is_composited && !node.main_thread_scrolling_reasons; + return GetMainThreadRepaintReasons(node) == + MainThreadScrollingReason::kNotScrollingOnMain; +} + +uint32_t ScrollTree::GetMainThreadRepaintReasons(const ScrollNode& node) const { + uint32_t reasons = node.main_thread_scrolling_reasons; + if (!node.is_composited) + reasons |= MainThreadScrollingReason::kNoScrollingLayer; + return reasons; } void ScrollTree::clear() { @@ -1653,10 +1704,15 @@ void ScrollTree::PushScrollUpdatesFromMainThread( for (auto map_entry = synced_scroll_offset_map_.begin(); map_entry != synced_scroll_offset_map_.end();) { ElementId id = map_entry->first; - if (main_scroll_offset_map.find(id) == main_scroll_offset_map.end()) + if (main_scroll_offset_map.find(id) == main_scroll_offset_map.end()) { + // This SyncedScrollOffset might still be used to send a delta from the + // active tree to the main thread, so we need to clear out the delta that + // was sent to the main thread for this commit. + map_entry->second->PushMainToPending(map_entry->second->Current(true)); map_entry = synced_scroll_offset_map_.erase(map_entry); - else + } else { map_entry++; + } } for (auto map_entry : main_scroll_offset_map) { @@ -1712,10 +1768,11 @@ void ScrollTree::PushScrollUpdatesFromPendingTree( } } -void ScrollTree::ApplySentScrollDeltasFromAbortedCommit() { +void ScrollTree::ApplySentScrollDeltasFromAbortedCommit( + bool main_frame_applied_deltas) { DCHECK(property_trees()->is_active()); for (auto& map_entry : synced_scroll_offset_map_) - map_entry.second->AbortCommit(); + map_entry.second->AbortCommit(main_frame_applied_deltas); } void ScrollTree::SetBaseScrollOffset(ElementId id, diff --git a/chromium/cc/trees/property_tree.h b/chromium/cc/trees/property_tree.h index 78f52a32cbc..5880323fb67 100644 --- a/chromium/cc/trees/property_tree.h +++ b/chromium/cc/trees/property_tree.h @@ -157,6 +157,7 @@ class CC_EXPORT PropertyTree { base::flat_map<ElementId, int> element_id_to_node_index_; }; +struct AnchorScrollContainersData; struct StickyPositionNodeData; class CC_EXPORT TransformTree final : public PropertyTree<TransformNode> { @@ -258,6 +259,10 @@ class CC_EXPORT TransformTree final : public PropertyTree<TransformNode> { } StickyPositionNodeData& EnsureStickyPositionData(int node_id); + const AnchorScrollContainersData* GetAnchorScrollContainersData( + int node_id) const; + AnchorScrollContainersData& EnsureAnchorScrollContainersData(int node_id); + // Computes the combined transform between |source_id| and |dest_id|. These // two nodes must be on the same ancestor chain. void CombineTransformsBetween(int source_id, @@ -278,6 +283,7 @@ class CC_EXPORT TransformTree final : public PropertyTree<TransformNode> { StickyPositionNodeData* MutableStickyPositionData(int node_id); gfx::Vector2dF StickyPositionOffset(TransformNode* node); + gfx::Vector2dF AnchorScrollOffset(TransformNode* node); void UpdateLocalTransform(TransformNode* node, const ViewportPropertyIds* viewport_property_ids); void UpdateScreenSpaceTransform(TransformNode* node, @@ -300,6 +306,13 @@ class CC_EXPORT TransformTree final : public PropertyTree<TransformNode> { std::vector<int> nodes_affected_by_outer_viewport_bounds_delta_; std::vector<TransformCachedNodeData> cached_data_; std::vector<StickyPositionNodeData> sticky_position_data_; + std::vector<AnchorScrollContainersData> anchor_scroll_containers_data_; +}; + +struct AnchorScrollContainersData { + int inner_most_scroll_container_id = kInvalidPropertyNodeId; + int outer_most_scroll_container_id = kInvalidPropertyNodeId; + gfx::Vector2d accumulated_scroll_origin; }; struct StickyPositionNodeData { @@ -390,11 +403,11 @@ class CC_EXPORT EffectTree final : public PropertyTree<EffectNode> { int LowestCommonAncestorWithRenderSurface(int id_1, int id_2) const; RenderSurfaceImpl* GetRenderSurface(int id) { - return render_surfaces_[id].get(); + return render_surfaces_[static_cast<size_t>(id)].get(); } const RenderSurfaceImpl* GetRenderSurface(int id) const { - return render_surfaces_[id].get(); + return render_surfaces_[static_cast<size_t>(id)].get(); } void ClearTransitionPseudoElementEffectNodes(); @@ -526,7 +539,7 @@ class CC_EXPORT ScrollTree final : public PropertyTree<ScrollNode> { // Applies deltas sent in the previous main frame onto the impl thread state. // Should only be called on the impl thread side PropertyTrees. - void ApplySentScrollDeltasFromAbortedCommit(); + void ApplySentScrollDeltasFromAbortedCommit(bool main_frame_applied_deltas); // Pushes scroll updates from the ScrollTree on the main thread onto the // impl thread associated state. @@ -583,6 +596,11 @@ class CC_EXPORT ScrollTree final : public PropertyTree<ScrollNode> { // main-thread scrolling reasons (see main_thread_scrolling_reason.h). bool CanRealizeScrollsOnCompositor(const ScrollNode& node) const; + // Reports reasons for blocking scroll updates on main-thread repaint. For use + // only with scroll unification enabled. Returns bitfield of values from + // MainThreadScrollingReason. + uint32_t GetMainThreadRepaintReasons(const ScrollNode& node) const; + private: // ScrollTree doesn't use the needs_update flag. using PropertyTree::needs_update; diff --git a/chromium/cc/trees/property_tree_builder.cc b/chromium/cc/trees/property_tree_builder.cc index 00eb3a85b00..eb6da2827e6 100644 --- a/chromium/cc/trees/property_tree_builder.cc +++ b/chromium/cc/trees/property_tree_builder.cc @@ -43,7 +43,7 @@ struct DataForRecursion { int closest_ancestor_with_cached_render_surface; int closest_ancestor_with_copy_request; int closest_ancestor_being_captured; - SkColor safe_opaque_background_color; + SkColor4f safe_opaque_background_color; bool animation_axis_aligned_since_render_target; bool not_axis_aligned_since_last_clip; gfx::Transform compound_transform_since_render_target; @@ -215,21 +215,17 @@ void PropertyTreeBuilderContext::AddClipNodeIfNeeded( data_for_children->clip_tree_parent = parent_id; } else { ClipNode node; - node.clip = layer->EffectiveClipRect(); - - // Move the clip bounds so that it is relative to the transform parent. - node.clip += layer->offset_to_transform_parent(); - - node.transform_id = created_transform_node - ? data_for_children->transform_tree_parent - : data_from_ancestor.transform_tree_parent; if (layer_clips_subtree) { - node.clip_type = ClipNode::ClipType::APPLIES_LOCAL_CLIP; + node.clip = layer->EffectiveClipRect(); + // Move the clip bounds so that it is relative to the transform parent. + node.clip += layer->offset_to_transform_parent(); } else { DCHECK(layer->filters().HasFilterThatMovesPixels()); - node.clip_type = ClipNode::ClipType::EXPANDS_CLIP; - node.clip_expander = ClipExpander(layer->effect_tree_index()); + node.pixel_moving_filter_id = layer->effect_tree_index(); } + node.transform_id = created_transform_node + ? data_for_children->transform_tree_parent + : data_from_ancestor.transform_tree_parent; data_for_children->clip_tree_parent = clip_tree_.Insert(node, parent_id); } @@ -716,15 +712,13 @@ void PropertyTreeBuilderContext::AddScrollNodeIfNeeded( void SetSafeOpaqueBackgroundColor(const DataForRecursion& data_from_ancestor, Layer* layer, DataForRecursion* data_for_children) { - // TODO(crbug/1308932): Remove toSkColor and make all SkColor4f. - SkColor background_color = layer->background_color().toSkColor(); + SkColor4f background_color = layer->background_color(); data_for_children->safe_opaque_background_color = - SkColorGetA(background_color) == 255 + background_color.isOpaque() ? background_color : data_from_ancestor.safe_opaque_background_color; - // TODO(crbug/1308932): Remove FromColor and make all SkColor4f. layer->SetSafeOpaqueBackgroundColor( - SkColor4f::FromColor(data_for_children->safe_opaque_background_color)); + data_for_children->safe_opaque_background_color); } void PropertyTreeBuilderContext::BuildPropertyTreesInternal( @@ -806,16 +800,15 @@ void PropertyTreeBuilderContext::BuildPropertyTrees() { data_for_recursion.animation_axis_aligned_since_render_target = true; data_for_recursion.not_axis_aligned_since_last_clip = false; - SkColor root_background_color = layer_tree_host_->background_color(); - if (SkColorGetA(root_background_color) != 255) - root_background_color = SkColorSetA(root_background_color, 255); - data_for_recursion.safe_opaque_background_color = root_background_color; + data_for_recursion.safe_opaque_background_color = + layer_tree_host_->background_color().isOpaque() + ? layer_tree_host_->background_color() + : layer_tree_host_->background_color().makeOpaque(); property_trees_.clear(); transform_tree_.set_device_scale_factor( layer_tree_host_->device_scale_factor()); ClipNode root_clip; - root_clip.clip_type = ClipNode::ClipType::APPLIES_LOCAL_CLIP; root_clip.clip = gfx::RectF(layer_tree_host_->device_viewport_rect()); root_clip.transform_id = kRootPropertyNodeId; data_for_recursion.clip_tree_parent = diff --git a/chromium/cc/trees/property_tree_builder_unittest.cc b/chromium/cc/trees/property_tree_builder_unittest.cc index 5b5f96f4476..b3a537c7f46 100644 --- a/chromium/cc/trees/property_tree_builder_unittest.cc +++ b/chromium/cc/trees/property_tree_builder_unittest.cc @@ -764,7 +764,7 @@ TEST_F(PropertyTreeBuilderTest, GradientMask) { child1->SetIsDrawable(true); gfx::LinearGradient gradient_mask(45); - gradient_mask.AddStep(50, 0x50); + gradient_mask.AddStep(.5, 0x50); child1->SetGradientMask(gradient_mask); // Without render surface. @@ -807,12 +807,12 @@ TEST_F(PropertyTreeBuilderTest, GradientMask) { EXPECT_EQ(gfx::RectF(10, 10, 300, 200), layer_impl1->draw_properties().mask_filter_info.bounds()); // |angle| is updated by the scale transform. - EXPECT_EQ(33, layer_impl1->draw_properties() + EXPECT_EQ(34, layer_impl1->draw_properties() .mask_filter_info.gradient_mask() - .angle()); + ->angle()); EXPECT_EQ(gradient_mask.steps(), layer_impl1->draw_properties() .mask_filter_info.gradient_mask() - .steps()); + ->steps()); } // Rotate transform eliminates gradient mask. @@ -878,10 +878,11 @@ TEST_F(PropertyTreeBuilderTest, GradientMask) { EXPECT_EQ(gfx::RectF(10, 10, 300, 200), render_surface_impl1->mask_filter_info().bounds()); // |angle| is updated by the scale transform. - EXPECT_EQ(33, - render_surface_impl1->mask_filter_info().gradient_mask().angle()); - EXPECT_EQ(gradient_mask.steps(), - render_surface_impl1->mask_filter_info().gradient_mask().steps()); + EXPECT_EQ( + 34, render_surface_impl1->mask_filter_info().gradient_mask()->angle()); + EXPECT_EQ( + gradient_mask.steps(), + render_surface_impl1->mask_filter_info().gradient_mask()->steps()); } // Rotate transform eliminates gradient mask. @@ -914,12 +915,12 @@ TEST_F(PropertyTreeBuilderTest, NestedGradientMask) { grand_child1->SetIsDrawable(true); gfx::LinearGradient gradient_mask1(30); - gradient_mask1.AddStep(50, 0x50); + gradient_mask1.AddStep(.5, 0x50); child1->SetGradientMask(gradient_mask1); gfx::LinearGradient gradient_mask2(45); gradient_mask2.AddStep(0, 0xFF); - gradient_mask2.AddStep(100, 0x0); + gradient_mask2.AddStep(1, 0x0); grand_child1->SetGradientMask(gradient_mask2); CommitAndActivate(); @@ -960,11 +961,12 @@ TEST_F(PropertyTreeBuilderTest, NestedGradientMask) { // |mask_info| coordinates are in the target space of the render surface's // layer. auto* render_surface_impl1 = GetRenderSurfaceImpl(child1); - EXPECT_EQ(gradient_mask1.steps(), - render_surface_impl1->mask_filter_info().gradient_mask().steps()); + EXPECT_EQ( + gradient_mask1.steps(), + render_surface_impl1->mask_filter_info().gradient_mask()->steps()); // |angle| is updated by the scale transform. - EXPECT_EQ(21, - render_surface_impl1->mask_filter_info().gradient_mask().angle()); + EXPECT_EQ( + 21, render_surface_impl1->mask_filter_info().gradient_mask()->angle()); // |mask_info| is in the coordinate space of the transform node associated // with this effect node. @@ -979,9 +981,9 @@ TEST_F(PropertyTreeBuilderTest, NestedGradientMask) { EXPECT_EQ(gfx::RectF(30, 10, 300, 150), draw_properties2.mask_filter_info.bounds()); // |angle| is updated by the scale transform. - EXPECT_EQ(26, draw_properties2.mask_filter_info.gradient_mask().angle()); + EXPECT_EQ(27, draw_properties2.mask_filter_info.gradient_mask()->angle()); EXPECT_EQ(gradient_mask2.steps(), - draw_properties2.mask_filter_info.gradient_mask().steps()); + draw_properties2.mask_filter_info.gradient_mask()->steps()); } gfx::Transform rotate_transform; diff --git a/chromium/cc/trees/proxy.h b/chromium/cc/trees/proxy.h index 6c879ed922d..9e865842342 100644 --- a/chromium/cc/trees/proxy.h +++ b/chromium/cc/trees/proxy.h @@ -101,9 +101,9 @@ class CC_EXPORT Proxy { virtual void SetRenderFrameObserver( std::unique_ptr<RenderFrameMetadataObserver> observer) = 0; - // Returns a percentage representing average throughput of last X seconds. + // Returns a percentage of dropped frames of the last second. // Only implemenented for single threaded proxy. - virtual uint32_t GetAverageThroughput() const = 0; + virtual double GetPercentDroppedFrames() const = 0; }; } // namespace cc diff --git a/chromium/cc/trees/proxy_impl.cc b/chromium/cc/trees/proxy_impl.cc index e253b21d1ba..298b13f3c57 100644 --- a/chromium/cc/trees/proxy_impl.cc +++ b/chromium/cc/trees/proxy_impl.cc @@ -64,7 +64,7 @@ class ScopedCommitCompletionEvent { proxy_main_weak_ptr_(proxy_main_weak_ptr) {} ScopedCommitCompletionEvent(const ScopedCommitCompletionEvent&) = delete; ~ScopedCommitCompletionEvent() { - event_->Signal(); + event_.ExtractAsDangling()->Signal(); main_thread_task_runner_->PostTask( FROM_HERE, base::BindOnce(&ProxyMain::DidCompleteCommit, proxy_main_weak_ptr_, commit_timestamps_)); @@ -77,7 +77,7 @@ class ScopedCommitCompletionEvent { } private: - const raw_ptr<CompletionEvent> event_; + raw_ptr<CompletionEvent> event_; CommitTimestamps commit_timestamps_; raw_ptr<base::SingleThreadTaskRunner> main_thread_task_runner_; base::WeakPtr<ProxyMain> proxy_main_weak_ptr_; @@ -797,8 +797,6 @@ void ProxyImpl::ScheduledActionPostCommit() { // This is run as a separate step from commit because it can be time-consuming // and ought not delay sending the next BeginMainFrame. host_impl_->CommitComplete(); - // TODO(szager): This should be set at activation time. crbug.com/1323906 - next_frame_is_newly_committed_frame_ = true; } void ProxyImpl::ScheduledActionActivateSyncTree() { @@ -809,6 +807,7 @@ void ProxyImpl::ScheduledActionActivateSyncTree() { "viz,benchmark", "MainFrame.Activate", TRACE_ID_LOCAL(host_impl_->sync_tree()->trace_id()), TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); + next_frame_is_newly_committed_frame_ = true; } DCHECK(IsImplThread()); host_impl_->ActivateSyncTree(); diff --git a/chromium/cc/trees/proxy_main.cc b/chromium/cc/trees/proxy_main.cc index 4bd1151680d..e56b02a53b8 100644 --- a/chromium/cc/trees/proxy_main.cc +++ b/chromium/cc/trees/proxy_main.cc @@ -606,7 +606,7 @@ bool ProxyMain::StartDeferringCommits(base::TimeDelta timeout, commits_restart_time_ = base::TimeTicks::Now() + timeout; // Notify dependent systems that the deferral status has changed. - layer_tree_host_->OnDeferCommitsChanged(true, reason); + layer_tree_host_->OnDeferCommitsChanged(true, reason, absl::nullopt); return true; } @@ -621,7 +621,7 @@ void ProxyMain::StopDeferringCommits(PaintHoldingCommitTrigger trigger) { TRACE_ID_LOCAL(this)); // Notify depended systems that the deferral status has changed. - layer_tree_host_->OnDeferCommitsChanged(false, reason); + layer_tree_host_->OnDeferCommitsChanged(false, reason, trigger); } bool ProxyMain::IsDeferringCommits() const { @@ -803,9 +803,9 @@ void ProxyMain::SetRenderFrameObserver( base::Unretained(proxy_impl_.get()), std::move(observer))); } -uint32_t ProxyMain::GetAverageThroughput() const { +double ProxyMain::GetPercentDroppedFrames() const { NOTIMPLEMENTED(); - return 0u; + return 0.0; } } // namespace cc diff --git a/chromium/cc/trees/proxy_main.h b/chromium/cc/trees/proxy_main.h index 1f0be862d15..bea1deee75c 100644 --- a/chromium/cc/trees/proxy_main.h +++ b/chromium/cc/trees/proxy_main.h @@ -120,7 +120,7 @@ class CC_EXPORT ProxyMain : public Proxy { base::WritableSharedMemoryMapping ukm_smoothness_data) override; void SetRenderFrameObserver( std::unique_ptr<RenderFrameMetadataObserver> observer) override; - uint32_t GetAverageThroughput() const override; + double GetPercentDroppedFrames() const override; // Returns |true| if the request was actually sent, |false| if one was // already outstanding. diff --git a/chromium/cc/trees/render_frame_metadata.h b/chromium/cc/trees/render_frame_metadata.h index 0c0395e5eb5..25e1daef871 100644 --- a/chromium/cc/trees/render_frame_metadata.h +++ b/chromium/cc/trees/render_frame_metadata.h @@ -64,7 +64,7 @@ class CC_EXPORT RenderFrameMetadata { // The background color of a CompositorFrame. It can be used for filling the // content area if the primary surface is unavailable and fallback is not // specified. - SkColor root_background_color = SK_ColorWHITE; + SkColor4f root_background_color = SkColors::kWhite; // Scroll offset of the root layer. absl::optional<gfx::PointF> root_scroll_offset; diff --git a/chromium/cc/trees/single_thread_proxy.cc b/chromium/cc/trees/single_thread_proxy.cc index 0066ee64b58..8df4049f4fa 100644 --- a/chromium/cc/trees/single_thread_proxy.cc +++ b/chromium/cc/trees/single_thread_proxy.cc @@ -366,7 +366,7 @@ bool SingleThreadProxy::StartDeferringCommits(base::TimeDelta timeout, commits_restart_time_ = base::TimeTicks::Now() + timeout; // Notify dependent systems that the deferral status has changed. - layer_tree_host_->OnDeferCommitsChanged(true, reason); + layer_tree_host_->OnDeferCommitsChanged(true, reason, absl::nullopt); return true; } @@ -383,7 +383,7 @@ void SingleThreadProxy::StopDeferringCommits( TRACE_ID_LOCAL(this)); // Notify dependent systems that the deferral status has changed. - layer_tree_host_->OnDeferCommitsChanged(false, reason); + layer_tree_host_->OnDeferCommitsChanged(false, reason, trigger); } bool SingleThreadProxy::IsDeferringCommits() const { @@ -909,9 +909,10 @@ void SingleThreadProxy::SetRenderFrameObserver( host_impl_->SetRenderFrameObserver(std::move(observer)); } -uint32_t SingleThreadProxy::GetAverageThroughput() const { +double SingleThreadProxy::GetPercentDroppedFrames() const { DebugScopedSetImplThread impl(task_runner_provider_); - return host_impl_->dropped_frame_counter()->GetAverageThroughput(); + return host_impl_->dropped_frame_counter() + ->sliding_window_current_percent_dropped(); } void SingleThreadProxy::UpdateBrowserControlsState( @@ -1063,7 +1064,7 @@ void SingleThreadProxy::DoBeginMainFrame( layer_tree_host_->BeginMainFrame(begin_frame_args); layer_tree_host_->AnimateLayers(begin_frame_args.frame_time); -#if BUILDFLAG(IS_CHROMEOS_ASH) +#if BUILDFLAG(IS_CHROMEOS) const bool record_metrics = layer_tree_host_->GetSettings().is_layer_tree_for_ui; #else diff --git a/chromium/cc/trees/single_thread_proxy.h b/chromium/cc/trees/single_thread_proxy.h index fef5133be31..96ddc04576d 100644 --- a/chromium/cc/trees/single_thread_proxy.h +++ b/chromium/cc/trees/single_thread_proxy.h @@ -76,7 +76,7 @@ class CC_EXPORT SingleThreadProxy : public Proxy, base::WritableSharedMemoryMapping ukm_smoothness_data) override; void SetRenderFrameObserver( std::unique_ptr<RenderFrameMetadataObserver> observer) override; - uint32_t GetAverageThroughput() const override; + double GetPercentDroppedFrames() const override; void UpdateBrowserControlsState(BrowserControlsState constraints, BrowserControlsState current, diff --git a/chromium/cc/trees/transform_node.cc b/chromium/cc/trees/transform_node.cc index a09615e1fae..d964b3ef628 100644 --- a/chromium/cc/trees/transform_node.cc +++ b/chromium/cc/trees/transform_node.cc @@ -17,6 +17,7 @@ TransformNode::TransformNode() parent_id(kInvalidPropertyNodeId), parent_frame_id(kInvalidPropertyNodeId), sticky_position_constraint_id(-1), + anchor_scroll_containers_data_id(-1), sorting_context_id(0), needs_local_transform_update(true), node_and_ancestors_are_animated_or_invertible(true), diff --git a/chromium/cc/trees/transform_node.h b/chromium/cc/trees/transform_node.h index e3e7ca9f0d7..95220aa2a7a 100644 --- a/chromium/cc/trees/transform_node.h +++ b/chromium/cc/trees/transform_node.h @@ -51,6 +51,10 @@ struct CC_EXPORT TransformNode { // transform node. -1 indicates there are no sticky position constraints. int sticky_position_constraint_id; + // This is the data of the scroll container of the anchor node specified by + // the `anchor-scroll` property. -1 indicates there is no such node. + int anchor_scroll_containers_data_id; + // This id determines which 3d rendering context the node is in. 0 is a // special value and indicates that the node is not in any 3d rendering // context. diff --git a/chromium/cc/trees/tree_synchronizer_unittest.cc b/chromium/cc/trees/tree_synchronizer_unittest.cc index 99a90139da0..7cfeade91bb 100644 --- a/chromium/cc/trees/tree_synchronizer_unittest.cc +++ b/chromium/cc/trees/tree_synchronizer_unittest.cc @@ -802,7 +802,7 @@ TEST_F(TreeSynchronizerTest, RoundedScrollDeltasOnCommit) { // Since this test simulates a scroll it needs an input handler. // TODO(bokan): Required because scroll commit is part of InputHandler - that - // shouldn't be. See comment in ThreadedInputHandler::ProcessCommitDeltas. + // shouldn't be. See comment in InputHandler::ProcessCommitDeltas. InputHandler::Create(static_cast<CompositorDelegateForInput&>(*host_impl)); scoped_refptr<Layer> scroll_layer = SetupScrollLayer(); @@ -828,7 +828,7 @@ TEST_F(TreeSynchronizerTest, PreserveFractionalScrollDeltasOnCommit) { // Since this test simulates a scroll it needs an input handler. // TODO(bokan): Required because scroll commit is part of InputHandler - that - // shouldn't be. See comment in ThreadedInputHandler::ProcessCommitDeltas. + // shouldn't be. See comment in InputHandler::ProcessCommitDeltas. InputHandler::Create(static_cast<CompositorDelegateForInput&>(*host_impl)); scoped_refptr<Layer> scroll_layer = SetupScrollLayer(); |