diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-10-06 12:48:11 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-10-13 09:33:43 +0000 |
commit | 7b5b123ac58f58ffde0f4f6e488bcd09aa4decd3 (patch) | |
tree | fa14ba0ca8d2683ba2efdabd246dc9b18a1229c6 /chromium/cc | |
parent | 79b4f909db1049fca459c07cca55af56a9b54fe3 (diff) |
BASELINE: Update Chromium to 84.0.4147.141
Change-Id: Ib85eb4cfa1cbe2b2b81e5022c8cad5c493969535
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/cc')
200 files changed, 7020 insertions, 3349 deletions
diff --git a/chromium/cc/BUILD.gn b/chromium/cc/BUILD.gn index 87888ce868a..deaca4f894b 100644 --- a/chromium/cc/BUILD.gn +++ b/chromium/cc/BUILD.gn @@ -3,6 +3,7 @@ # found in the LICENSE file. import("//build/config/sanitizers/sanitizers.gni") import("//gpu/vulkan/features.gni") +import("//skia/features.gni") import("//cc/cc.gni") @@ -42,7 +43,6 @@ cc_component("cc") { "input/page_scale_animation.h", "input/scroll_elasticity_helper.cc", "input/scroll_elasticity_helper.h", - "input/scroll_input_type.h", "input/scroll_snap_data.cc", "input/scroll_snap_data.h", "input/scroll_state.cc", @@ -161,10 +161,16 @@ cc_component("cc") { "metrics/event_metrics.h", "metrics/events_metrics_manager.cc", "metrics/events_metrics_manager.h", + "metrics/frame_sequence_metrics.cc", + "metrics/frame_sequence_metrics.h", "metrics/frame_sequence_tracker.cc", "metrics/frame_sequence_tracker.h", + "metrics/frame_sequence_tracker_collection.cc", + "metrics/frame_sequence_tracker_collection.h", "metrics/latency_ukm_reporter.cc", "metrics/latency_ukm_reporter.h", + "metrics/lcd_text_metrics_reporter.cc", + "metrics/lcd_text_metrics_reporter.h", "metrics/throughput_ukm_reporter.cc", "metrics/throughput_ukm_reporter.h", "metrics/video_playback_roughness_reporter.cc", @@ -173,6 +179,8 @@ cc_component("cc") { "raster/bitmap_raster_buffer_provider.h", "raster/gpu_raster_buffer_provider.cc", "raster/gpu_raster_buffer_provider.h", + "raster/lcd_text_disallowed_reason.cc", + "raster/lcd_text_disallowed_reason.h", "raster/one_copy_raster_buffer_provider.cc", "raster/one_copy_raster_buffer_provider.h", "raster/paint_worklet_image_provider.cc", @@ -306,6 +314,8 @@ cc_component("cc") { "trees/effect_node.h", "trees/frame_rate_counter.cc", "trees/frame_rate_counter.h", + "trees/frame_rate_estimator.cc", + "trees/frame_rate_estimator.h", "trees/image_animation_controller.cc", "trees/image_animation_controller.h", "trees/latency_info_swap_promise.cc", @@ -699,6 +709,7 @@ cc_test("cc_unittests") { "tiles/tile_priority_unittest.cc", "trees/damage_tracker_unittest.cc", "trees/draw_properties_unittest.cc", + "trees/frame_rate_estimator_unittest.cc", "trees/image_animation_controller_unittest.cc", "trees/layer_tree_frame_sink_unittest.cc", "trees/layer_tree_host_impl_unittest.cc", @@ -796,7 +807,6 @@ cc_test("cc_unittests") { defines = [] if (is_fuchsia) { - defines += [ "GL_NOT_ON_PLATFORM" ] manifest = "//build/config/fuchsia/gfx_tests.cmx" } @@ -815,6 +825,10 @@ cc_test("cc_unittests") { defines += [ "ENABLE_CC_VULKAN_TESTS" ] } } + + if (skia_use_dawn) { + defines += [ "ENABLE_CC_DAWN_TESTS" ] + } } cc_test("cc_perftests") { diff --git a/chromium/cc/animation/animation.cc b/chromium/cc/animation/animation.cc index dec58b94a00..5c40eed8453 100644 --- a/chromium/cc/animation/animation.cc +++ b/chromium/cc/animation/animation.cc @@ -114,9 +114,35 @@ void Animation::PushPropertiesTo(Animation* animation_impl) { keyframe_effect_->PushPropertiesTo(animation_impl->keyframe_effect_.get()); } -void Animation::Tick(base::TimeTicks monotonic_time) { - DCHECK(!monotonic_time.is_null()); - keyframe_effect_->Tick(monotonic_time); +void Animation::Tick(base::TimeTicks tick_time) { + DCHECK(!IsWorkletAnimation()); + if (IsScrollLinkedAnimation()) { + // blink::Animation uses its start time to calculate local time for each of + // its keyframes. However, in cc the start time is stored at the Keyframe + // level so we have to delegate the tick time to a lower level to calculate + // the local time. + // With ScrollTimeline, the start time of the animation is calculated + // differently i.e. it is not the current time at the moment of start. + // To deal with this the scroll timeline pauses the animation at its desired + // time and then ticks it which side-steps the start time altogether. See + // crbug.com/1076012 for alternative design choices considered for future + // improvement. + TickWithLocalTime(tick_time - base::TimeTicks()); + } else { + DCHECK(!tick_time.is_null()); + keyframe_effect_->Tick(tick_time); + } +} + +void Animation::TickWithLocalTime(base::TimeDelta local_time) { + // TODO(yigu): KeyframeEffect should support ticking KeyframeModel + // directly without using Pause(). https://crbug.com/1076012. + keyframe_effect_->Pause(local_time); + keyframe_effect_->Tick(base::TimeTicks()); +} + +bool Animation::IsScrollLinkedAnimation() const { + return animation_timeline_ && animation_timeline_->IsScrollTimeline(); } void Animation::UpdateState(bool start_ready_animations, diff --git a/chromium/cc/animation/animation.h b/chromium/cc/animation/animation.h index 003215c61e4..7fa4136d591 100644 --- a/chromium/cc/animation/animation.h +++ b/chromium/cc/animation/animation.h @@ -106,7 +106,8 @@ class CC_ANIMATION_EXPORT Animation : public base::RefCounted<Animation> { // Adds TIME_UPDATED event generated in the current frame to the given // animation events. virtual void TakeTimeUpdatedEvent(AnimationEvents* events) {} - virtual void Tick(base::TimeTicks monotonic_time); + virtual void Tick(base::TimeTicks tick_time); + bool IsScrollLinkedAnimation() const; void AddToTicking(); void RemoveFromTicking(); @@ -150,6 +151,7 @@ class CC_ANIMATION_EXPORT Animation : public base::RefCounted<Animation> { explicit Animation(int id); Animation(int id, std::unique_ptr<KeyframeEffect>); virtual ~Animation(); + void TickWithLocalTime(base::TimeDelta local_time); AnimationHost* animation_host_; AnimationTimeline* animation_timeline_; diff --git a/chromium/cc/animation/animation_curve.cc b/chromium/cc/animation/animation_curve.cc index b7c5891f7e4..1a46580f534 100644 --- a/chromium/cc/animation/animation_curve.cc +++ b/chromium/cc/animation/animation_curve.cc @@ -4,7 +4,7 @@ #include "cc/animation/animation_curve.h" -#include "base/logging.h" +#include "base/check.h" #include "cc/animation/scroll_offset_animation_curve.h" namespace cc { diff --git a/chromium/cc/animation/animation_host.cc b/chromium/cc/animation/animation_host.cc index ca3bfb8e707..1dfe39e66c9 100644 --- a/chromium/cc/animation/animation_host.cc +++ b/chromium/cc/animation/animation_host.cc @@ -46,19 +46,6 @@ AnimationWorkletMutationState ToAnimationWorkletMutationState( } } -bool TickAnimationsIf(AnimationHost::AnimationsList animations, - base::TimeTicks monotonic_time, - bool (*predicate)(const Animation&)) { - bool did_tick = false; - for (auto& it : animations) { - if (predicate(*it)) { - it->Tick(monotonic_time); - did_tick = true; - } - } - return did_tick; -} - } // namespace std::unique_ptr<AnimationHost> AnimationHost::CreateMainInstance() { @@ -305,6 +292,12 @@ void AnimationHost::PushPropertiesToImplThread(AnimationHost* host_impl) { host_impl->main_thread_animations_count_ = main_thread_animations_count_; host_impl->current_frame_had_raf_ = current_frame_had_raf_; host_impl->next_frame_has_pending_raf_ = next_frame_has_pending_raf_; + + // The pending info list is cleared in LayerTreeHostImpl::CommitComplete + // and should be empty when pushing properties. + DCHECK(host_impl->pending_throughput_tracker_infos_.empty()); + host_impl->pending_throughput_tracker_infos_ = + TakePendingThroughputTrackerInfos(); } scoped_refptr<ElementAnimations> @@ -404,12 +397,17 @@ bool AnimationHost::TickAnimations(base::TimeTicks monotonic_time, TRACE_EVENT_INSTANT0("cc", "NeedsTickAnimations", TRACE_EVENT_SCOPE_THREAD); - // Worklet animations are ticked at a later stage. See above comment for - // details. - bool animated = TickAnimationsIf(ticking_animations_, monotonic_time, - [](const Animation& animation) { - return !animation.IsWorkletAnimation(); - }); + bool animated = false; + for (auto& kv : id_to_timeline_map_) { + AnimationTimeline* timeline = kv.second.get(); + if (timeline->IsScrollTimeline()) { + animated |= timeline->TickScrollLinkedAnimations( + ticking_animations_, scroll_tree, is_active_tree); + } else { + animated |= timeline->TickTimeLinkedAnimations(ticking_animations_, + monotonic_time); + } + } // TODO(majidvp): At the moment we call this for both active and pending // trees similar to other animations. However our final goal is to only call @@ -431,10 +429,11 @@ void AnimationHost::TickScrollAnimations(base::TimeTicks monotonic_time, } void AnimationHost::TickWorkletAnimations() { - TickAnimationsIf(ticking_animations_, base::TimeTicks(), - [](const Animation& animation) { - return animation.IsWorkletAnimation(); - }); + for (auto& animation : ticking_animations_) { + if (!animation->IsWorkletAnimation()) + continue; + animation->Tick(base::TimeTicks()); + } } std::unique_ptr<MutatorInputState> AnimationHost::CollectWorkletAnimationsState( @@ -805,4 +804,24 @@ bool AnimationHost::NextFrameHasPendingRAF() const { return next_frame_has_pending_raf_; } +AnimationHost::PendingThroughputTrackerInfos +AnimationHost::TakePendingThroughputTrackerInfos() { + PendingThroughputTrackerInfos infos = + std::move(pending_throughput_tracker_infos_); + pending_throughput_tracker_infos_ = {}; + return infos; +} + +void AnimationHost::StartThroughputTracking( + TrackedAnimationSequenceId sequence_id) { + pending_throughput_tracker_infos_.push_back({sequence_id, true}); + SetNeedsPushProperties(); +} + +void AnimationHost::StopThroughputTracking( + TrackedAnimationSequenceId sequnece_id) { + pending_throughput_tracker_infos_.push_back({sequnece_id, false}); + SetNeedsPushProperties(); +} + } // namespace cc diff --git a/chromium/cc/animation/animation_host.h b/chromium/cc/animation/animation_host.h index 38a320ce134..de1ae6f8811 100644 --- a/chromium/cc/animation/animation_host.h +++ b/chromium/cc/animation/animation_host.h @@ -212,6 +212,12 @@ class CC_ANIMATION_EXPORT AnimationHost : public MutatorHost, bool HasCustomPropertyAnimations() const override; bool CurrentFrameHadRAF() const override; bool NextFrameHasPendingRAF() const override; + PendingThroughputTrackerInfos TakePendingThroughputTrackerInfos() override; + + // Starts/stops throughput tracking represented by |sequence_id|. + void StartThroughputTracking(TrackedAnimationSequenceId sequence_id); + void StopThroughputTracking(TrackedAnimationSequenceId sequnece_id); + void SetAnimationCounts(size_t total_animations_count, bool current_frame_had_raf, bool next_frame_has_pending_raf); @@ -268,6 +274,8 @@ class CC_ANIMATION_EXPORT AnimationHost : public MutatorHost, bool current_frame_had_raf_ = false; bool next_frame_has_pending_raf_ = false; + PendingThroughputTrackerInfos pending_throughput_tracker_infos_; + base::WeakPtrFactory<AnimationHost> weak_factory_{this}; }; diff --git a/chromium/cc/animation/animation_host_unittest.cc b/chromium/cc/animation/animation_host_unittest.cc index 0674937ce03..c2659983070 100644 --- a/chromium/cc/animation/animation_host_unittest.cc +++ b/chromium/cc/animation/animation_host_unittest.cc @@ -17,11 +17,11 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +using ::testing::_; using ::testing::InvokeWithoutArgs; using ::testing::Mock; using ::testing::NiceMock; using ::testing::Return; -using ::testing::_; namespace cc { namespace { @@ -362,5 +362,84 @@ TEST_F(AnimationHostTest, LayerTreeMutatorUpdateReflectsScrollAnimations) { false); } +TEST_F(AnimationHostTest, TickScrollLinkedAnimation) { + PropertyTrees property_trees; + property_trees.is_main_thread = false; + property_trees.is_active = true; + CreateScrollingNodeForElement(element_id_, &property_trees); + + // Create scroll timeline that links scroll animation and scroll-linked + // animation together. + auto scroll_timeline = ScrollTimeline::Create( + element_id_, ScrollTimeline::ScrollDown, 0, 100, 1000); + + int animation_id = 11; + // Create an animation that is bound to the scroll timeline. + scoped_refptr<Animation> animation = Animation::Create(animation_id); + host_impl_->AddAnimationTimeline(scroll_timeline); + scroll_timeline->AttachAnimation(animation); + animation->AddToTicking(); + ASSERT_TRUE(animation->IsScrollLinkedAnimation()); + + animation->AttachElement(element_id_); + + AddOpacityTransitionToAnimation(animation.get(), 1, .7f, .3f, true); + auto* keyframe_model = animation->GetKeyframeModel(TargetProperty::OPACITY); + EXPECT_EQ(keyframe_model->run_state(), + KeyframeModel::WAITING_FOR_TARGET_AVAILABILITY); + + auto& scroll_tree = property_trees.scroll_tree; + SetScrollOffset(&property_trees, element_id_, gfx::ScrollOffset(0, 20)); + EXPECT_TRUE(host_impl_->TickAnimations(base::TimeTicks(), + property_trees.scroll_tree, false)); + + EXPECT_EQ(keyframe_model->run_state(), KeyframeModel::PAUSED); + double tick_time = (scroll_timeline->CurrentTime(scroll_tree, false).value() - + base::TimeTicks()) + .InSecondsF(); + EXPECT_EQ(tick_time, 0.2); + + scroll_timeline->DetachAnimation(animation); + EXPECT_FALSE(host_impl_->TickAnimations(base::TimeTicks(), + property_trees.scroll_tree, false)); +} + +TEST_F(AnimationHostTest, ScrollTimelineOffsetUpdatedByScrollAnimation) { + client_.RegisterElementId(element_id_, ElementListType::ACTIVE); + client_impl_.RegisterElementId(element_id_, ElementListType::PENDING); + client_impl_.RegisterElementId(element_id_, ElementListType::ACTIVE); + host_impl_->AddAnimationTimeline(timeline_); + + PropertyTrees property_trees; + property_trees.is_main_thread = false; + property_trees.is_active = true; + CreateScrollingNodeForElement(element_id_, &property_trees); + + int animation_id = 11; + scoped_refptr<MockAnimation> mock_scroll_animation( + new MockAnimation(animation_id)); + EXPECT_CALL(*mock_scroll_animation, Tick(_)) + .WillOnce(InvokeWithoutArgs([&]() { + // Scroll to 20% of the max value. + SetScrollOffset(&property_trees, element_id_, gfx::ScrollOffset(0, 20)); + })); + + // Ensure scroll animation is ticking. + timeline_->AttachAnimation(mock_scroll_animation); + host_impl_->AddToTicking(mock_scroll_animation); + + auto scroll_timeline = ScrollTimeline::Create( + element_id_, ScrollTimeline::ScrollDown, 0, 100, 1000); + + host_impl_->TickAnimations(base::TimeTicks(), property_trees.scroll_tree, + false); + + double tick_time = + (scroll_timeline->CurrentTime(property_trees.scroll_tree, false).value() - + base::TimeTicks()) + .InSecondsF(); + EXPECT_EQ(tick_time, 0.2); +} + } // namespace } // namespace cc diff --git a/chromium/cc/animation/animation_timeline.cc b/chromium/cc/animation/animation_timeline.cc index 766257ac075..d7067c5e235 100644 --- a/chromium/cc/animation/animation_timeline.cc +++ b/chromium/cc/animation/animation_timeline.cc @@ -8,6 +8,7 @@ #include "cc/animation/animation.h" #include "cc/animation/animation_host.h" +#include "cc/trees/property_tree.h" namespace cc { @@ -65,15 +66,6 @@ Animation* AnimationTimeline::GetAnimationById(int animation_id) const { return f == id_to_animation_map_.end() ? nullptr : f->second.get(); } -std::vector<Animation*> AnimationTimeline::GetAnimations() const { - std::vector<Animation*> animations; - animations.reserve(id_to_animation_map_.size()); - - for (auto& kv : id_to_animation_map_) - animations.push_back(kv.second.get()); - return animations; -} - void AnimationTimeline::ClearAnimations() { for (auto& kv : id_to_animation_map_) EraseAnimation(kv.second); @@ -82,6 +74,36 @@ void AnimationTimeline::ClearAnimations() { SetNeedsPushProperties(); } +bool AnimationTimeline::TickTimeLinkedAnimations( + const std::vector<scoped_refptr<Animation>>& ticking_animations, + base::TimeTicks monotonic_time) { + DCHECK(!IsScrollTimeline()); + + bool animated = false; + for (auto& animation : ticking_animations) { + if (animation->animation_timeline() != this) + continue; + // Worklet animations are ticked separately by AnimationHost. + if (animation->IsWorkletAnimation()) + continue; + + // Scroll-linked animations are ticked separately. + if (animation->IsScrollLinkedAnimation()) + continue; + + animation->Tick(monotonic_time); + animated = true; + } + return animated; +} + +bool AnimationTimeline::TickScrollLinkedAnimations( + const std::vector<scoped_refptr<Animation>>& ticking_animations, + const ScrollTree& scroll_tree, + bool is_active_tree) { + return false; +} + void AnimationTimeline::SetNeedsPushProperties() { needs_push_properties_ = true; if (animation_host_) diff --git a/chromium/cc/animation/animation_timeline.h b/chromium/cc/animation/animation_timeline.h index 9c266e1b93d..6ff994afa56 100644 --- a/chromium/cc/animation/animation_timeline.h +++ b/chromium/cc/animation/animation_timeline.h @@ -15,6 +15,7 @@ namespace cc { class Animation; class AnimationHost; +class ScrollTree; // An AnimationTimeline owns a group of Animations. // @@ -42,9 +43,15 @@ class CC_ANIMATION_EXPORT AnimationTimeline void AttachAnimation(scoped_refptr<Animation> animation); void DetachAnimation(scoped_refptr<Animation> animation); - std::vector<Animation*> GetAnimations() const; void ClearAnimations(); bool HasAnimation() const { return !id_to_animation_map_.empty(); } + bool TickTimeLinkedAnimations( + const std::vector<scoped_refptr<Animation>>& ticking_animations, + base::TimeTicks monotonic_time); + virtual bool TickScrollLinkedAnimations( + const std::vector<scoped_refptr<Animation>>& ticking_animations, + const ScrollTree& scroll_tree, + bool is_active_tree); virtual void PushPropertiesTo(AnimationTimeline* timeline_impl); virtual void ActivateTimeline() {} diff --git a/chromium/cc/animation/keyframe_effect.cc b/chromium/cc/animation/keyframe_effect.cc index 4c14471e845..5af52eb211b 100644 --- a/chromium/cc/animation/keyframe_effect.cc +++ b/chromium/cc/animation/keyframe_effect.cc @@ -168,7 +168,7 @@ void KeyframeEffect::RemoveFromTicking() { is_ticking_ = false; // Resetting last_tick_time_ here ensures that calling ::UpdateState // before ::Animate doesn't start a keyframe model. - last_tick_time_ = base::TimeTicks(); + last_tick_time_ = base::nullopt; animation_->RemoveFromTicking(); } @@ -178,19 +178,20 @@ void KeyframeEffect::UpdateState(bool start_ready_keyframe_models, // Animate hasn't been called, this happens if an element has been added // between the Commit and Draw phases. - if (last_tick_time_ == base::TimeTicks()) + if (last_tick_time_ == base::nullopt) start_ready_keyframe_models = false; if (start_ready_keyframe_models) PromoteStartedKeyframeModels(events); - MarkFinishedKeyframeModels(last_tick_time_); - MarkKeyframeModelsForDeletion(last_tick_time_, events); + auto last_tick_time = last_tick_time_.value_or(base::TimeTicks()); + MarkFinishedKeyframeModels(last_tick_time); + MarkKeyframeModelsForDeletion(last_tick_time, events); PurgeKeyframeModelsMarkedForDeletion(/* impl_only */ true); if (start_ready_keyframe_models) { if (needs_to_start_keyframe_models_) { - StartKeyframeModels(last_tick_time_); + StartKeyframeModels(last_tick_time); PromoteStartedKeyframeModels(events); } } @@ -313,7 +314,8 @@ void KeyframeEffect::RemoveKeyframeModel(int keyframe_model_id) { void KeyframeEffect::AbortKeyframeModel(int keyframe_model_id) { if (KeyframeModel* keyframe_model = GetKeyframeModelById(keyframe_model_id)) { if (!keyframe_model->is_finished()) { - keyframe_model->SetRunState(KeyframeModel::ABORTED, last_tick_time_); + keyframe_model->SetRunState(KeyframeModel::ABORTED, + last_tick_time_.value_or(base::TimeTicks())); if (has_bound_element_animations()) element_animations_->UpdateClientAnimationState(); } @@ -338,10 +340,13 @@ void KeyframeEffect::AbortKeyframeModelsWithProperty( // Currently only impl-only scroll offset KeyframeModels can be completed // on the main thread. if (needs_completion && keyframe_model->is_impl_only()) { - keyframe_model->SetRunState(KeyframeModel::ABORTED_BUT_NEEDS_COMPLETION, - last_tick_time_); + keyframe_model->SetRunState( + KeyframeModel::ABORTED_BUT_NEEDS_COMPLETION, + last_tick_time_.value_or(base::TimeTicks())); } else { - keyframe_model->SetRunState(KeyframeModel::ABORTED, last_tick_time_); + keyframe_model->SetRunState( + KeyframeModel::ABORTED, + last_tick_time_.value_or(base::TimeTicks())); } aborted_keyframe_model = true; } @@ -575,7 +580,7 @@ bool KeyframeEffect::IsCurrentlyAnimatingProperty( ElementListType list_type) const { for (const auto& keyframe_model : keyframe_models_) { if (!keyframe_model->is_finished() && - keyframe_model->InEffect(last_tick_time_) && + keyframe_model->InEffect(last_tick_time_.value_or(base::TimeTicks())) && keyframe_model->target_property_id() == target_property) { if ((list_type == ElementListType::ACTIVE && keyframe_model->affects_active_elements()) || @@ -613,7 +618,8 @@ void KeyframeEffect::GetPropertyAnimationState( for (const auto& keyframe_model : keyframe_models_) { if (!keyframe_model->is_finished()) { - bool in_effect = keyframe_model->InEffect(last_tick_time_); + bool in_effect = + keyframe_model->InEffect(last_tick_time_.value_or(base::TimeTicks())); bool active = keyframe_model->affects_active_elements(); bool pending = keyframe_model->affects_pending_elements(); int property = keyframe_model->target_property_id(); @@ -642,10 +648,12 @@ void KeyframeEffect::MarkAbortedKeyframeModelsForDeletion( if (KeyframeModel* keyframe_model = GetKeyframeModelById(keyframe_model_impl->id())) { if (keyframe_model->run_state() == KeyframeModel::ABORTED) { - keyframe_model_impl->SetRunState(KeyframeModel::WAITING_FOR_DELETION, - keyframe_effect_impl->last_tick_time_); - keyframe_model->SetRunState(KeyframeModel::WAITING_FOR_DELETION, - last_tick_time_); + keyframe_model_impl->SetRunState( + KeyframeModel::WAITING_FOR_DELETION, + keyframe_effect_impl->last_tick_time_.value_or(base::TimeTicks())); + keyframe_model->SetRunState( + KeyframeModel::WAITING_FOR_DELETION, + last_tick_time_.value_or(base::TimeTicks())); keyframe_model_aborted = true; } } @@ -911,16 +919,18 @@ void KeyframeEffect::PromoteStartedKeyframeModels(AnimationEvents* events) { for (auto& keyframe_model : keyframe_models_) { if (keyframe_model->run_state() == KeyframeModel::STARTING && keyframe_model->affects_active_elements()) { - keyframe_model->SetRunState(KeyframeModel::RUNNING, last_tick_time_); + keyframe_model->SetRunState(KeyframeModel::RUNNING, + last_tick_time_.value_or(base::TimeTicks())); if (!keyframe_model->has_set_start_time() && !keyframe_model->needs_synchronized_start_time()) - keyframe_model->set_start_time(last_tick_time_); + keyframe_model->set_start_time( + last_tick_time_.value_or(base::TimeTicks())); base::TimeTicks start_time; if (keyframe_model->has_set_start_time()) start_time = keyframe_model->start_time(); else - start_time = last_tick_time_; + start_time = last_tick_time_.value_or(base::TimeTicks()); GenerateEvent(events, *keyframe_model, AnimationEvent::STARTED, start_time); diff --git a/chromium/cc/animation/keyframe_effect.h b/chromium/cc/animation/keyframe_effect.h index 1d2655395f0..e7806e4cc1d 100644 --- a/chromium/cc/animation/keyframe_effect.h +++ b/chromium/cc/animation/keyframe_effect.h @@ -187,7 +187,7 @@ class CC_ANIMATION_EXPORT KeyframeEffect { bool scroll_offset_animation_was_interrupted_; bool is_ticking_; - base::TimeTicks last_tick_time_; + base::Optional<base::TimeTicks> last_tick_time_; bool needs_push_properties_; }; diff --git a/chromium/cc/animation/scroll_offset_animation_curve.cc b/chromium/cc/animation/scroll_offset_animation_curve.cc index d6a3000c31f..2914ce4c193 100644 --- a/chromium/cc/animation/scroll_offset_animation_curve.cc +++ b/chromium/cc/animation/scroll_offset_animation_curve.cc @@ -7,7 +7,7 @@ #include <algorithm> #include <cmath> -#include "base/logging.h" +#include "base/check_op.h" #include "base/memory/ptr_util.h" #include "base/numerics/ranges.h" #include "cc/animation/timing_function.h" diff --git a/chromium/cc/animation/scroll_timeline.cc b/chromium/cc/animation/scroll_timeline.cc index 914381524f0..a3d7e9439aa 100644 --- a/chromium/cc/animation/scroll_timeline.cc +++ b/chromium/cc/animation/scroll_timeline.cc @@ -155,6 +155,38 @@ void ScrollTimeline::ActivateTimeline() { } } +bool ScrollTimeline::TickScrollLinkedAnimations( + const std::vector<scoped_refptr<Animation>>& ticking_animations, + const ScrollTree& scroll_tree, + bool is_active_tree) { + base::Optional<base::TimeTicks> tick_time = + CurrentTime(scroll_tree, is_active_tree); + if (!tick_time) + return false; + + bool animated = false; + // This potentially iterates over all ticking animations multiple + // times (# of ScrollTimeline * # of ticking_animations_). + // The alternative we have considered here was to maintain a + // ticking_animations_ list for each timeline but at the moment we + // have opted to avoid this complexity in favor of simpler but less + // efficient solution. + for (auto& animation : ticking_animations) { + if (animation->animation_timeline() != this) + continue; + // Worklet animations are ticked at a later stage. + if (animation->IsWorkletAnimation()) + continue; + + if (!animation->IsScrollLinkedAnimation()) + continue; + + animation->Tick(tick_time.value()); + animated = true; + } + return animated; +} + void ScrollTimeline::UpdateScrollerIdAndScrollOffsets( base::Optional<ElementId> pending_id, base::Optional<double> start_scroll_offset, diff --git a/chromium/cc/animation/scroll_timeline.h b/chromium/cc/animation/scroll_timeline.h index 0aee63a239a..fa917824b7b 100644 --- a/chromium/cc/animation/scroll_timeline.h +++ b/chromium/cc/animation/scroll_timeline.h @@ -73,6 +73,11 @@ class CC_ANIMATION_EXPORT ScrollTimeline : public AnimationTimeline { void PushPropertiesTo(AnimationTimeline* impl_timeline) override; void ActivateTimeline() override; + bool TickScrollLinkedAnimations( + const std::vector<scoped_refptr<Animation>>& ticking_animations, + const ScrollTree& scroll_tree, + bool is_active_tree) override; + base::Optional<ElementId> GetActiveIdForTest() const { return active_id_; } base::Optional<ElementId> GetPendingIdForTest() const { return pending_id_; } ScrollDirection GetDirectionForTest() const { return direction_; } diff --git a/chromium/cc/animation/timing_function.cc b/chromium/cc/animation/timing_function.cc index d3a66810330..8906be4d9f7 100644 --- a/chromium/cc/animation/timing_function.cc +++ b/chromium/cc/animation/timing_function.cc @@ -7,8 +7,9 @@ #include <cmath> #include <memory> -#include "base/logging.h" +#include "base/check_op.h" #include "base/memory/ptr_util.h" +#include "base/notreached.h" namespace cc { diff --git a/chromium/cc/animation/transform_operation.cc b/chromium/cc/animation/transform_operation.cc index f0dcc7723e9..1bb8996d953 100644 --- a/chromium/cc/animation/transform_operation.cc +++ b/chromium/cc/animation/transform_operation.cc @@ -5,7 +5,8 @@ #include <algorithm> #include <limits> -#include "base/logging.h" +#include "base/check_op.h" +#include "base/notreached.h" #include "base/numerics/math_constants.h" #include "base/numerics/ranges.h" #include "cc/animation/transform_operation.h" diff --git a/chromium/cc/animation/worklet_animation.cc b/chromium/cc/animation/worklet_animation.cc index 54bb27f6e50..17ed083efcc 100644 --- a/chromium/cc/animation/worklet_animation.cc +++ b/chromium/cc/animation/worklet_animation.cc @@ -92,8 +92,7 @@ void WorkletAnimation::Tick(base::TimeTicks monotonic_time) { // animations lifecycle. To avoid this we pause the underlying keyframe effect // at the local time obtained from the user script - essentially turning each // call to |WorkletAnimation::Tick| into a seek in the effect. - keyframe_effect_->Pause(local_time_.value()); - keyframe_effect_->Tick(monotonic_time); + TickWithLocalTime(local_time_.value()); } void WorkletAnimation::UpdateState(bool start_ready_animations, diff --git a/chromium/cc/base/features.cc b/chromium/cc/base/features.cc index 7528a58827a..8c0c599f83d 100644 --- a/chromium/cc/base/features.cc +++ b/chromium/cc/base/features.cc @@ -47,4 +47,7 @@ bool IsMainLatencyRecoveryEnabled() { #endif } +const base::Feature kScrollUnification{"ScrollUnification", + base::FEATURE_DISABLED_BY_DEFAULT}; + } // namespace features diff --git a/chromium/cc/base/features.h b/chromium/cc/base/features.h index 01659128cb2..56c620c6eea 100644 --- a/chromium/cc/base/features.h +++ b/chromium/cc/base/features.h @@ -22,6 +22,14 @@ CC_BASE_EXPORT extern const base::Feature kMainLatencyRecovery; CC_BASE_EXPORT bool IsImplLatencyRecoveryEnabled(); CC_BASE_EXPORT bool IsMainLatencyRecoveryEnabled(); +// When enabled, all scrolling is performed on the compositor thread - +// delegating only the hit test to Blink. This causes Blink to send additional +// information in the scroll property tree. When a scroll can't be hit tested +// on the compositor, it will post a hit test task to Blink and continue the +// scroll when that resolves. For details, see: +// https://docs.google.com/document/d/1smLAXs-DSLLmkEt4FIPP7PVglJXOcwRc7A5G0SEwxaY/edit +CC_BASE_EXPORT extern const base::Feature kScrollUnification; + } // namespace features #endif // CC_BASE_FEATURES_H_ diff --git a/chromium/cc/base/list_container_helper.cc b/chromium/cc/base/list_container_helper.cc index 3a4c0b6a2f9..afd386e3a88 100644 --- a/chromium/cc/base/list_container_helper.cc +++ b/chromium/cc/base/list_container_helper.cc @@ -9,7 +9,7 @@ #include <algorithm> #include <vector> -#include "base/logging.h" +#include "base/check_op.h" #include "base/memory/aligned_memory.h" namespace { diff --git a/chromium/cc/base/reverse_spiral_iterator.cc b/chromium/cc/base/reverse_spiral_iterator.cc index a0d836878dd..0aca645c247 100644 --- a/chromium/cc/base/reverse_spiral_iterator.cc +++ b/chromium/cc/base/reverse_spiral_iterator.cc @@ -6,6 +6,8 @@ #include <algorithm> +#include "base/check_op.h" + namespace cc { ReverseSpiralIterator::ReverseSpiralIterator() diff --git a/chromium/cc/base/simple_enclosed_region.cc b/chromium/cc/base/simple_enclosed_region.cc index 00c58abaae0..24227a0b20c 100644 --- a/chromium/cc/base/simple_enclosed_region.cc +++ b/chromium/cc/base/simple_enclosed_region.cc @@ -7,7 +7,7 @@ #include <stddef.h> #include <stdint.h> -#include "base/logging.h" +#include "base/check_op.h" #include "cc/base/region.h" #include "ui/gfx/geometry/rect.h" diff --git a/chromium/cc/base/spiral_iterator.cc b/chromium/cc/base/spiral_iterator.cc index 7d7387bd55e..30a06a4904f 100644 --- a/chromium/cc/base/spiral_iterator.cc +++ b/chromium/cc/base/spiral_iterator.cc @@ -4,6 +4,8 @@ #include "cc/base/spiral_iterator.h" +#include "base/check_op.h" + #include <algorithm> namespace cc { diff --git a/chromium/cc/base/switches.cc b/chromium/cc/base/switches.cc index 57409ca3d62..e7e8704263d 100644 --- a/chromium/cc/base/switches.cc +++ b/chromium/cc/base/switches.cc @@ -48,10 +48,15 @@ const char kCheckDamageEarly[] = "check-damage-early"; // Enables the GPU benchmarking extension const char kEnableGpuBenchmarking[] = "enable-gpu-benchmarking"; +// Disables LayerTreeHost::OnMemoryPressure +const char kDisableLayerTreeHostMemoryPressure[] = + "disable-layer-tree-host-memory-pressure"; + // Renders a border around compositor layers to help debug and study // layer compositing. const char kShowCompositedLayerBorders[] = "show-composited-layer-borders"; const char kUIShowCompositedLayerBorders[] = "ui-show-composited-layer-borders"; +// Parameters for kUIShowCompositedLayerBorders. const char kCompositedRenderPassBorders[] = "renderpass"; const char kCompositedSurfaceBorders[] = "surface"; const char kCompositedLayerBorders[] = "layer"; @@ -85,6 +90,10 @@ const char kUIShowSurfaceDamageRects[] = "ui-show-surface-damage-rects"; const char kShowScreenSpaceRects[] = "show-screenspace-rects"; const char kUIShowScreenSpaceRects[] = "ui-show-screenspace-rects"; +// Highlights layers that can't use lcd text. Layers containing no text won't +// be highlighted. See DebugColors::NonLCDTextHighlightColor() for the colors. +const char kHighlightNonLCDTextLayers[] = "highlight-non-lcd-text-layers"; + // Switches the ui compositor to use layer lists instead of layer trees. const char kUIEnableLayerLists[] = "ui-enable-layer-lists"; diff --git a/chromium/cc/base/switches.h b/chromium/cc/base/switches.h index d3a9d37d839..7b6c6b398e0 100644 --- a/chromium/cc/base/switches.h +++ b/chromium/cc/base/switches.h @@ -31,6 +31,9 @@ CC_BASE_EXPORT extern const char kCheckDamageEarly[]; // Switches for both the renderer and ui compositors. CC_BASE_EXPORT extern const char kEnableGpuBenchmarking[]; +// Switches for LayerTreeHost. +CC_BASE_EXPORT extern const char kDisableLayerTreeHostMemoryPressure[]; + // Debug visualizations. CC_BASE_EXPORT extern const char kShowCompositedLayerBorders[]; CC_BASE_EXPORT extern const char kUIShowCompositedLayerBorders[]; @@ -44,14 +47,18 @@ CC_BASE_EXPORT extern const char kShowSurfaceDamageRects[]; CC_BASE_EXPORT extern const char kUIShowSurfaceDamageRects[]; CC_BASE_EXPORT extern const char kShowScreenSpaceRects[]; CC_BASE_EXPORT extern const char kUIShowScreenSpaceRects[]; -CC_BASE_EXPORT extern const char kUIEnableLayerLists[]; -CC_BASE_EXPORT extern const char kCompositedRenderPassBorders[]; -CC_BASE_EXPORT extern const char kCompositedSurfaceBorders[]; -CC_BASE_EXPORT extern const char kCompositedLayerBorders[]; +CC_BASE_EXPORT extern const char kHighlightNonLCDTextLayers[]; #if DCHECK_IS_ON() CC_BASE_EXPORT extern const char kLogOnUIDoubleBackgroundBlur[]; #endif +// Parameters for kUIShowCompositedLayerBorders. +CC_BASE_EXPORT extern const char kCompositedRenderPassBorders[]; +CC_BASE_EXPORT extern const char kCompositedSurfaceBorders[]; +CC_BASE_EXPORT extern const char kCompositedLayerBorders[]; + +CC_BASE_EXPORT extern const char kUIEnableLayerLists[]; + // Test related. CC_BASE_EXPORT extern const char kCCLayerTreeTestNoTimeout[]; CC_BASE_EXPORT extern const char kCCLayerTreeTestLongTimeout[]; diff --git a/chromium/cc/base/tiling_data.cc b/chromium/cc/base/tiling_data.cc index b17338e5c5c..deb55701417 100644 --- a/chromium/cc/base/tiling_data.cc +++ b/chromium/cc/base/tiling_data.cc @@ -6,6 +6,8 @@ #include <algorithm> +#include "base/check_op.h" +#include "base/notreached.h" #include "base/numerics/ranges.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/rect_f.h" diff --git a/chromium/cc/benchmarks/micro_benchmark.cc b/chromium/cc/benchmarks/micro_benchmark.cc index 5f23e66ee37..4479e8abb40 100644 --- a/chromium/cc/benchmarks/micro_benchmark.cc +++ b/chromium/cc/benchmarks/micro_benchmark.cc @@ -7,7 +7,7 @@ #include <memory> #include "base/callback.h" -#include "base/logging.h" +#include "base/check.h" #include "base/memory/ptr_util.h" #include "base/single_thread_task_runner.h" #include "base/values.h" diff --git a/chromium/cc/benchmarks/rasterize_and_record_benchmark_impl.cc b/chromium/cc/benchmarks/rasterize_and_record_benchmark_impl.cc index 609b925b264..84a5e330c6c 100644 --- a/chromium/cc/benchmarks/rasterize_and_record_benchmark_impl.cc +++ b/chromium/cc/benchmarks/rasterize_and_record_benchmark_impl.cc @@ -13,6 +13,7 @@ #include "base/values.h" #include "cc/layers/layer_impl.h" #include "cc/layers/picture_layer_impl.h" +#include "cc/paint/display_item_list.h" #include "cc/raster/playback_image_provider.h" #include "cc/raster/raster_buffer_provider.h" #include "cc/trees/layer_tree_host_impl.h" @@ -166,6 +167,17 @@ void RasterizeAndRecordBenchmarkImpl::DidCompleteCommit( result->SetInteger("total_picture_layers_off_screen", rasterize_results_.total_picture_layers_off_screen); + std::unique_ptr<base::DictionaryValue> lcd_text_pixels( + new base::DictionaryValue()); + for (size_t i = 0; i < kLCDTextDisallowedReasonCount; i++) { + lcd_text_pixels->SetInteger( + LCDTextDisallowedReasonToString( + static_cast<LCDTextDisallowedReason>(i)), + rasterize_results_.visible_pixels_by_lcd_text_disallowed_reason[i]); + } + result->SetDictionary("visible_pixels_by_lcd_text_disallowed_reason", + std::move(lcd_text_pixels)); + NotifyDone(std::move(result)); } @@ -180,6 +192,13 @@ void RasterizeAndRecordBenchmarkImpl::RunOnLayer(PictureLayerImpl* layer) { return; } + int text_pixels = + layer->GetRasterSource()->GetDisplayItemList()->AreaOfDrawText( + layer->visible_layer_rect()); + rasterize_results_ + .visible_pixels_by_lcd_text_disallowed_reason[static_cast<size_t>( + layer->lcd_text_disallowed_reason())] += text_pixels; + FixedInvalidationPictureLayerTilingClient client(layer, gfx::Rect(layer->bounds())); @@ -230,6 +249,7 @@ RasterizeAndRecordBenchmarkImpl::RasterizeResults::RasterizeResults() : pixels_rasterized(0), pixels_rasterized_with_non_solid_color(0), pixels_rasterized_as_opaque(0), + visible_pixels_by_lcd_text_disallowed_reason{0}, total_layers(0), total_picture_layers(0), total_picture_layers_with_no_content(0), diff --git a/chromium/cc/benchmarks/rasterize_and_record_benchmark_impl.h b/chromium/cc/benchmarks/rasterize_and_record_benchmark_impl.h index be8a6de987c..a4ac27614a5 100644 --- a/chromium/cc/benchmarks/rasterize_and_record_benchmark_impl.h +++ b/chromium/cc/benchmarks/rasterize_and_record_benchmark_impl.h @@ -14,6 +14,7 @@ #include "base/single_thread_task_runner.h" #include "base/time/time.h" #include "cc/benchmarks/micro_benchmark_impl.h" +#include "cc/raster/lcd_text_disallowed_reason.h" #include "cc/raster/task_graph_runner.h" namespace cc { @@ -41,6 +42,8 @@ class RasterizeAndRecordBenchmarkImpl : public MicroBenchmarkImpl { int pixels_rasterized; int pixels_rasterized_with_non_solid_color; int pixels_rasterized_as_opaque; + int visible_pixels_by_lcd_text_disallowed_reason + [kLCDTextDisallowedReasonCount]; base::TimeDelta total_best_time; int total_layers; int total_picture_layers; diff --git a/chromium/cc/debug/BUILD.gn b/chromium/cc/debug/BUILD.gn index 2bb36bfd8fa..4a046e990a2 100644 --- a/chromium/cc/debug/BUILD.gn +++ b/chromium/cc/debug/BUILD.gn @@ -6,6 +6,8 @@ import("//cc/cc.gni") cc_component("debug") { output_name = "cc_debug" sources = [ + # For enum LCDTextDisallowedReason used in debug_colors.*. + "../raster/lcd_text_disallowed_reason.h", "debug_colors.cc", "debug_colors.h", "debug_export.h", diff --git a/chromium/cc/debug/debug_colors.cc b/chromium/cc/debug/debug_colors.cc index 76fa7258618..1401b47b0d9 100644 --- a/chromium/cc/debug/debug_colors.cc +++ b/chromium/cc/debug/debug_colors.cc @@ -2,10 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/logging.h" - #include "cc/debug/debug_colors.h" +#include "base/check_op.h" +#include "base/notreached.h" + namespace cc { static float Scale(float width, float device_scale_factor) { @@ -321,4 +322,26 @@ SkColor DebugColors::PaintTimeDisplayTextAndGraphColor() { return SkColorSetRGB(75, 155, 55); } +SkColor DebugColors::NonLCDTextHighlightColor(LCDTextDisallowedReason reason) { + switch (reason) { + case LCDTextDisallowedReason::kNone: + return SK_ColorTRANSPARENT; + case LCDTextDisallowedReason::kSetting: + return SkColorSetARGB(96, 128, 255, 0); + case LCDTextDisallowedReason::kBackgroundColorNotOpaque: + return SkColorSetARGB(96, 128, 128, 0); + case LCDTextDisallowedReason::kContentsNotOpaque: + return SkColorSetARGB(96, 255, 0, 0); + case LCDTextDisallowedReason::kNonIntegralTranslation: + return SkColorSetARGB(96, 255, 128, 0); + case LCDTextDisallowedReason::kNonIntegralXOffset: + case LCDTextDisallowedReason::kNonIntegralYOffset: + return SkColorSetARGB(96, 255, 0, 128); + case LCDTextDisallowedReason::kWillChangeTransform: + return SkColorSetARGB(96, 128, 0, 255); + } + NOTREACHED(); + return SK_ColorTRANSPARENT; +} + } // namespace cc diff --git a/chromium/cc/debug/debug_colors.h b/chromium/cc/debug/debug_colors.h index 97e6688bccd..9718669b30b 100644 --- a/chromium/cc/debug/debug_colors.h +++ b/chromium/cc/debug/debug_colors.h @@ -7,6 +7,7 @@ #include "base/containers/span.h" #include "cc/debug/debug_export.h" +#include "cc/raster/lcd_text_disallowed_reason.h" #include "third_party/skia/include/core/SkColor.h" namespace cc { @@ -125,6 +126,8 @@ class CC_DEBUG_EXPORT DebugColors { static SkColor FPSDisplayTextAndGraphColor(); static SkColor MemoryDisplayTextColor(); static SkColor PaintTimeDisplayTextAndGraphColor(); + + static SkColor NonLCDTextHighlightColor(LCDTextDisallowedReason); }; } // namespace cc diff --git a/chromium/cc/debug/layer_tree_debug_state.cc b/chromium/cc/debug/layer_tree_debug_state.cc index 66d1e134c43..0ce673525cf 100644 --- a/chromium/cc/debug/layer_tree_debug_state.cc +++ b/chromium/cc/debug/layer_tree_debug_state.cc @@ -4,7 +4,6 @@ #include "cc/debug/layer_tree_debug_state.h" -#include "base/logging.h" namespace cc { @@ -25,7 +24,7 @@ LayerTreeDebugState::LayerTreeDebugState() show_layer_animation_bounds_rects(false), slow_down_raster_scale_factor(0), rasterize_only_visible_content(false), - show_picture_borders(false), + highlight_non_lcd_text_layers(false), show_hit_test_borders(false), record_rendering_stats_(false) {} @@ -79,7 +78,7 @@ bool LayerTreeDebugState::Equal(const LayerTreeDebugState& a, b.show_layer_animation_bounds_rects && a.slow_down_raster_scale_factor == b.slow_down_raster_scale_factor && a.rasterize_only_visible_content == b.rasterize_only_visible_content && - a.show_picture_borders == b.show_picture_borders && + a.highlight_non_lcd_text_layers == b.highlight_non_lcd_text_layers && a.show_hit_test_borders == b.show_hit_test_borders && a.record_rendering_stats_ == b.record_rendering_stats_); } @@ -107,7 +106,7 @@ LayerTreeDebugState LayerTreeDebugState::Unite(const LayerTreeDebugState& a, if (b.slow_down_raster_scale_factor) r.slow_down_raster_scale_factor = b.slow_down_raster_scale_factor; r.rasterize_only_visible_content |= b.rasterize_only_visible_content; - r.show_picture_borders |= b.show_picture_borders; + r.highlight_non_lcd_text_layers |= b.highlight_non_lcd_text_layers; r.show_hit_test_borders |= b.show_hit_test_borders; diff --git a/chromium/cc/debug/layer_tree_debug_state.h b/chromium/cc/debug/layer_tree_debug_state.h index db18711a3f0..95b79cda95d 100644 --- a/chromium/cc/debug/layer_tree_debug_state.h +++ b/chromium/cc/debug/layer_tree_debug_state.h @@ -46,7 +46,7 @@ class CC_DEBUG_EXPORT LayerTreeDebugState { int slow_down_raster_scale_factor; bool rasterize_only_visible_content; - bool show_picture_borders; + bool highlight_non_lcd_text_layers; bool show_hit_test_borders; diff --git a/chromium/cc/debug/picture_debug_util.cc b/chromium/cc/debug/picture_debug_util.cc index 86ca1c6b00f..9b8feaadcde 100644 --- a/chromium/cc/debug/picture_debug_util.cc +++ b/chromium/cc/debug/picture_debug_util.cc @@ -11,7 +11,6 @@ #include <vector> #include "base/base64.h" -#include "base/logging.h" #include "third_party/skia/include/core/SkData.h" #include "third_party/skia/include/core/SkPicture.h" #include "ui/gfx/codec/jpeg_codec.h" diff --git a/chromium/cc/input/browser_controls_offset_manager.cc b/chromium/cc/input/browser_controls_offset_manager.cc index 248ad7bf66b..ea4c6e27993 100644 --- a/chromium/cc/input/browser_controls_offset_manager.cc +++ b/chromium/cc/input/browser_controls_offset_manager.cc @@ -8,7 +8,7 @@ #include <algorithm> -#include "base/logging.h" +#include "base/check_op.h" #include "base/memory/ptr_util.h" #include "base/numerics/ranges.h" #include "cc/input/browser_controls_offset_manager_client.h" diff --git a/chromium/cc/input/browser_controls_offset_manager_unittest.cc b/chromium/cc/input/browser_controls_offset_manager_unittest.cc index 6dfaefd4843..8c443fd0bee 100644 --- a/chromium/cc/input/browser_controls_offset_manager_unittest.cc +++ b/chromium/cc/input/browser_controls_offset_manager_unittest.cc @@ -8,7 +8,6 @@ #include <cmath> #include <memory> -#include "base/logging.h" #include "base/time/time.h" #include "cc/input/browser_controls_offset_manager_client.h" #include "cc/layers/layer_impl.h" diff --git a/chromium/cc/input/input_handler.h b/chromium/cc/input/input_handler.h index c031f16b0b7..eaaa9afde45 100644 --- a/chromium/cc/input/input_handler.h +++ b/chromium/cc/input/input_handler.h @@ -12,7 +12,6 @@ #include "cc/input/event_listener_properties.h" #include "cc/input/main_thread_scrolling_reason.h" #include "cc/input/overscroll_behavior.h" -#include "cc/input/scroll_input_type.h" #include "cc/input/scroll_state.h" #include "cc/input/scrollbar.h" #include "cc/input/touch_action.h" @@ -20,6 +19,7 @@ #include "cc/paint/element_id.h" #include "cc/trees/swap_promise_monitor.h" #include "components/viz/common/frame_sinks/begin_frame_args.h" +#include "ui/events/types/scroll_input_type.h" #include "ui/events/types/scroll_types.h" namespace gfx { @@ -171,12 +171,12 @@ class CC_EXPORT InputHandler { // SCROLL_IGNORED if there is nothing to be scrolled at the given // coordinates. virtual ScrollStatus ScrollBegin(ScrollState* scroll_state, - ScrollInputType type) = 0; + ui::ScrollInputType type) = 0; // Similar to ScrollBegin, except the hit test is skipped and scroll always // targets at the root layer. virtual ScrollStatus RootScrollBegin(ScrollState* scroll_state, - ScrollInputType type) = 0; + ui::ScrollInputType type) = 0; // Scroll the layer selected by |ScrollBegin| by given |scroll_state| delta. // Internally, the delta is transformed to local layer's coordinate space for @@ -203,9 +203,9 @@ class CC_EXPORT InputHandler { // Called to notify every time scroll-begin/end is attempted by an input // event. - virtual void RecordScrollBegin(ScrollInputType input_type, + virtual void RecordScrollBegin(ui::ScrollInputType input_type, ScrollBeginThreadState scroll_start_state) = 0; - virtual void RecordScrollEnd(ScrollInputType input_type) = 0; + virtual void RecordScrollEnd(ui::ScrollInputType input_type) = 0; virtual InputHandlerPointerResult MouseMoveAt( const gfx::Point& mouse_position) = 0; @@ -243,10 +243,6 @@ class CC_EXPORT InputHandler { // Returns true if there is an active scroll on the viewport. virtual bool IsCurrentlyScrollingViewport() const = 0; - // Whether the layer under |viewport_point| is the currently scrolling layer. - virtual bool IsCurrentlyScrollingLayerAt( - const gfx::Point& viewport_point) const = 0; - virtual EventListenerProperties GetEventListenerProperties( EventListenerClass event_class) const = 0; @@ -279,9 +275,10 @@ class CC_EXPORT InputHandler { // During the lifetime of the returned EventsMetricsManager::ScopedMonitor, if // SetNeedsOneBeginImplFrame() or SetNeedsRedraw() are called on // LayerTreeHostImpl or a scroll animation is updated, |event_metrics| will be - // saved for reporting event latency metrics. + // saved for reporting event latency metrics. It is allowed to pass nullptr as + // |event_metrics| in which case the return value would also be nullptr. virtual std::unique_ptr<EventsMetricsManager::ScopedMonitor> - GetScopedEventMetricsMonitor(const EventMetrics& event_metrics) = 0; + GetScopedEventMetricsMonitor(std::unique_ptr<EventMetrics> event_metrics) = 0; virtual ScrollElasticityHelper* CreateScrollElasticityHelper() = 0; @@ -309,6 +306,10 @@ class CC_EXPORT InputHandler { // it wasn't aborted). virtual void ScrollEndForSnapFling(bool did_finish) = 0; + // Notifies when any input event is received, irrespective of whether it is + // being handled by the InputHandler or not. + virtual void NotifyInputEvent() = 0; + protected: InputHandler() = default; virtual ~InputHandler() = default; diff --git a/chromium/cc/input/layer_selection_bound.cc b/chromium/cc/input/layer_selection_bound.cc index 81af0bf7d32..7de8f2991c3 100644 --- a/chromium/cc/input/layer_selection_bound.cc +++ b/chromium/cc/input/layer_selection_bound.cc @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/logging.h" #include "base/strings/stringprintf.h" #include "cc/input/layer_selection_bound.h" diff --git a/chromium/cc/input/main_thread_scrolling_reason.cc b/chromium/cc/input/main_thread_scrolling_reason.cc index 56e1d58d4fa..b84c31dd04c 100644 --- a/chromium/cc/input/main_thread_scrolling_reason.cc +++ b/chromium/cc/input/main_thread_scrolling_reason.cc @@ -42,16 +42,14 @@ void MainThreadScrollingReason::AddToTracedValue( traced_value.AppendString("Frame overlay"); if (reasons & kHandlingScrollFromMainThread) traced_value.AppendString("Handling scroll from main thread"); - if (reasons & kHasOpacityAndLCDText) - traced_value.AppendString("Has opacity and LCD text"); if (reasons & kHasTransformAndLCDText) traced_value.AppendString("Has transform and LCD text"); if (reasons & kBackgroundNotOpaqueInRectAndLCDText) traced_value.AppendString("Background is not opaque in rect and LCD text"); + if (reasons & kCantPaintScrollingBackground) + traced_value.AppendString("Can't paint scrolling background"); if (reasons & kHasClipRelatedProperty) traced_value.AppendString("Has clip related property"); - if (reasons & kHasBoxShadowFromNonRootLayer) - traced_value.AppendString("Has box shadow from non-root layer"); if (reasons & kIsNotStackingContextAndLCDText) traced_value.AppendString("Is not stacking context and LCD text"); diff --git a/chromium/cc/input/main_thread_scrolling_reason.h b/chromium/cc/input/main_thread_scrolling_reason.h index 2dde0784544..cd129cf69fc 100644 --- a/chromium/cc/input/main_thread_scrolling_reason.h +++ b/chromium/cc/input/main_thread_scrolling_reason.h @@ -42,12 +42,11 @@ struct CC_EXPORT MainThreadScrollingReason { // These *AndLCDText reasons are due to subpixel text rendering which can // only be applied by blending glyphs with the background at a specific // screen position; transparency and transforms break this. - kNonCompositedReasonsFirst = 16, - kHasOpacityAndLCDText = 1 << 16, + kNonCompositedReasonsFirst = 17, kHasTransformAndLCDText = 1 << 17, kBackgroundNotOpaqueInRectAndLCDText = 1 << 18, + kCantPaintScrollingBackground = 1 << 19, kHasClipRelatedProperty = 1 << 20, - kHasBoxShadowFromNonRootLayer = 1 << 21, kIsNotStackingContextAndLCDText = 1 << 22, kNonCompositedReasonsLast = 22, @@ -70,9 +69,9 @@ struct CC_EXPORT MainThreadScrollingReason { }; static const uint32_t kNonCompositedReasons = - kHasOpacityAndLCDText | kHasTransformAndLCDText | - kBackgroundNotOpaqueInRectAndLCDText | kHasClipRelatedProperty | - kHasBoxShadowFromNonRootLayer | kIsNotStackingContextAndLCDText; + kHasTransformAndLCDText | kBackgroundNotOpaqueInRectAndLCDText | + kCantPaintScrollingBackground | kHasClipRelatedProperty | + kIsNotStackingContextAndLCDText; // Returns true if the given MainThreadScrollingReason can be set by the main // thread. diff --git a/chromium/cc/input/main_thread_scrolling_reason_unittest.cc b/chromium/cc/input/main_thread_scrolling_reason_unittest.cc index 8744d643907..7cf3f40d164 100644 --- a/chromium/cc/input/main_thread_scrolling_reason_unittest.cc +++ b/chromium/cc/input/main_thread_scrolling_reason_unittest.cc @@ -19,11 +19,10 @@ TEST_F(MainThreadScrollingReasonTest, AsText) { "Scrollbar scrolling, " "Frame overlay, " "Handling scroll from main thread, " - "Has opacity and LCD text, " "Has transform and LCD text, " "Background is not opaque in rect and LCD text, " + "Can't paint scrolling background, " "Has clip related property, " - "Has box shadow from non-root layer, " "Is not stacking context and LCD text, " "Non fast scrollable region, " "Failed hit test, " diff --git a/chromium/cc/input/page_scale_animation.cc b/chromium/cc/input/page_scale_animation.cc index fa059d3086a..c307ff967d2 100644 --- a/chromium/cc/input/page_scale_animation.cc +++ b/chromium/cc/input/page_scale_animation.cc @@ -6,7 +6,7 @@ #include <math.h> -#include "base/logging.h" +#include "base/check.h" #include "base/memory/ptr_util.h" #include "ui/gfx/geometry/point_f.h" #include "ui/gfx/geometry/rect_f.h" diff --git a/chromium/cc/input/scroll_input_type.h b/chromium/cc/input/scroll_input_type.h deleted file mode 100644 index 88e56c8018a..00000000000 --- a/chromium/cc/input/scroll_input_type.h +++ /dev/null @@ -1,20 +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_SCROLL_INPUT_TYPE_H_ -#define CC_INPUT_SCROLL_INPUT_TYPE_H_ - -namespace cc { - -enum class ScrollInputType { - kTouchscreen = 0, - kWheel, - kAutoscroll, - kScrollbar, - kMaxValue = kScrollbar, -}; - -} // namespace cc - -#endif // CC_INPUT_SCROLL_INPUT_TYPE_H_ diff --git a/chromium/cc/input/scroll_snap_data.cc b/chromium/cc/input/scroll_snap_data.cc index 10f3280d34a..f2586f2b976 100644 --- a/chromium/cc/input/scroll_snap_data.cc +++ b/chromium/cc/input/scroll_snap_data.cc @@ -3,6 +3,9 @@ // found in the LICENSE file. #include "cc/input/scroll_snap_data.h" + +#include "base/check.h" +#include "base/notreached.h" #include "base/numerics/ranges.h" #include "cc/input/snap_selection_strategy.h" diff --git a/chromium/cc/input/scroll_state.cc b/chromium/cc/input/scroll_state.cc index 521882a0636..7e14c22c82b 100644 --- a/chromium/cc/input/scroll_state.cc +++ b/chromium/cc/input/scroll_state.cc @@ -24,4 +24,11 @@ void ScrollState::ConsumeDelta(double x, double y) { data_.delta_consumed_for_scroll_sequence = true; } +gfx::ScrollOffset ScrollState::DeltaOrHint() const { + if (is_beginning()) + return gfx::ScrollOffset(delta_x_hint(), delta_y_hint()); + + return gfx::ScrollOffset(delta_x(), delta_y()); +} + } // namespace cc diff --git a/chromium/cc/input/scroll_state.h b/chromium/cc/input/scroll_state.h index a1a5c8c0d6a..a73b9b117c4 100644 --- a/chromium/cc/input/scroll_state.h +++ b/chromium/cc/input/scroll_state.h @@ -11,6 +11,7 @@ #include "cc/cc_export.h" #include "cc/input/scroll_state_data.h" #include "ui/gfx/geometry/point.h" +#include "ui/gfx/geometry/scroll_offset.h" #include "ui/gfx/geometry/vector2d.h" namespace cc { @@ -81,6 +82,10 @@ class CC_EXPORT ScrollState { return data_.delta_granularity; } + // Returns a the delta hints if this is a scroll begin or the real delta if + // it's a scroll update + gfx::ScrollOffset DeltaOrHint() const; + ScrollStateData* data() { return &data_; } private: diff --git a/chromium/cc/input/scrollbar.h b/chromium/cc/input/scrollbar.h index fafe3c267d6..af8cef429b1 100644 --- a/chromium/cc/input/scrollbar.h +++ b/chromium/cc/input/scrollbar.h @@ -48,6 +48,10 @@ enum ScrollbarPart { class Scrollbar : public base::RefCounted<Scrollbar> { public: + // Check if this scrollbar and the other scrollbar are backed with the same + // source scrollbar (e.g. blink::Scrollbar). + virtual bool IsSame(const Scrollbar&) const = 0; + virtual ScrollbarOrientation Orientation() const = 0; virtual bool IsLeftSideVerticalScrollbar() const = 0; virtual bool IsSolidColor() const = 0; diff --git a/chromium/cc/input/scrollbar_controller.cc b/chromium/cc/input/scrollbar_controller.cc index b4d2ef531e2..aac1d4ceccd 100644 --- a/chromium/cc/input/scrollbar_controller.cc +++ b/chromium/cc/input/scrollbar_controller.cc @@ -34,21 +34,6 @@ void ScrollbarController::WillBeginImplFrame() { RecomputeAutoscrollStateIfNeeded(); } -gfx::Vector2dF ScrollbarController::GetThumbRelativePoint( - const ScrollbarLayerImplBase* scrollbar, - const gfx::PointF position_in_widget) { - bool clipped; - const gfx::PointF position_in_layer = - GetScrollbarRelativePosition(scrollbar, position_in_widget, &clipped); - - if (clipped) - return gfx::Vector2d(0, 0); - - const gfx::RectF thumb_rect(scrollbar->ComputeThumbQuadRect()); - DCHECK(thumb_rect.Contains(position_in_layer)); - return position_in_layer - gfx::PointF(thumb_rect.origin()); -} - // Retrieves the ScrollbarLayerImplBase corresponding to the stashed ElementId. ScrollbarLayerImplBase* ScrollbarController::ScrollbarLayer() { if (!captured_scrollbar_metadata_.has_value()) @@ -75,12 +60,12 @@ InputHandlerPointerResult ScrollbarController::HandlePointerDown( // empty InputHandlerPointerResult in this case so that when it is bubbled up // to InputHandlerProxy::RouteToTypeSpecificHandler, the pointer event gets // passed on to the main thread. - if (!(layer_impl && layer_impl->ToScrollbarLayer())) + if (!(layer_impl && layer_impl->IsScrollbarLayer())) return InputHandlerPointerResult(); // If the scrollbar layer has faded out (eg: Overlay scrollbars), don't // initiate a scroll. - const ScrollbarLayerImplBase* scrollbar = layer_impl->ToScrollbarLayer(); + const ScrollbarLayerImplBase* scrollbar = ToScrollbarLayer(layer_impl); if (scrollbar->OverlayScrollbarOpacity() == 0.f) return InputHandlerPointerResult(); @@ -111,13 +96,14 @@ InputHandlerPointerResult ScrollbarController::HandlePointerDown( scroll_result.scroll_units = Granularity(scrollbar_part, shift_modifier); if (scrollbar_part == ScrollbarPart::THUMB) { drag_state_ = DragState(); - drag_state_->anchor_relative_to_thumb_ = - GetThumbRelativePoint(scrollbar, position_in_widget); + drag_state_->drag_origin = position_in_widget; // Record the current scroller offset. This will be needed to snap the // thumb back to its original position if the pointer moves too far away // from the track during a thumb drag. drag_state_->scroll_position_at_start_ = scrollbar->current_pos(); + drag_state_->scroller_length_at_previous_move = + scrollbar->scroll_layer_length(); } if (!scroll_result.scroll_offset.IsZero()) { @@ -249,69 +235,21 @@ float ScrollbarController::GetScrollDeltaForAbsoluteJump( return delta * GetScrollerToScrollbarRatio(scrollbar); } -gfx::ScrollOffset ScrollbarController::GetScrollOffsetForDragPosition( +int ScrollbarController::GetScrollDeltaForDragPosition( const ScrollbarLayerImplBase* scrollbar, const gfx::PointF pointer_position_in_widget) { - layer_tree_host_impl_->active_tree()->UpdateScrollbarGeometries(); - - if (SnapToDragOrigin(scrollbar, pointer_position_in_widget)) { - const float delta = - scrollbar->current_pos() - drag_state_->scroll_position_at_start_; - return scrollbar->orientation() == ScrollbarOrientation::VERTICAL - ? gfx::ScrollOffset(0, -delta) - : gfx::ScrollOffset(-delta, 0); - } - - const gfx::Rect thumb_rect(scrollbar->ComputeThumbQuadRect()); - const gfx::PointF drag_position_relative_to_layer = - gfx::PointF(thumb_rect.origin()) + drag_state_->anchor_relative_to_thumb_; - - bool clipped = false; - const gfx::PointF pointer_position_in_layer = GetScrollbarRelativePosition( - scrollbar, pointer_position_in_widget, &clipped); - - if (clipped) - return gfx::ScrollOffset(0, 0); - - // Calculate the delta based on the previously known thumb drag point. - const gfx::Vector2dF pointer_delta = - pointer_position_in_layer - drag_position_relative_to_layer; - - float scaled_scroller_to_scrollbar_ratio = - GetScrollerToScrollbarRatio(scrollbar); - float current_scroll_position = scrollbar->current_pos(); - - // Thumb position needs to be floored and Values between 0 and 1 are rounded - // to one to match main thread per pixel behavior. Corresponding main thread - // code in ScrollbarTheme::ThumbPosition - float thumb_position = std::max(0.0f, current_scroll_position) / - scaled_scroller_to_scrollbar_ratio; - thumb_position = (thumb_position < 1.0 && thumb_position > 0.0) - ? 1.0 - : floorf(thumb_position); - - float delta_in_orientation = + const float pointer_delta = scrollbar->orientation() == ScrollbarOrientation::VERTICAL - ? pointer_delta.y() - : pointer_delta.x(); + ? pointer_position_in_widget.y() - drag_state_->drag_origin.y() + : pointer_position_in_widget.x() - drag_state_->drag_origin.x(); - // This is effectively equal to delta_in_orientation * - // scaled_scroller_to_scrollbar_ratio but is necessary due to truncated delta - // value. Floored thumb_position cancels out the rounding error introduced - // in pointer_delta due to static_cast<int> in - // ScrollbarLayerImplBase::ComputeThumbQuadRectWithThumbThicknessScale - float scroll_delta = (delta_in_orientation + thumb_position) * - scaled_scroller_to_scrollbar_ratio - - current_scroll_position; - - gfx::ScrollOffset scaled_thumb_drag_delta; + const float new_offset = + pointer_delta * GetScrollerToScrollbarRatio(scrollbar); + const float scroll_delta = drag_state_->scroll_position_at_start_ + + new_offset - scrollbar->current_pos(); // Scroll delta floored to match main thread per pixel behavior - scrollbar->orientation() == ScrollbarOrientation::VERTICAL - ? scaled_thumb_drag_delta.set_y(floorf(scroll_delta)) - : scaled_thumb_drag_delta.set_x(floorf(scroll_delta)); - - return scaled_thumb_drag_delta; + return floorf(scroll_delta); } // Performs hit test and prepares scroll deltas that will be used by GSU. @@ -338,6 +276,18 @@ InputHandlerPointerResult ScrollbarController::HandlePointerMove( if (drag_processed_for_current_frame_) return scroll_result; + if (SnapToDragOrigin(scrollbar, position_in_widget)) { + const float delta = + scrollbar->current_pos() - drag_state_->scroll_position_at_start_; + scroll_result.scroll_units = ui::ScrollGranularity::kScrollByPrecisePixel; + scroll_result.scroll_offset = + scrollbar->orientation() == ScrollbarOrientation::VERTICAL + ? gfx::ScrollOffset(0, -delta) + : gfx::ScrollOffset(-delta, 0); + drag_processed_for_current_frame_ = true; + return scroll_result; + } + // When initiating a thumb drag, a pointerdown and a pointermove can both // arrive a the ScrollbarController in succession before a GSB would have // been dispatched. So, querying LayerTreeHostImpl::CurrentlyScrollingNode() @@ -352,9 +302,27 @@ InputHandlerPointerResult ScrollbarController::HandlePointerMove( // valid ScrollNode. DCHECK(target_node); + int delta = GetScrollDeltaForDragPosition(scrollbar, position_in_widget); + if (drag_state_->scroller_length_at_previous_move != + scrollbar->scroll_layer_length()) { + drag_state_->scroller_displacement = delta; + drag_state_->scroller_length_at_previous_move = + scrollbar->scroll_layer_length(); + + // This is done to ensure that, when the scroller length changes mid thumb + // drag, the scroller shouldn't jump. We early out because the delta would + // be zero in this case anyway (since drag_state_->scroller_displacement = + // delta). So that means, in the worst case you'd miss 1 GSU every time the + // scroller expands while a thumb drag is in progress. + return scroll_result; + } + delta -= drag_state_->scroller_displacement; + // If scroll_offset can't be consumed, there's no point in continuing on. - const gfx::ScrollOffset scroll_offset( - GetScrollOffsetForDragPosition(scrollbar, position_in_widget)); + const gfx::ScrollOffset scroll_offset(scrollbar->orientation() == + ScrollbarOrientation::VERTICAL + ? gfx::ScrollOffset(0, delta) + : gfx::ScrollOffset(delta, 0)); const gfx::Vector2dF clamped_scroll_offset( layer_tree_host_impl_->ComputeScrollDelta( *target_node, ScrollOffsetToVector2dF(scroll_offset))); @@ -624,14 +592,16 @@ int ScrollbarController::GetScrollDeltaForScrollbarPart( const ScrollbarPart scrollbar_part, const bool shift_modifier) { int scroll_delta = 0; - if (layer_tree_host_impl_->settings().percent_based_scrolling) { - // TODO(arakeri): Implement percent based deltas. - } switch (scrollbar_part) { case ScrollbarPart::BACK_BUTTON: case ScrollbarPart::FORWARD_BUTTON: - scroll_delta = kPixelsPerLineStep * ScreenSpaceScaleFactor(); + if (layer_tree_host_impl_->settings().percent_based_scrolling) { + scroll_delta = + kPercentDeltaForDirectionalScroll * GetViewportLength(scrollbar); + } else { + scroll_delta = kPixelsPerLineStep * ScreenSpaceScaleFactor(); + } break; case ScrollbarPart::BACK_TRACK: case ScrollbarPart::FORWARD_TRACK: { diff --git a/chromium/cc/input/scrollbar_controller.h b/chromium/cc/input/scrollbar_controller.h index ab93bde692d..4177c3464e5 100644 --- a/chromium/cc/input/scrollbar_controller.h +++ b/chromium/cc/input/scrollbar_controller.h @@ -138,6 +138,7 @@ class CC_EXPORT ScrollbarController { void DidUnregisterScrollbar(ElementId element_id); ScrollbarLayerImplBase* ScrollbarLayer(); void WillBeginImplFrame(); + void ResetState(); private: // "Autoscroll" here means the continuous scrolling that occurs when the @@ -163,13 +164,19 @@ class CC_EXPORT ScrollbarController { }; struct CC_EXPORT DragState { - // This is used to track the pointer location relative to the thumb origin - // when a drag has started. - gfx::Vector2dF anchor_relative_to_thumb_; + // This marks the point at which the drag initiated (relative to the widget) + gfx::PointF drag_origin; // This is needed for thumb snapping when the pointer moves too far away // from the track while scrolling. float scroll_position_at_start_; + + // The |scroller_displacement| indicates the scroll offset compensation that + // needs to be applied when the scroller's length changes dynamically mid + // thumb drag. This is needed done to ensure that the scroller does not jump + // while a thumb drag is in progress. + float scroller_displacement; + float scroller_length_at_previous_move; }; struct CC_EXPORT CapturedScrollbarMetadata { @@ -223,7 +230,6 @@ class CC_EXPORT ScrollbarController { // the thumb reaching the pointer or the pointer leaving (or re-entering) the // bounds. void RecomputeAutoscrollStateIfNeeded(); - void ResetState(); // Shift (or "Option" in case of Mac) + click is expected to do a non-animated // jump to a certain offset. @@ -233,16 +239,11 @@ class CC_EXPORT ScrollbarController { ui::ScrollGranularity Granularity(const ScrollbarPart scrollbar_part, bool shift_modifier); - // Calculates the scroll_offset based on position_in_widget and - // drag_anchor_relative_to_thumb_. - gfx::ScrollOffset GetScrollOffsetForDragPosition( + // Calculates the delta based on position_in_widget and drag_origin. + int GetScrollDeltaForDragPosition( const ScrollbarLayerImplBase* scrollbar, const gfx::PointF pointer_position_in_widget); - // Returns a Vector2dF for position_in_widget relative to the scrollbar thumb. - gfx::Vector2dF GetThumbRelativePoint(const ScrollbarLayerImplBase* scrollbar, - const gfx::PointF position_in_widget); - // Returns the ratio of the scroller length to the scrollbar length. This is // needed to scale the scroll delta for thumb drag. float GetScrollerToScrollbarRatio(const ScrollbarLayerImplBase* scrollbar); diff --git a/chromium/cc/ipc/cc_param_traits_macros.h b/chromium/cc/ipc/cc_param_traits_macros.h index eed946bde85..4b1d723b862 100644 --- a/chromium/cc/ipc/cc_param_traits_macros.h +++ b/chromium/cc/ipc/cc_param_traits_macros.h @@ -6,12 +6,22 @@ #define CC_IPC_CC_PARAM_TRAITS_MACROS_H_ #include "base/component_export.h" +#include "cc/input/overscroll_behavior.h" #include "cc/input/touch_action.h" #include "ipc/ipc_message_macros.h" #undef IPC_MESSAGE_EXPORT #define IPC_MESSAGE_EXPORT COMPONENT_EXPORT(CC_IPC) +IPC_ENUM_TRAITS_MAX_VALUE( + cc::OverscrollBehavior::OverscrollBehaviorType, + cc::OverscrollBehavior::OverscrollBehaviorType::kOverscrollBehaviorTypeMax) + +IPC_STRUCT_TRAITS_BEGIN(cc::OverscrollBehavior) + IPC_STRUCT_TRAITS_MEMBER(x) + IPC_STRUCT_TRAITS_MEMBER(y) +IPC_STRUCT_TRAITS_END() + IPC_ENUM_TRAITS_MAX_VALUE(cc::TouchAction, cc::TouchAction::kMax) #endif // CC_IPC_CC_PARAM_TRAITS_MACROS_H_ diff --git a/chromium/cc/layers/heads_up_display_layer_impl.cc b/chromium/cc/layers/heads_up_display_layer_impl.cc index e45fae1cf9f..6851763975f 100644 --- a/chromium/cc/layers/heads_up_display_layer_impl.cc +++ b/chromium/cc/layers/heads_up_display_layer_impl.cc @@ -553,6 +553,15 @@ void HeadsUpDisplayLayerImpl::UpdateHudContents() { FrameRateCounter* fps_counter = layer_tree_impl()->frame_rate_counter(); fps_graph_.value = fps_counter->GetAverageFPS(); fps_counter->GetMinAndMaxFPS(&fps_graph_.min, &fps_graph_.max); + current_throughput_ = layer_tree_impl()->current_universal_throughput(); + if (current_throughput_.has_value()) { + if (!max_throughput.has_value() || + current_throughput_.value() > max_throughput.value()) + max_throughput = current_throughput_; + if (!min_throughput.has_value() || + current_throughput_.value() < min_throughput.value()) + min_throughput = current_throughput_; + } } if (debug_state.ShowMemoryStats()) { @@ -682,7 +691,8 @@ SkRect HeadsUpDisplayLayerImpl::DrawFPSDisplay( const int kHistogramWidth = 37; int width = kGraphWidth + kHistogramWidth + 4 * kPadding; - int height = kTitleFontHeight + kFontHeight + kGraphHeight + 6 * kPadding + 2; + int height = + 2 * kTitleFontHeight + 2 * kFontHeight + kGraphHeight + 10 * kPadding + 2; int left = 0; SkRect area = SkRect::MakeXYWH(left, top, width, height); @@ -702,6 +712,7 @@ SkRect HeadsUpDisplayLayerImpl::DrawFPSDisplay( SkRect::MakeXYWH(graph_bounds.right() + kGap, graph_bounds.top(), kHistogramWidth, kGraphHeight); + // Draw the fps meter. const std::string title("Frame Rate"); const std::string value_text = base::StringPrintf("%5.1f fps", fps_graph_.value); @@ -722,7 +733,32 @@ SkRect HeadsUpDisplayLayerImpl::DrawFPSDisplay( DrawGraphLines(canvas, &flags, graph_bounds, fps_graph_); - // Collect graph and histogram data. + // Draw the throughput meter. + int current_top = histogram_bounds.bottom() + kPadding; + const std::string throughput_title("Throughput"); + const std::string throughput_value_text = + current_throughput_.has_value() + ? base::StringPrintf("%d %%", current_throughput_.value()) + : base::StringPrintf("n/a"); + + VLOG(1) << throughput_value_text; + + flags.setColor(DebugColors::HUDTitleColor()); + DrawText(canvas, flags, throughput_title, TextAlign::kLeft, kTitleFontHeight, + title_bounds.left(), title_bounds.bottom() + current_top); + + flags.setColor(DebugColors::FPSDisplayTextAndGraphColor()); + DrawText(canvas, flags, throughput_value_text, TextAlign::kLeft, kFontHeight, + text_bounds.left(), text_bounds.bottom() + current_top); + if (min_throughput.has_value()) { + const std::string throughput_min_max_text = base::StringPrintf( + "%d-%d", min_throughput.value(), max_throughput.value()); + DrawText(canvas, flags, throughput_min_max_text, TextAlign::kRight, + kFontHeight, text_bounds.right(), + text_bounds.bottom() + current_top); + } + + // Collect fps graph and histogram data. SkPath path; const int kHistogramSize = 20; diff --git a/chromium/cc/layers/heads_up_display_layer_impl.h b/chromium/cc/layers/heads_up_display_layer_impl.h index e0784ff4b21..323da1fff87 100644 --- a/chromium/cc/layers/heads_up_display_layer_impl.h +++ b/chromium/cc/layers/heads_up_display_layer_impl.h @@ -164,6 +164,11 @@ class CC_EXPORT HeadsUpDisplayLayerImpl : public LayerImpl { int layout_shift_rects_fade_step_ = 0; std::vector<DebugRect> paint_rects_; std::vector<DebugRect> layout_shift_debug_rects_; + base::Optional<int> current_throughput_; + // The worst and best throughput we have seen so far, they either both have no + // value, or both have value. + base::Optional<int> min_throughput; + base::Optional<int> max_throughput; base::TimeTicks time_of_last_graph_update_; }; diff --git a/chromium/cc/layers/layer.cc b/chromium/cc/layers/layer.cc index f651c0b2d67..c354081f17b 100644 --- a/chromium/cc/layers/layer.cc +++ b/chromium/cc/layers/layer.cc @@ -80,7 +80,6 @@ Layer::Inputs::Inputs(int layer_id) contents_opaque(false), is_drawable(false), double_sided(true), - is_scrollbar(false), has_will_change_transform_hint(false), background_color(0) {} @@ -1015,12 +1014,8 @@ void Layer::SetScrollable(const gfx::Size& bounds) { SetNeedsCommit(); } -void Layer::SetIsScrollbar(bool is_scrollbar) { - if (inputs_.is_scrollbar == is_scrollbar) - return; - - inputs_.is_scrollbar = is_scrollbar; - SetNeedsCommit(); +bool Layer::IsScrollbarLayerForTesting() const { + return false; } void Layer::SetUserScrollable(bool horizontal, bool vertical) { @@ -1112,16 +1107,6 @@ void Layer::SetForceRenderSurfaceForTesting(bool force) { SetNeedsCommit(); } -void Layer::SetDoubleSided(bool double_sided) { - DCHECK(IsPropertyChangeAllowed()); - if (inputs_.double_sided == double_sided) - return; - inputs_.double_sided = double_sided; - SetNeedsCommit(); - SetPropertyTreesNeedRebuild(); - SetSubtreePropertyChanged(); -} - void Layer::SetTransformTreeIndex(int index) { DCHECK(IsPropertyChangeAllowed()); if (transform_tree_index_ == index) @@ -1241,13 +1226,14 @@ std::string Layer::ToString() const { " name: %s\n" " Bounds: %s\n" " ElementId: %s\n" + " HitTestable: %d\n" " OffsetToTransformParent: %s\n" " clip_tree_index: %d\n" " effect_tree_index: %d\n" " scroll_tree_index: %d\n" " transform_tree_index: %d\n", id(), DebugName().c_str(), bounds().ToString().c_str(), - element_id().ToString().c_str(), + element_id().ToString().c_str(), HitTestable(), offset_to_transform_parent().ToString().c_str(), clip_tree_index(), effect_tree_index(), scroll_tree_index(), transform_tree_index()); } @@ -1339,8 +1325,6 @@ void Layer::PushPropertiesTo(LayerImpl* layer) { layer->UpdateScrollable(); - layer->set_is_scrollbar(inputs_.is_scrollbar); - // The property trees must be safe to access because they will be used below // to call |SetScrollOffsetClobberActiveValue|. DCHECK(layer->layer_tree_impl()->lifecycle().AllowsPropertyTreeAccess()); diff --git a/chromium/cc/layers/layer.h b/chromium/cc/layers/layer.h index 6b7e264a4f3..43debb199e8 100644 --- a/chromium/cc/layers/layer.h +++ b/chromium/cc/layers/layer.h @@ -401,8 +401,7 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> { : gfx::Size(); } - void SetIsScrollbar(bool is_scrollbar); - bool is_scrollbar() const { return inputs_.is_scrollbar; } + virtual bool IsScrollbarLayerForTesting() const; // For layer tree mode only. // Set or get if this layer is able to be scrolled along each axis. These are @@ -475,22 +474,6 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> { return force_render_surface_for_testing_; } - // Set or get if this layer should continue to be visible when rotated such - // that its back face is facing toward the camera. If false, the layer will - // disappear when its back face is visible, but if true, the mirror image of - // its front face will be shown. For instance, with a 180deg rotation around - // the middle of the layer on the Y axis, if this is false then nothing is - // visible. But if true, the layer is seen with its contents flipped along the - // Y axis. Being single-sided applies transitively to the subtree of this - // layer. If it is hidden because of its back face being visible, then its - // subtree will be too (even if a subtree layer's front face would have been - // visible). - // - // Note that should_check_backface_visibility() is the final computed value - // for back face visibility, which is only for internal use. - void SetDoubleSided(bool double_sided); - bool double_sided() const { return inputs_.double_sided; } - // When true the layer may contribute to the compositor's output. When false, // it does not. This property does not apply to children of the layer, they // may contribute while this layer does not. The layer itself will determine @@ -850,9 +833,6 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> { bool double_sided : 1; - // Indicates that this layer is a scrollbar. - bool is_scrollbar : 1; - bool has_will_change_transform_hint : 1; SkColor background_color; diff --git a/chromium/cc/layers/layer_impl.cc b/chromium/cc/layers/layer_impl.cc index 13c24539bee..bb1c93f2538 100644 --- a/chromium/cc/layers/layer_impl.cc +++ b/chromium/cc/layers/layer_impl.cc @@ -69,7 +69,6 @@ LayerImpl::LayerImpl(LayerTreeImpl* tree_impl, current_draw_mode_(DRAW_MODE_NONE), has_will_change_transform_hint_(false), needs_push_properties_(false), - is_scrollbar_(false), scrollbars_hidden_(false), needs_show_scrollbars_(false), raster_even_if_not_drawn_(false), @@ -408,8 +407,6 @@ void LayerImpl::PushPropertiesTo(LayerImpl* layer) { layer->SetBounds(bounds_); layer->UpdateScrollable(); - layer->set_is_scrollbar(is_scrollbar_); - layer->UnionUpdateRect(update_rect_); layer->UpdateDebugInfo(debug_info_.get()); @@ -512,8 +509,8 @@ void LayerImpl::SetBounds(const gfx::Size& bounds) { NoteLayerPropertyChanged(); } -ScrollbarLayerImplBase* LayerImpl::ToScrollbarLayer() { - return nullptr; +bool LayerImpl::IsScrollbarLayer() const { + return false; } void LayerImpl::SetDrawsContent(bool draws_content) { @@ -704,7 +701,6 @@ void LayerImpl::AsValueInto(base::trace_event::TracedValue* state) const { } state->SetBoolean("hit_testable", HitTestable()); - state->SetBoolean("can_use_lcd_text", CanUseLCDText()); state->SetBoolean("contents_opaque", contents_opaque()); state->SetBoolean("has_will_change_transform_hint", @@ -774,32 +770,6 @@ gfx::Transform LayerImpl::ScreenSpaceTransform() const { return draw_properties().screen_space_transform; } -bool LayerImpl::CanUseLCDText() const { - if (layer_tree_impl()->settings().layers_always_allowed_lcd_text) - return true; - if (!layer_tree_impl()->settings().can_use_lcd_text) - return false; - if (!contents_opaque()) - return false; - - if (GetEffectTree().Node(effect_tree_index())->screen_space_opacity != 1.f) - return false; - if (!GetTransformTree() - .Node(transform_tree_index()) - ->node_and_ancestors_have_only_integer_translation) - return false; - if (static_cast<int>(offset_to_transform_parent().x()) != - offset_to_transform_parent().x()) - return false; - if (static_cast<int>(offset_to_transform_parent().y()) != - offset_to_transform_parent().y()) - return false; - - if (has_will_change_transform_hint()) - return false; - return true; -} - int LayerImpl::GetSortingContextId() const { return GetTransformTree().Node(transform_tree_index())->sorting_context_id; } diff --git a/chromium/cc/layers/layer_impl.h b/chromium/cc/layers/layer_impl.h index c7b7845723a..95eb4828df8 100644 --- a/chromium/cc/layers/layer_impl.h +++ b/chromium/cc/layers/layer_impl.h @@ -51,7 +51,6 @@ struct LayerDebugInfo; class LayerTreeImpl; class MicroBenchmarkImpl; class PrioritizedTile; -class ScrollbarLayerImplBase; class SimpleEnclosedRegion; class Tile; @@ -149,7 +148,7 @@ class CC_EXPORT LayerImpl { virtual void NotifyTileStateChanged(const Tile* tile) {} - virtual ScrollbarLayerImplBase* ToScrollbarLayer(); + virtual bool IsScrollbarLayer() const; // Returns true if this layer has content to draw. void SetDrawsContent(bool draws_content); @@ -207,8 +206,6 @@ class CC_EXPORT LayerImpl { return performance_properties_; } - bool CanUseLCDText() const; - // Setter for draw_properties_. void set_visible_layer_rect(const gfx::Rect& visible_rect) { draw_properties_.visible_layer_rect = visible_rect; @@ -360,10 +357,6 @@ class CC_EXPORT LayerImpl { return contributes_to_drawn_render_surface_; } - bool is_scrollbar() const { return is_scrollbar_; } - - void set_is_scrollbar(bool is_scrollbar) { is_scrollbar_ = is_scrollbar; } - void set_may_contain_video(bool yes) { may_contain_video_ = yes; } bool may_contain_video() const { return may_contain_video_; } @@ -507,13 +500,12 @@ class CC_EXPORT LayerImpl { DrawMode current_draw_mode_; EffectTree& GetEffectTree() const; - - private: PropertyTrees* GetPropertyTrees() const; ClipTree& GetClipTree() const; ScrollTree& GetScrollTree() const; TransformTree& GetTransformTree() const; + private: ElementId element_id_; // Element ID of the document containing this layer. ElementId frame_element_id_; @@ -535,7 +527,6 @@ class CC_EXPORT LayerImpl { bool has_will_change_transform_hint_ : 1; bool needs_push_properties_ : 1; - bool is_scrollbar_ : 1; bool scrollbars_hidden_ : 1; // The needs_show_scrollbars_ bit tracks a pending request from Blink to show diff --git a/chromium/cc/layers/layer_perftest.cc b/chromium/cc/layers/layer_perftest.cc index 60f73ee4c5a..5218685e1d4 100644 --- a/chromium/cc/layers/layer_perftest.cc +++ b/chromium/cc/layers/layer_perftest.cc @@ -74,7 +74,6 @@ TEST_F(LayerPerfTest, PushPropertiesTo) { float transform_origin_z = 0; bool scrollable = true; bool contents_opaque = true; - bool double_sided = true; bool hide_layer_and_subtree = true; bool masks_to_bounds = true; @@ -84,7 +83,6 @@ TEST_F(LayerPerfTest, PushPropertiesTo) { test_layer->SetNeedsDisplayRect(gfx::Rect(5, 5)); test_layer->SetTransformOrigin(gfx::Point3F(0.f, 0.f, transform_origin_z)); test_layer->SetContentsOpaque(contents_opaque); - test_layer->SetDoubleSided(double_sided); test_layer->SetHideLayerAndSubtree(hide_layer_and_subtree); test_layer->SetMasksToBounds(masks_to_bounds); test_layer->PushPropertiesTo(impl_layer.get()); @@ -92,7 +90,6 @@ TEST_F(LayerPerfTest, PushPropertiesTo) { transform_origin_z += 0.01f; scrollable = !scrollable; contents_opaque = !contents_opaque; - double_sided = !double_sided; hide_layer_and_subtree = !hide_layer_and_subtree; masks_to_bounds = !masks_to_bounds; diff --git a/chromium/cc/layers/layer_unittest.cc b/chromium/cc/layers/layer_unittest.cc index eb64652b974..c49c99b106f 100644 --- a/chromium/cc/layers/layer_unittest.cc +++ b/chromium/cc/layers/layer_unittest.cc @@ -360,14 +360,6 @@ TEST_F(LayerTest, LayerPropertyChangedForSubtree) { grand_child->PushPropertiesTo(grand_child_impl.get())); EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(1); - EXECUTE_AND_VERIFY_SUBTREE_CHANGED(top->SetDoubleSided(false)); - EXECUTE_AND_VERIFY_SUBTREE_CHANGES_RESET( - top->PushPropertiesTo(top_impl.get()); - child->PushPropertiesTo(child_impl.get()); - child2->PushPropertiesTo(child2_impl.get()); - grand_child->PushPropertiesTo(grand_child_impl.get())); - - EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(1); EXECUTE_AND_VERIFY_SUBTREE_CHANGED(top->SetHideLayerAndSubtree(true)); EXECUTE_AND_VERIFY_SUBTREE_CHANGES_RESET( top->PushPropertiesTo(top_impl.get()); @@ -937,7 +929,6 @@ TEST_F(LayerTest, CheckPropertyChangeCausesCorrectBehavior) { Region(gfx::Rect(1, 1, 2, 2)))); EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetTransform( gfx::Transform(0.0, 0.0, 0.0, 0.0, 0.0, 0.0))); - EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetDoubleSided(false)); TouchActionRegion touch_action_region; touch_action_region.Union(TouchAction::kNone, gfx::Rect(10, 10)); EXPECT_SET_NEEDS_COMMIT( diff --git a/chromium/cc/layers/painted_overlay_scrollbar_layer.cc b/chromium/cc/layers/painted_overlay_scrollbar_layer.cc index cc62a68d921..2014160a634 100644 --- a/chromium/cc/layers/painted_overlay_scrollbar_layer.cc +++ b/chromium/cc/layers/painted_overlay_scrollbar_layer.cc @@ -30,6 +30,15 @@ std::unique_ptr<LayerImpl> PaintedOverlayScrollbarLayer::CreateLayerImpl( } scoped_refptr<PaintedOverlayScrollbarLayer> +PaintedOverlayScrollbarLayer::CreateOrReuse( + scoped_refptr<Scrollbar> scrollbar, + PaintedOverlayScrollbarLayer* existing_layer) { + if (existing_layer && existing_layer->scrollbar_->IsSame(*scrollbar)) + return existing_layer; + return Create(std::move(scrollbar)); +} + +scoped_refptr<PaintedOverlayScrollbarLayer> PaintedOverlayScrollbarLayer::Create(scoped_refptr<Scrollbar> scrollbar) { return base::WrapRefCounted( new PaintedOverlayScrollbarLayer(std::move(scrollbar))); @@ -180,7 +189,7 @@ bool PaintedOverlayScrollbarLayer::PaintTickmarks() { } ScrollbarLayerBase::ScrollbarLayerType -PaintedOverlayScrollbarLayer::ScrollbarLayerTypeForTesting() const { +PaintedOverlayScrollbarLayer::GetScrollbarLayerType() const { return kPaintedOverlay; } diff --git a/chromium/cc/layers/painted_overlay_scrollbar_layer.h b/chromium/cc/layers/painted_overlay_scrollbar_layer.h index cdb192deee0..8651c6f4144 100644 --- a/chromium/cc/layers/painted_overlay_scrollbar_layer.h +++ b/chromium/cc/layers/painted_overlay_scrollbar_layer.h @@ -21,18 +21,22 @@ class CC_EXPORT PaintedOverlayScrollbarLayer : public ScrollbarLayerBase { public: std::unique_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) override; + static scoped_refptr<PaintedOverlayScrollbarLayer> CreateOrReuse( + scoped_refptr<Scrollbar> scrollbar, + PaintedOverlayScrollbarLayer* existing_layer); + static scoped_refptr<PaintedOverlayScrollbarLayer> Create( + scoped_refptr<Scrollbar> scrollbar); + PaintedOverlayScrollbarLayer(const PaintedOverlayScrollbarLayer&) = delete; PaintedOverlayScrollbarLayer& operator=(const PaintedOverlayScrollbarLayer&) = delete; - static scoped_refptr<PaintedOverlayScrollbarLayer> Create( - scoped_refptr<Scrollbar> scrollbar); bool OpacityCanAnimateOnImplThread() const override; bool Update() override; void SetLayerTreeHost(LayerTreeHost* host) override; void PushPropertiesTo(LayerImpl* layer) override; - ScrollbarLayerType ScrollbarLayerTypeForTesting() const override; + ScrollbarLayerType GetScrollbarLayerType() const override; protected: explicit PaintedOverlayScrollbarLayer(scoped_refptr<Scrollbar> scrollbar); diff --git a/chromium/cc/layers/painted_scrollbar_layer.cc b/chromium/cc/layers/painted_scrollbar_layer.cc index 2a5745d38af..84f6b872302 100644 --- a/chromium/cc/layers/painted_scrollbar_layer.cc +++ b/chromium/cc/layers/painted_scrollbar_layer.cc @@ -20,6 +20,14 @@ std::unique_ptr<LayerImpl> PaintedScrollbarLayer::CreateLayerImpl( is_overlay_); } +scoped_refptr<PaintedScrollbarLayer> PaintedScrollbarLayer::CreateOrReuse( + scoped_refptr<Scrollbar> scrollbar, + PaintedScrollbarLayer* existing_layer) { + if (existing_layer && existing_layer->scrollbar_->IsSame(*scrollbar)) + return existing_layer; + return Create(std::move(scrollbar)); +} + scoped_refptr<PaintedScrollbarLayer> PaintedScrollbarLayer::Create( scoped_refptr<Scrollbar> scrollbar) { return base::WrapRefCounted(new PaintedScrollbarLayer(std::move(scrollbar))); @@ -237,7 +245,7 @@ UIResourceBitmap PaintedScrollbarLayer::RasterizeScrollbarPart( } ScrollbarLayerBase::ScrollbarLayerType -PaintedScrollbarLayer::ScrollbarLayerTypeForTesting() const { +PaintedScrollbarLayer::GetScrollbarLayerType() const { return kPainted; } diff --git a/chromium/cc/layers/painted_scrollbar_layer.h b/chromium/cc/layers/painted_scrollbar_layer.h index e910a004849..191c37d8e5a 100644 --- a/chromium/cc/layers/painted_scrollbar_layer.h +++ b/chromium/cc/layers/painted_scrollbar_layer.h @@ -21,6 +21,9 @@ class CC_EXPORT PaintedScrollbarLayer : public ScrollbarLayerBase { public: std::unique_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) override; + static scoped_refptr<PaintedScrollbarLayer> CreateOrReuse( + scoped_refptr<Scrollbar> scrollbar, + PaintedScrollbarLayer* existing_layer); static scoped_refptr<PaintedScrollbarLayer> Create( scoped_refptr<Scrollbar> scrollbar); @@ -36,7 +39,7 @@ class CC_EXPORT PaintedScrollbarLayer : public ScrollbarLayerBase { return internal_content_bounds_; } - ScrollbarLayerType ScrollbarLayerTypeForTesting() const override; + ScrollbarLayerType GetScrollbarLayerType() const override; protected: explicit PaintedScrollbarLayer(scoped_refptr<Scrollbar> scrollbar); diff --git a/chromium/cc/layers/picture_layer.cc b/chromium/cc/layers/picture_layer.cc index 957bcef1174..b7244152c31 100644 --- a/chromium/cc/layers/picture_layer.cc +++ b/chromium/cc/layers/picture_layer.cc @@ -15,6 +15,7 @@ #include "cc/trees/layer_tree_host.h" #include "cc/trees/layer_tree_impl.h" #include "cc/trees/transform_node.h" +#include "third_party/skia/include/core/SkPictureRecorder.h" #include "ui/gfx/geometry/rect_conversions.h" namespace cc { @@ -139,6 +140,24 @@ bool PictureLayer::Update() { picture_layer_inputs_.display_list = picture_layer_inputs_.client->PaintContentsToDisplayList( ContentLayerClient::PAINTING_BEHAVIOR_NORMAL); + + // Clear out previous directly composited image state - if the layer + // qualifies we'll set up the state below. + picture_layer_inputs_.directly_composited_image_size = base::nullopt; + picture_layer_inputs_.nearest_neighbor = false; + base::Optional<DisplayItemList::DirectlyCompositedImageResult> result = + picture_layer_inputs_.display_list->GetDirectlyCompositedImageResult( + bounds()); + if (result) { + // Directly composited images are not guaranteed to fully cover every + // pixel in the layer due to ceiling when calculating the tile content + // rect from the layer bounds. + recording_source_->SetRequiresClear(true); + picture_layer_inputs_.directly_composited_image_size = + result->intrinsic_image_size; + picture_layer_inputs_.nearest_neighbor = result->nearest_neighbor; + } + picture_layer_inputs_.painter_reported_memory_usage = picture_layer_inputs_.client->GetApproximateUnsharedMemoryUsage(); recording_source_->UpdateDisplayItemList( @@ -158,33 +177,18 @@ bool PictureLayer::Update() { } sk_sp<SkPicture> PictureLayer::GetPicture() const { - // We could either flatten the RecordingSource into a single SkPicture, or - // paint a fresh one depending on what we intend to do with it. For now we - // just paint a fresh one to get consistent results. - if (!DrawsContent()) + if (!DrawsContent() || bounds().IsEmpty()) return nullptr; - gfx::Size layer_size = bounds(); - RecordingSource recording_source; - Region recording_invalidation; - - gfx::Rect new_recorded_viewport = - picture_layer_inputs_.client->PaintableRegion(); scoped_refptr<DisplayItemList> display_list = picture_layer_inputs_.client->PaintContentsToDisplayList( ContentLayerClient::PAINTING_BEHAVIOR_NORMAL); - size_t painter_reported_memory_usage = - picture_layer_inputs_.client->GetApproximateUnsharedMemoryUsage(); - - recording_source.UpdateAndExpandInvalidation( - &recording_invalidation, layer_size, new_recorded_viewport); - recording_source.UpdateDisplayItemList( - display_list, painter_reported_memory_usage, - layer_tree_host()->recording_scale_factor()); - - scoped_refptr<RasterSource> raster_source = - recording_source.CreateRasterSource(); - return raster_source->GetFlattenedPicture(); + SkPictureRecorder recorder; + SkCanvas* canvas = + recorder.beginRecording(bounds().width(), bounds().height()); + canvas->clear(SK_ColorTRANSPARENT); + display_list->Raster(canvas); + return recorder.finishRecordingAsPicture(); } void PictureLayer::ClearClient() { diff --git a/chromium/cc/layers/picture_layer_impl.cc b/chromium/cc/layers/picture_layer_impl.cc index e1e9864e812..00e845e2b3f 100644 --- a/chromium/cc/layers/picture_layer_impl.cc +++ b/chromium/cc/layers/picture_layer_impl.cc @@ -29,6 +29,7 @@ #include "cc/trees/effect_node.h" #include "cc/trees/layer_tree_impl.h" #include "cc/trees/occlusion.h" +#include "cc/trees/transform_node.h" #include "components/viz/common/frame_sinks/begin_frame_args.h" #include "components/viz/common/quads/debug_border_draw_quad.h" #include "components/viz/common/quads/picture_draw_quad.h" @@ -96,8 +97,9 @@ PictureLayerImpl::PictureLayerImpl(LayerTreeImpl* tree_impl, int id) only_used_low_res_last_append_quads_(false), nearest_neighbor_(false), use_transformed_rasterization_(false), - can_use_lcd_text_(true), + lcd_text_disallowed_reason_(LCDTextDisallowedReason::kNone), directly_composited_image_size_(base::nullopt), + directly_composited_image_initial_raster_scale_(0.f), tile_size_calculator_(this) { layer_tree_impl()->RegisterPictureLayerImpl(this); } @@ -175,10 +177,12 @@ void PictureLayerImpl::PushPropertiesTo(LayerImpl* base_layer) { layer_impl->raster_source_scale_ = raster_source_scale_; layer_impl->raster_contents_scale_ = raster_contents_scale_; layer_impl->low_res_raster_contents_scale_ = low_res_raster_contents_scale_; + layer_impl->directly_composited_image_initial_raster_scale_ = + directly_composited_image_initial_raster_scale_; // Simply push the value to the active tree without any extra invalidations, // since the pending tree tiles would have this handled. This is here to // ensure the state is consistent for future raster. - layer_impl->can_use_lcd_text_ = can_use_lcd_text_; + layer_impl->lcd_text_disallowed_reason_ = lcd_text_disallowed_reason_; layer_impl->SanityCheckTilingState(); } @@ -241,6 +245,41 @@ void PictureLayerImpl::AppendQuads(viz::RenderPass* render_pass, tilings_->num_tilings() ? MaximumTilingContentsScale() : 1.f; PopulateScaledSharedQuadState(shared_quad_state, max_contents_scale, contents_opaque()); + + if (directly_composited_image_size_) { + // Directly composited images should be clipped to the layer's content rect. + // When a PictureLayerTiling is created for a directly composited image, the + // layer bounds are multiplied by the raster scale in order to compute the + // tile size. If the aspect ratio of the layer doesn't match that of the + // image, it's possible that one of the dimensions of the resulting size + // (layer bounds * raster scale) is a fractional number, as raster scale + // does not scale x and y independently. + // When this happens, the ToEnclosingRect() operation in + // |PictureLayerTiling::EnclosingContentsRectFromLayer()| will + // create a tiling that, when scaled by |max_contents_scale| above, is + // larger than the layer bounds by a fraction of a pixel. + gfx::Rect clip_rect = draw_properties().drawable_content_rect; + if (shared_quad_state->is_clipped) + clip_rect.Intersect(shared_quad_state->clip_rect); + + shared_quad_state->is_clipped = true; + shared_quad_state->clip_rect = clip_rect; + +#if DCHECK_IS_ON() + // Validate that the tile and bounds size are always within one pixel. + PictureLayerTiling* high_res = + tilings_->FindTilingWithResolution(HIGH_RESOLUTION); + if (high_res) { + const float epsilon = 1.f; + gfx::SizeF scaled_tiling_size(high_res->tiling_size()); + scaled_tiling_size.Scale(1 / raster_contents_scale_); + DCHECK(std::abs(bounds().width() - scaled_tiling_size.width()) < epsilon); + DCHECK(std::abs(bounds().height() - scaled_tiling_size.height()) < + epsilon); + } +#endif + } + Occlusion scaled_occlusion = draw_properties() .occlusion_in_content_space.GetOcclusionWithGivenDrawTransform( @@ -359,6 +398,18 @@ void PictureLayerImpl::AppendQuads(viz::RenderPass* render_pass, } } + if (layer_tree_impl()->debug_state().highlight_non_lcd_text_layers) { + SkColor color = + DebugColors::NonLCDTextHighlightColor(lcd_text_disallowed_reason()); + if (color != SK_ColorTRANSPARENT && + GetRasterSource()->GetDisplayItemList()->AreaOfDrawText( + gfx::Rect(bounds()))) { + render_pass->CreateAndAppendDrawQuad<viz::SolidColorDrawQuad>()->SetNew( + shared_quad_state, debug_border_rect, debug_border_rect, color, + append_quads_data); + } + } + // Keep track of the tilings that were used so that tilings that are // unused can be considered for removal. last_append_quads_tilings_.clear(); @@ -542,9 +593,14 @@ bool PictureLayerImpl::UpdateTiles() { if (layer_tree_impl()->IsActiveTree()) CleanUpTilingsOnActiveLayer(last_append_quads_tilings_); + const float old_ideal_contents_scale = ideal_contents_scale_; UpdateIdealScales(); - if (!raster_contents_scale_ || ShouldAdjustRasterScale()) { + const bool ideal_contents_scale_changed = + old_ideal_contents_scale != 0 && + old_ideal_contents_scale != ideal_contents_scale_; + if (!raster_contents_scale_ || + ShouldAdjustRasterScale(ideal_contents_scale_changed)) { RecalculateRasterScales(); AddTilingsForRasterScale(); } @@ -706,7 +762,7 @@ void PictureLayerImpl::UpdateRasterSource( // The |raster_source_| is initially null, so have to check for that for the // first frame. - bool could_have_tilings = raster_source_.get() && CanHaveTilings(); + bool could_have_tilings = CanHaveTilings(); raster_source_.swap(raster_source); // Register images from the new raster source, if the recording was updated. @@ -758,13 +814,14 @@ bool PictureLayerImpl::UpdateCanUseLCDTextAfterCommit() { DCHECK(layer_tree_impl()->IsSyncTree()); // Once we disable lcd text, we don't re-enable it. - if (!can_use_lcd_text_) + if (!can_use_lcd_text()) return false; - if (can_use_lcd_text_ == CanUseLCDText()) + auto new_lcd_text_disallowed_reason = ComputeLCDTextDisallowedReason(); + if (lcd_text_disallowed_reason_ == new_lcd_text_disallowed_reason) return false; - can_use_lcd_text_ = CanUseLCDText(); + lcd_text_disallowed_reason_ = new_lcd_text_disallowed_reason; // Synthetically invalidate everything. gfx::Rect bounds_rect(bounds()); invalidation_ = Region(bounds_rect); @@ -773,6 +830,35 @@ bool PictureLayerImpl::UpdateCanUseLCDTextAfterCommit() { return true; } +LCDTextDisallowedReason PictureLayerImpl::ComputeLCDTextDisallowedReason() + const { + if (layer_tree_impl()->settings().layers_always_allowed_lcd_text) + return LCDTextDisallowedReason::kNone; + if (!layer_tree_impl()->settings().can_use_lcd_text) + return LCDTextDisallowedReason::kSetting; + if (!contents_opaque()) { + if (SkColorGetA(background_color()) != SK_AlphaOPAQUE) + return LCDTextDisallowedReason::kBackgroundColorNotOpaque; + return LCDTextDisallowedReason::kContentsNotOpaque; + } + + if (!GetTransformTree() + .Node(transform_tree_index()) + ->node_and_ancestors_have_only_integer_translation) + return LCDTextDisallowedReason::kNonIntegralTranslation; + if (static_cast<int>(offset_to_transform_parent().x()) != + offset_to_transform_parent().x()) + return LCDTextDisallowedReason::kNonIntegralXOffset; + if (static_cast<int>(offset_to_transform_parent().y()) != + offset_to_transform_parent().y()) + return LCDTextDisallowedReason::kNonIntegralYOffset; + + if (has_will_change_transform_hint()) + return LCDTextDisallowedReason::kWillChangeTransform; + + return LCDTextDisallowedReason::kNone; +} + void PictureLayerImpl::NotifyTileStateChanged(const Tile* tile) { if (layer_tree_impl()->IsActiveTree()) damage_rect_.Union(tile->enclosing_layer_rect()); @@ -837,7 +923,7 @@ std::unique_ptr<Tile> PictureLayerImpl::CreateTile( return layer_tree_impl()->tile_manager()->CreateTile( info, id(), layer_tree_impl()->source_frame_number(), flags, - can_use_lcd_text_); + can_use_lcd_text()); } const Region* PictureLayerImpl::GetPendingInvalidation() { @@ -996,16 +1082,91 @@ void PictureLayerImpl::SetDirectlyCompositedImageSize( NoteLayerPropertyChanged(); } -float PictureLayerImpl::GetDirectlyCompositedImageRasterScale() const { +bool PictureLayerImpl::ShouldDirectlyCompositeImage(float raster_scale) const { + // If the results of scaling the bounds by the expected raster scale + // would end up with a content rect whose width/height are more than one + // pixel different from the layer bounds, don't directly composite the image + // to avoid incorrect rendering. + gfx::SizeF layer_bounds(bounds()); + gfx::RectF scaled_bounds_rect(layer_bounds); + scaled_bounds_rect.Scale(raster_scale); + + // Take the scaled bounds, get the enclosing rect then scale it back down - + // this is the same set of operations that will happen when using the tiling + // at that raster scale. + gfx::RectF content_rect(gfx::ToEnclosingRect(scaled_bounds_rect)); + content_rect.Scale(1 / raster_scale); + + return std::abs(layer_bounds.width() - content_rect.width()) < 1.f && + std::abs(layer_bounds.height() - content_rect.height()) < 1.f; +} + +float PictureLayerImpl::GetDefaultDirectlyCompositedImageRasterScale() const { + DCHECK(directly_composited_image_size_.has_value()); float x = static_cast<float>(directly_composited_image_size_->width()) / bounds().width(); float y = static_cast<float>(directly_composited_image_size_->height()) / bounds().height(); - DCHECK_EQ(x, 1.f); - DCHECK_EQ(y, 1.f); return GetPreferredRasterScale(gfx::Vector2dF(x, y)); } +float PictureLayerImpl::CalculateDirectlyCompositedImageRasterScale() const { + float default_raster_scale = GetDefaultDirectlyCompositedImageRasterScale(); + bool default_raster_scale_changed = + default_raster_scale != directly_composited_image_initial_raster_scale_; + + // If the default raster scale didn't change, we will calculate based on the + // previous raster source scale. The calculation may change based on updated + // ideal source scale. + float adjusted_raster_scale = default_raster_scale_changed + ? default_raster_scale + : raster_source_scale_; + + // We never want a raster scale larger than the default, since that uses more + // memory but can't result it better quality (upscaling will happen in the + // display compositor instead). + float max_scale = std::max(default_raster_scale, MinimumContentsScale()); + float min_scale = MinimumContentsScale(); + + float clamped_ideal_source_scale = + base::ClampToRange(ideal_source_scale_, min_scale, max_scale); + while (adjusted_raster_scale < clamped_ideal_source_scale) + adjusted_raster_scale *= 2.f; + while (adjusted_raster_scale > 4 * clamped_ideal_source_scale) + adjusted_raster_scale /= 2.f; + + adjusted_raster_scale = + base::ClampToRange(adjusted_raster_scale, min_scale, max_scale); + return adjusted_raster_scale; +} + +// Log either the tile area saved or added due to directly compositing an +// image. This is logged every time we choose a raster source scale for a +// directly composited image. +void PictureLayerImpl::LogDirectlyCompositedImageRasterScaleUMAs() const { + base::CheckedNumeric<int> actual_area = + ScaleToCeiledSize(bounds(), raster_source_scale_).GetCheckedArea(); + base::CheckedNumeric<int> ideal_area = + ScaleToCeiledSize(bounds(), ideal_source_scale_).GetCheckedArea(); + if (actual_area.IsValid() && ideal_area.IsValid()) { + int area_difference = + std::abs(static_cast<int>((actual_area - ideal_area).ValueOrDie())); + bool raster_area_matches = raster_source_scale_ == ideal_source_scale_; + UMA_HISTOGRAM_BOOLEAN( + "Compositing.Renderer.DirectlyCompositedImage.TileAreaMatches", + raster_area_matches); + if (raster_source_scale_ < ideal_source_scale_) { + UMA_HISTOGRAM_COUNTS_10M( + "Compositing.Renderer.DirectlyCompositedImage.TileAreaSaved", + area_difference); + } else if (raster_source_scale_ > ideal_source_scale_) { + UMA_HISTOGRAM_COUNTS_10M( + "Compositing.Renderer.DirectlyCompositedImage.TileAreaAdded", + area_difference); + } + } +} + PictureLayerTiling* PictureLayerImpl::AddTiling( const gfx::AxisTransform2d& contents_transform) { DCHECK(CanHaveTilings()); @@ -1053,29 +1214,76 @@ void PictureLayerImpl::AddTilingsForRasterScale() { } high_res->set_resolution(HIGH_RESOLUTION); - if (layer_tree_impl()->IsPendingTree()) { + if (layer_tree_impl()->IsPendingTree() || + (layer_tree_impl()->settings().commit_to_active_tree && + directly_composited_image_size_.has_value())) { // On the pending tree, drop any tilings that are non-ideal since we don't // need them to activate anyway. + + // For DirectlyCompositedImages, if we recomputed a new raster scale, we + // should drop the non-ideal ones if we're committing to the active tree. + // Otherwise a non-ideal scale that is _larger_ than the HIGH_RESOLUTION + // tile will be used as the coverage scale, and we'll produce a slightly + // different rendering. We don't drop the tilings on the active tree if + // we're not committing to the active tree to prevent checkerboarding. tilings_->RemoveNonIdealTilings(); } SanityCheckTilingState(); } -bool PictureLayerImpl::ShouldAdjustRasterScale() const { +bool PictureLayerImpl::ShouldAdjustRasterScale( + bool ideal_contents_scale_changed) const { if (directly_composited_image_size_) { - float desired_raster_scale = GetDirectlyCompositedImageRasterScale(); - float max_scale = std::max(desired_raster_scale, MinimumContentsScale()); + // If we have a directly composited image size, but previous raster scale + // calculations did not set an initial raster scale, we must recalcluate. + if (directly_composited_image_initial_raster_scale_ == 0) + return true; + + float default_raster_scale = GetDefaultDirectlyCompositedImageRasterScale(); + + // First check to see if we need to adjust based on ideal_source_scale_ + // changing (i.e. scale transform has been modified). These limits exist + // so that we don't raster at the intrinsic image size if the layer will + // be scaled down more than 4x ideal. This saves memory without sacrificing + // noticeable quality. We'll also bump the scale back up in the case where + // the ideal scale is increased. + float max_scale = std::max(default_raster_scale, MinimumContentsScale()); if (raster_source_scale_ < std::min(ideal_source_scale_, max_scale)) return true; if (raster_source_scale_ > 4 * ideal_source_scale_) return true; - return false; + + // If the default raster scale changed, that means the bounds or image size + // changed. We should recalculate in order to raster at the intrinsic image + // size. Note that this is not a comparison of the used raster_source_scale_ + // and desired because of the adjustments in RecalculateRasterScales. + bool default_raster_scale_changed = + default_raster_scale != directly_composited_image_initial_raster_scale_; + if (ideal_contents_scale_changed && !default_raster_scale_changed) { + // Log a histogram to indicate that we most likely saved raster costs, + // if the ideal contents scale changed but we did not need to recalculate + // raster scales because this layer is a directly composited image. + bool transform_trigger = + draw_properties().screen_space_transform_is_animating || + has_will_change_transform_hint(); + UMA_HISTOGRAM_BOOLEAN( + "Compositing.Renderer.DirectlyCompositedImage." + "AvoidRasterAdjustmentWithTransformTrigger", + transform_trigger); + } + return default_raster_scale_changed; } if (was_screen_space_transform_animating_ != - draw_properties().screen_space_transform_is_animating) - return true; + draw_properties().screen_space_transform_is_animating) { + // Skip adjusting raster scale when animations finish if we have a + // will-change: transform hint to preserve maximum resolution tiles + // needed. + if (draw_properties().screen_space_transform_is_animating || + !has_will_change_transform_hint()) + return true; + } bool is_pinching = layer_tree_impl()->PinchGestureActive(); if (is_pinching && raster_page_scale_) { @@ -1153,33 +1361,39 @@ void PictureLayerImpl::AddLowResolutionTilingIfNeeded() { void PictureLayerImpl::RecalculateRasterScales() { if (directly_composited_image_size_) { - if (!raster_source_scale_) - raster_source_scale_ = GetDirectlyCompositedImageRasterScale(); - - float min_scale = MinimumContentsScale(); - float desired_raster_scale = GetDirectlyCompositedImageRasterScale(); - float max_scale = std::max(desired_raster_scale, MinimumContentsScale()); - float clamped_ideal_source_scale = - base::ClampToRange(ideal_source_scale_, min_scale, max_scale); - - while (raster_source_scale_ < clamped_ideal_source_scale) - raster_source_scale_ *= 2.f; - while (raster_source_scale_ > 4 * clamped_ideal_source_scale) - raster_source_scale_ /= 2.f; - - raster_source_scale_ = - base::ClampToRange(raster_source_scale_, min_scale, max_scale); - - raster_page_scale_ = 1.f; - raster_device_scale_ = 1.f; - raster_contents_scale_ = raster_source_scale_; - low_res_raster_contents_scale_ = raster_contents_scale_; - return; + float used_raster_scale = CalculateDirectlyCompositedImageRasterScale(); + const bool should_directly_composite = + ShouldDirectlyCompositeImage(used_raster_scale); + + UMA_HISTOGRAM_BOOLEAN( + "Compositing.Renderer.DirectlyCompositedImage." + "RasterScaleDirectlyComposited", + should_directly_composite); + if (should_directly_composite) { + directly_composited_image_initial_raster_scale_ = + GetDefaultDirectlyCompositedImageRasterScale(); + raster_source_scale_ = used_raster_scale; + raster_page_scale_ = 1.f; + raster_device_scale_ = 1.f; + raster_contents_scale_ = raster_source_scale_; + low_res_raster_contents_scale_ = raster_contents_scale_; + + LogDirectlyCompositedImageRasterScaleUMAs(); + return; + } + + // If we should not directly composite this image, reset values and fall + // back to normal raster scale calculations below. + directly_composited_image_size_ = base::nullopt; + directly_composited_image_initial_raster_scale_ = 0.f; } float old_raster_contents_scale = raster_contents_scale_; float old_raster_page_scale = raster_page_scale_; + // The raster scale if previous tilings should be preserved. + float preserved_raster_contents_scale = old_raster_contents_scale; + raster_device_scale_ = ideal_device_scale_; raster_page_scale_ = ideal_page_scale_; raster_source_scale_ = ideal_source_scale_; @@ -1201,8 +1415,9 @@ void PictureLayerImpl::RecalculateRasterScales() { while (desired_contents_scale < ideal_contents_scale_) desired_contents_scale *= kMaxScaleRatioDuringPinch; } - raster_contents_scale_ = tilings_->GetSnappedContentsScaleKey( - desired_contents_scale, kSnapToExistingTilingRatio); + raster_contents_scale_ = preserved_raster_contents_scale = + tilings_->GetSnappedContentsScaleKey(desired_contents_scale, + kSnapToExistingTilingRatio); raster_page_scale_ = raster_contents_scale_ / raster_device_scale_ / raster_source_scale_; } @@ -1252,15 +1467,27 @@ void PictureLayerImpl::RecalculateRasterScales() { if (start_area <= viewport_area) should_raster_at_starting_scale = true; } + // Use the computed scales for the raster scale directly, do not try to use // the ideal scale here. The current ideal scale may be way too large in the // case of an animation with scale, and will be constantly changing. + float animation_desired_scale; if (should_raster_at_starting_scale) - raster_contents_scale_ = starting_scale; + animation_desired_scale = starting_scale; else if (can_raster_at_maximum_scale) - raster_contents_scale_ = maximum_scale; + animation_desired_scale = maximum_scale; else - raster_contents_scale_ = 1.f * ideal_page_scale_ * ideal_device_scale_; + animation_desired_scale = 1.f * ideal_page_scale_ * ideal_device_scale_; + + if (has_will_change_transform_hint()) { + // If we have a will-change: transform hint, do not shrink the content + // raster scale, otherwise we will end up throwing away larger tiles we + // may need again. + raster_contents_scale_ = + std::max(preserved_raster_contents_scale, animation_desired_scale); + } else { + raster_contents_scale_ = animation_desired_scale; + } } // Clamp will-change: transform layers to be at least the native scale. @@ -1378,7 +1605,13 @@ float PictureLayerImpl::MinimumContentsScale() const { if (!min_dimension) return setting_min; - return std::max(1.f / min_dimension, setting_min); + // Directly composited images may result in contents scales that are + // less than the configured setting. We allow this lower scale so that we + // can raster at the intrinsic image size. + const float inverse_min_dimension = 1.f / min_dimension; + return (directly_composited_image_size_.has_value()) + ? inverse_min_dimension + : std::max(inverse_min_dimension, setting_min); } float PictureLayerImpl::MaximumContentsScale() const { @@ -1409,9 +1642,12 @@ void PictureLayerImpl::ResetRasterScale() { raster_source_scale_ = 0.f; raster_contents_scale_ = 0.f; low_res_raster_contents_scale_ = 0.f; + directly_composited_image_initial_raster_scale_ = 0.f; } bool PictureLayerImpl::CanHaveTilings() const { + if (!raster_source_) + return false; if (raster_source_->IsSolidColor()) return false; if (!DrawsContent()) @@ -1526,6 +1762,10 @@ void PictureLayerImpl::AsValueInto( state); MathUtil::AddToTracedValue("visible_rect", visible_layer_rect(), state); + state->SetString( + "lcd_text_disallowed_reason", + LCDTextDisallowedReasonToString(lcd_text_disallowed_reason_)); + state->BeginArray("pictures"); raster_source_->AsValueInto(state); state->EndArray(); diff --git a/chromium/cc/layers/picture_layer_impl.h b/chromium/cc/layers/picture_layer_impl.h index 1061fc68370..0be0cf1c68a 100644 --- a/chromium/cc/layers/picture_layer_impl.h +++ b/chromium/cc/layers/picture_layer_impl.h @@ -18,6 +18,7 @@ #include "cc/layers/tile_size_calculator.h" #include "cc/paint/discardable_image_map.h" #include "cc/paint/image_id.h" +#include "cc/raster/lcd_text_disallowed_reason.h" #include "cc/tiles/picture_layer_tiling.h" #include "cc/tiles/picture_layer_tiling_set.h" #include "cc/tiles/tiling_set_eviction_queue.h" @@ -136,7 +137,15 @@ class CC_EXPORT PictureLayerImpl ImageInvalidationResult InvalidateRegionForImages( const PaintImageIdFlatSet& images_to_invalidate); - bool can_use_lcd_text() const { return can_use_lcd_text_; } + bool can_use_lcd_text() const { + return lcd_text_disallowed_reason_ == LCDTextDisallowedReason::kNone; + } + LCDTextDisallowedReason lcd_text_disallowed_reason() const { + return lcd_text_disallowed_reason_; + } + LCDTextDisallowedReason ComputeLCDTextDisallowedReasonForTesting() const { + return ComputeLCDTextDisallowedReason(); + } const Region& InvalidationForTesting() const { return invalidation_; } @@ -164,7 +173,7 @@ class CC_EXPORT PictureLayerImpl void RemoveAllTilings(); void AddTilingsForRasterScale(); void AddLowResolutionTilingIfNeeded(); - bool ShouldAdjustRasterScale() const; + bool ShouldAdjustRasterScale(bool ideal_contents_scale_changed) const; void RecalculateRasterScales(); gfx::Vector2dF CalculateRasterTranslation(float raster_scale); void CleanUpTilingsOnActiveLayer( @@ -173,7 +182,21 @@ class CC_EXPORT PictureLayerImpl float MaximumContentsScale() const; void UpdateViewportRectForTilePriorityInContentSpace(); PictureLayerImpl* GetRecycledTwinLayer() const; - float GetDirectlyCompositedImageRasterScale() const; + bool ShouldDirectlyCompositeImage(float raster_scale) const; + + // Returns the default raster scale used for current layer bounds and directly + // composited image size. To avoid re-raster on scale changes, this may be + // different than the used raster scale, see: |RecalculateRasterScales()| and + // |CalculateDirectlyCompositedImageRasterScale()|. + float GetDefaultDirectlyCompositedImageRasterScale() const; + + // Returns the raster scale that should be used for a directly composited + // image. This takes into account the ideal contents scale to ensure we don't + // use too much memory for layers that are small due to contents scale + // factors, and bumps up the reduced scale if those layers end up increasing + // their contents scale. + float CalculateDirectlyCompositedImageRasterScale() const; + void LogDirectlyCompositedImageRasterScaleUMAs() const; void SanityCheckTilingState() const; @@ -195,6 +218,8 @@ class CC_EXPORT PictureLayerImpl const std::vector<DiscardableImageMap::PaintWorkletInputWithImageId>& inputs); + LCDTextDisallowedReason ComputeLCDTextDisallowedReason() const; + PictureLayerImpl* twin_layer_; std::unique_ptr<PictureLayerTilingSet> tilings_; @@ -228,10 +253,20 @@ class CC_EXPORT PictureLayerImpl bool nearest_neighbor_ : 1; bool use_transformed_rasterization_ : 1; - bool can_use_lcd_text_ : 1; + LCDTextDisallowedReason lcd_text_disallowed_reason_; + + // The intrinsic size of the directly composited image. A directly composited + // image is an image which is the only thing drawn into a layer. In these + // cases we attempt to raster the image at its intrinsic size. base::Optional<gfx::Size> directly_composited_image_size_; + // The default raster source scale for a directly composited image, the last + // time raster scales were calculated. This will be the same as + // |raster_source_scale_| if no adjustments were made in + // |CalculateDirectlyCompositedImageRasterScale()|. + float directly_composited_image_initial_raster_scale_; + // Use this instead of |visible_layer_rect()| for tiling calculations. This // takes external viewport and transform for tile priority into account. gfx::Rect viewport_rect_for_tile_priority_in_content_space_; diff --git a/chromium/cc/layers/picture_layer_impl_unittest.cc b/chromium/cc/layers/picture_layer_impl_unittest.cc index 2631d7ed1e5..25fff7cb2ae 100644 --- a/chromium/cc/layers/picture_layer_impl_unittest.cc +++ b/chromium/cc/layers/picture_layer_impl_unittest.cc @@ -13,6 +13,7 @@ #include "base/location.h" #include "base/stl_util.h" +#include "base/test/metrics/histogram_tester.h" #include "base/threading/thread_task_runner_handle.h" #include "cc/animation/animation_host.h" #include "cc/base/math_util.h" @@ -2880,6 +2881,66 @@ TEST_F(LegacySWPictureLayerImplTest, HighResTilingDuringAnimation) { EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 11.f); } +TEST_F(LegacySWPictureLayerImplTest, + AnimationTilingChangesWithWillChangeTransformHint) { + gfx::Size viewport_size(1000, 1000); + host_impl()->active_tree()->SetDeviceViewportRect(gfx::Rect(viewport_size)); + + gfx::Size layer_bounds(100, 100); + SetupDefaultTrees(layer_bounds); + + float contents_scale = 1.f; + float device_scale = 1.f; + float page_scale = 1.f; + float maximum_animation_scale = 1.f; + float starting_animation_scale = 0.f; + bool animating_transform = false; + + EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 1.f); + + active_layer()->SetHasWillChangeTransformHint(true); + pending_layer()->SetHasWillChangeTransformHint(true); + + // Starting an animation should cause tiling resolution to get set to the + // maximum animation scale factor. + animating_transform = true; + maximum_animation_scale = 2.f; + contents_scale = 1.f; + + SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale, + maximum_animation_scale, + starting_animation_scale, animating_transform); + EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 2.f); + + // Once we stop animating, because we have a will-change: transform hint + // we should not reset the scale factor. + animating_transform = false; + + SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale, + maximum_animation_scale, + starting_animation_scale, animating_transform); + EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 2.f); + + // Starting an animation with a different maximum animation scale should + // not cause a change either. + animating_transform = true; + maximum_animation_scale = 1.5f; + + SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale, + maximum_animation_scale, + starting_animation_scale, animating_transform); + EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 2.f); + + // Again, stop animating, because we have a will-change: transform hint + // we should not reset the scale factor. + animating_transform = false; + + SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale, + maximum_animation_scale, + starting_animation_scale, animating_transform); + EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 2.f); +} + TEST_F(LegacySWPictureLayerImplTest, HighResTilingDuringAnimationAspectRatio) { gfx::Size viewport_size(2000, 1000); host_impl()->active_tree()->SetDeviceViewportRect(gfx::Rect(viewport_size)); @@ -3441,6 +3502,9 @@ TEST_F(LegacySWPictureLayerImplTest, RasterScaleChangeWithoutAnimation) { EXPECT_BOTH_EQ(HighResTiling()->contents_scale_key(), 3.f); } +TEST_F(LegacySWPictureLayerImplTest, + AnimationChangeRespectsWillChangeTransformHint) {} + TEST_F(LegacySWPictureLayerImplTest, LowResReadyToDrawNotEnoughToActivate) { gfx::Size tile_size(100, 100); gfx::Size layer_bounds(1000, 1000); @@ -5326,6 +5390,328 @@ TEST_F(LegacySWPictureLayerImplTest, CompositedImageRasterScaleChanges) { } } +TEST_F(LegacySWPictureLayerImplTest, CompositedImageRasterOnChange) { + gfx::Size layer_bounds(400, 400); + scoped_refptr<FakeRasterSource> pending_raster_source = + FakeRasterSource::CreateFilled(layer_bounds); + + SetupPendingTree(pending_raster_source); + + // Set an image size that is smaller than the layer bounds. + gfx::Size image_size(200, 200); + pending_layer()->SetDirectlyCompositedImageSize(image_size); + SetupDrawPropertiesAndUpdateTiles(pending_layer(), 1.f, 1.f, 1.f, 1.f, 1.f, + false); + EXPECT_FLOAT_EQ(0.5f, pending_layer() + ->picture_layer_tiling_set() + ->FindTilingWithResolution(HIGH_RESOLUTION) + ->contents_scale_key()); + + // Change the bounds and ensure we recalculated raster scale. + pending_layer()->SetBounds(gfx::Size(320, 320)); + SetupDrawPropertiesAndUpdateTiles(pending_layer(), 1.f, 1.f, 1.f, 1.f, 1.f, + false); + EXPECT_FLOAT_EQ(0.625f, pending_layer() + ->picture_layer_tiling_set() + ->FindTilingWithResolution(HIGH_RESOLUTION) + ->contents_scale_key()); + + // Set an image size much larger than the layer bounds (5x). Verify that the + // scaling down code is triggered (we should halve the raster scale until it + // is less than 4x the ideal source scale). + pending_layer()->SetBounds(layer_bounds); + pending_layer()->SetDirectlyCompositedImageSize(gfx::Size(2000, 2000)); + SetupDrawPropertiesAndUpdateTiles(pending_layer(), 1.f, 1.f, 1.f, 1.f, 1.f, + false); + EXPECT_FLOAT_EQ(2.5f, pending_layer() + ->picture_layer_tiling_set() + ->FindTilingWithResolution(HIGH_RESOLUTION) + ->contents_scale_key()); + + // Update the bounds to no longer match the aspect ratio, but still compute + // the same raster scale. + pending_layer()->SetBounds(gfx::Size(600, 500)); + SetupDrawPropertiesAndUpdateTiles(pending_layer(), 1.f, 1.f, 1.f, 1.f, 1.f, + false); + EXPECT_FLOAT_EQ(4.f, pending_layer() + ->picture_layer_tiling_set() + ->FindTilingWithResolution(HIGH_RESOLUTION) + ->contents_scale_key()); + + // Update the bounds and and bump up the ideal scale so that the scale down + // restriction is lifted. + pending_layer()->SetBounds(layer_bounds); + SetupDrawPropertiesAndUpdateTiles(pending_layer(), 4.f, 1.f, 1.f, 1.f, 1.f, + false); + EXPECT_FLOAT_EQ(5.f, pending_layer() + ->picture_layer_tiling_set() + ->FindTilingWithResolution(HIGH_RESOLUTION) + ->contents_scale_key()); + + // Lower the ideal scale to see that the clamping still applied as it is + // lowered. + SetupDrawPropertiesAndUpdateTiles(pending_layer(), 0.5f, 1.f, 1.f, 1.f, 1.f, + false); + EXPECT_FLOAT_EQ(1.25f, pending_layer() + ->picture_layer_tiling_set() + ->FindTilingWithResolution(HIGH_RESOLUTION) + ->contents_scale_key()); + + SetupDrawPropertiesAndUpdateTiles(pending_layer(), 0.25f, 1.f, 1.f, 1.f, 1.f, + false); + EXPECT_FLOAT_EQ(0.625f, pending_layer() + ->picture_layer_tiling_set() + ->FindTilingWithResolution(HIGH_RESOLUTION) + ->contents_scale_key()); +} + +TEST_F(LegacySWPictureLayerImplTest, CompositedImageRasterOptOutTransitions) { + gfx::Size layer_bounds(5, 5); + scoped_refptr<FakeRasterSource> pending_raster_source = + FakeRasterSource::CreateFilled(layer_bounds); + + SetupPendingTree(pending_raster_source); + + // Start with image and bounds matching to have this layer initially opted + // in to directly composited images. + pending_layer()->SetBounds(layer_bounds); + pending_layer()->SetDirectlyCompositedImageSize(layer_bounds); + SetupDrawPropertiesAndUpdateTiles(pending_layer(), 0.3f, 1.f, 1.f, 1.f, 1.f, + false); + EXPECT_FLOAT_EQ(1.f, pending_layer() + ->picture_layer_tiling_set() + ->FindTilingWithResolution(HIGH_RESOLUTION) + ->contents_scale_key()); + + // Change the image and bounds to values that make the layer not eligible for + // direct compositing. This must be reflected by a |contents_scale_key()| of + // 0.1f (matching the ideal source scale). + gfx::Size image_size(300, 300); + pending_layer()->SetDirectlyCompositedImageSize(image_size); + SetupDrawPropertiesAndUpdateTiles(pending_layer(), 0.2f, 1.f, 1.f, 1.f, 1.f, + false); + EXPECT_FLOAT_EQ(0.2f, pending_layer() + ->picture_layer_tiling_set() + ->FindTilingWithResolution(HIGH_RESOLUTION) + ->contents_scale_key()); + + // Ensure we get back to a directly composited image if the input values + // change such that the optimization should apply. + pending_layer()->SetBounds(ScaleToFlooredSize(layer_bounds, 2)); + pending_layer()->SetDirectlyCompositedImageSize( + ScaleToFlooredSize(image_size, 2)); + SetupDrawPropertiesAndUpdateTiles(pending_layer(), 0.2f, 1.f, 1.f, 1.f, 1.f, + false); + EXPECT_FLOAT_EQ(0.46875, pending_layer() + ->picture_layer_tiling_set() + ->FindTilingWithResolution(HIGH_RESOLUTION) + ->contents_scale_key()); +} + +TEST_F(LegacySWPictureLayerImplTest, CompositedImageHistograms) { + base::HistogramTester histogram_tester; + + gfx::Size layer_bounds(5, 5); + scoped_refptr<FakeRasterSource> pending_raster_source = + FakeRasterSource::CreateFilled(layer_bounds); + + SetupPendingTree(pending_raster_source); + + // Set the image and bounds to values that make the layer not eligible for + // direct compositing. This must be reflected by a |contents_scale_key()| of + // 0.2f (matching the ideal source scale). + gfx::Size image_size(300, 300); + pending_layer()->SetDirectlyCompositedImageSize(image_size); + SetupDrawPropertiesAndUpdateTiles(pending_layer(), 0.2f, 1.f, 1.f, 1.f, 1.f, + false); + EXPECT_FLOAT_EQ(0.2f, pending_layer() + ->picture_layer_tiling_set() + ->FindTilingWithResolution(HIGH_RESOLUTION) + ->contents_scale_key()); + histogram_tester.ExpectBucketCount( + "Compositing.Renderer.DirectlyCompositedImage." + "RasterScaleDirectlyComposited", + false, 1); + histogram_tester.ExpectBucketCount( + "Compositing.Renderer.DirectlyCompositedImage." + "RasterScaleDirectlyComposited", + true, 0); + + // At ideal scale of 1, we save 270000 pixels for a 600x600 layer directly + // compositing a 300x300 image. + pending_layer()->SetBounds(gfx::Size(600, 600)); + pending_layer()->SetDirectlyCompositedImageSize(image_size); + SetupDrawPropertiesAndUpdateTiles(pending_layer(), 1.f, 1.f, 1.f, 1.f, 1.f, + false); + histogram_tester.ExpectBucketCount( + "Compositing.Renderer.DirectlyCompositedImage." + "TileAreaSaved", + 270000, 1); + histogram_tester.ExpectTotalCount( + "Compositing.Renderer.DirectlyCompositedImage." + "TileAreaSaved", + 1); + histogram_tester.ExpectTotalCount( + "Compositing.Renderer.DirectlyCompositedImage." + "TileAreaAdded", + 0); + histogram_tester.ExpectBucketCount( + "Compositing.Renderer.DirectlyCompositedImage." + "TileAreaMatches", + true, 0); + histogram_tester.ExpectBucketCount( + "Compositing.Renderer.DirectlyCompositedImage." + "TileAreaMatches", + false, 1); + histogram_tester.ExpectBucketCount( + "Compositing.Renderer.DirectlyCompositedImage." + "RasterScaleDirectlyComposited", + true, 1); + + // Matching bounds should log TileAreaMatches. + pending_layer()->SetDirectlyCompositedImageSize(gfx::Size(600, 600)); + SetupDrawPropertiesAndUpdateTiles(pending_layer(), 1.f, 1.f, 1.f, 1.f, 1.f, + false); + histogram_tester.ExpectBucketCount( + "Compositing.Renderer.DirectlyCompositedImage." + "TileAreaMatches", + true, 1); + histogram_tester.ExpectBucketCount( + "Compositing.Renderer.DirectlyCompositedImage." + "TileAreaMatches", + false, 1); + histogram_tester.ExpectTotalCount( + "Compositing.Renderer.DirectlyCompositedImage." + "TileAreaSaved", + 1); + histogram_tester.ExpectTotalCount( + "Compositing.Renderer.DirectlyCompositedImage." + "TileAreaAdded", + 0); + histogram_tester.ExpectBucketCount( + "Compositing.Renderer.DirectlyCompositedImage." + "RasterScaleDirectlyComposited", + true, 2); + + // Changing the bounds to be smaller than the image should add a TileAreaAdded + // histogram bucket count. + pending_layer()->SetBounds(gfx::Size(300, 300)); + SetupDrawPropertiesAndUpdateTiles(pending_layer(), 1.f, 1.f, 1.f, 1.f, 1.f, + false); + histogram_tester.ExpectBucketCount( + "Compositing.Renderer.DirectlyCompositedImage." + "TileAreaAdded", + 270000, 1); + histogram_tester.ExpectTotalCount( + "Compositing.Renderer.DirectlyCompositedImage." + "TileAreaSaved", + 1); + histogram_tester.ExpectTotalCount( + "Compositing.Renderer.DirectlyCompositedImage." + "TileAreaAdded", + 1); + histogram_tester.ExpectBucketCount( + "Compositing.Renderer.DirectlyCompositedImage." + "TileAreaMatches", + true, 1); + histogram_tester.ExpectBucketCount( + "Compositing.Renderer.DirectlyCompositedImage." + "TileAreaMatches", + false, 2); + histogram_tester.ExpectBucketCount( + "Compositing.Renderer.DirectlyCompositedImage." + "RasterScaleDirectlyComposited", + true, 3); + + // So far we haven't avoided any re-raster (as sizes have been updated at + // every stage of the test). Similarly, just updating tiles with no scale + // changes should not result in those histograms being logged. + SetupDrawPropertiesAndUpdateTiles(pending_layer(), 1.f, 1.f, 1.f, 1.f, 1.f, + false); + histogram_tester.ExpectBucketCount( + "Compositing.Renderer.DirectlyCompositedImage." + "AvoidRasterAdjustmentWithTransformTrigger", + false, 0); + histogram_tester.ExpectBucketCount( + "Compositing.Renderer.DirectlyCompositedImage." + "AvoidRasterAdjustmentWithTransformTrigger", + true, 0); + + // Reduce the ideal scale - directly composited image should not re-raster. + // Neither will-change:transform or active transformation so the 'false' + // bucket should get an entry. + SetupDrawPropertiesAndUpdateTiles(pending_layer(), 0.5f, 1.f, 1.f, 1.f, 1.f, + false); + histogram_tester.ExpectBucketCount( + "Compositing.Renderer.DirectlyCompositedImage." + "AvoidRasterAdjustmentWithTransformTrigger", + false, 1); + histogram_tester.ExpectBucketCount( + "Compositing.Renderer.DirectlyCompositedImage." + "AvoidRasterAdjustmentWithTransformTrigger", + true, 0); + + // Set will-change:transform and update tiles with a different ideal scale. + pending_layer()->SetHasWillChangeTransformHint(true); + SetupDrawPropertiesAndUpdateTiles(pending_layer(), 0.6f, 1.f, 1.f, 1.f, 1.f, + false); + histogram_tester.ExpectBucketCount( + "Compositing.Renderer.DirectlyCompositedImage." + "AvoidRasterAdjustmentWithTransformTrigger", + false, 1); + histogram_tester.ExpectBucketCount( + "Compositing.Renderer.DirectlyCompositedImage." + "AvoidRasterAdjustmentWithTransformTrigger", + true, 1); + + // None of the operations past the first one should have incremented this + // histogram. + histogram_tester.ExpectBucketCount( + "Compositing.Renderer.DirectlyCompositedImage." + "RasterScaleDirectlyComposited", + false, 1); +} + +TEST_F(LegacySWPictureLayerImplTest, CompositedImageHistogramsOverflow) { + base::HistogramTester histogram_tester; + + constexpr int kLargeDimension = std::numeric_limits<int>::max() / 2; + gfx::Size layer_bounds(kLargeDimension, kLargeDimension); + scoped_refptr<FakeRasterSource> pending_raster_source = + FakeRasterSource::CreateFilled(layer_bounds); + + // Overflow the area calculation for the histograms - we should still get + // the correct high res scale key, but no histograms should be logged. + SetupPendingTree(pending_raster_source); + pending_layer()->SetDirectlyCompositedImageSize(layer_bounds); + SetupDrawPropertiesAndUpdateTiles(pending_layer(), 1.f, 1.f, 1.f, 1.f, 1.f, + false); + EXPECT_FLOAT_EQ(1.f, pending_layer() + ->picture_layer_tiling_set() + ->FindTilingWithResolution(HIGH_RESOLUTION) + ->contents_scale_key()); + histogram_tester.ExpectTotalCount( + "Compositing.Renderer.DirectlyCompositedImage." + "TileAreaSaved", + 0); + histogram_tester.ExpectTotalCount( + "Compositing.Renderer.DirectlyCompositedImage." + "TileAreaAdded", + 0); + histogram_tester.ExpectTotalCount( + "Compositing.Renderer.DirectlyCompositedImage." + "TileAreaMatches", + 0); + histogram_tester.ExpectBucketCount( + "Compositing.Renderer.DirectlyCompositedImage." + "RasterScaleDirectlyComposited", + true, 1); + histogram_tester.ExpectBucketCount( + "Compositing.Renderer.DirectlyCompositedImage." + "RasterScaleDirectlyComposited", + false, 0); +} + TEST_F(LegacySWPictureLayerImplTest, ChangeRasterTranslationNukePendingLayerTiles) { gfx::Size layer_bounds(200, 200); diff --git a/chromium/cc/layers/recording_source.h b/chromium/cc/layers/recording_source.h index d09d8db06fe..cd4c2d51e5a 100644 --- a/chromium/cc/layers/recording_source.h +++ b/chromium/cc/layers/recording_source.h @@ -63,7 +63,6 @@ class CC_EXPORT RecordingSource { int slow_down_raster_scale_factor_for_debug_; bool requires_clear_; bool is_solid_color_; - bool clear_canvas_with_debug_color_; SkColor solid_color_; SkColor background_color_; scoped_refptr<DisplayItemList> display_list_; diff --git a/chromium/cc/layers/render_surface_impl.cc b/chromium/cc/layers/render_surface_impl.cc index e6d2862ae3c..3d60067a835 100644 --- a/chromium/cc/layers/render_surface_impl.cc +++ b/chromium/cc/layers/render_surface_impl.cc @@ -8,7 +8,7 @@ #include <algorithm> -#include "base/logging.h" +#include "base/check_op.h" #include "base/strings/stringprintf.h" #include "cc/base/math_util.h" #include "cc/debug/debug_colors.h" @@ -110,10 +110,6 @@ SkBlendMode RenderSurfaceImpl::BlendMode() const { return OwningEffectNode()->blend_mode; } -bool RenderSurfaceImpl::UsesDefaultBlendMode() const { - return BlendMode() == SkBlendMode::kSrcOver; -} - SkColor RenderSurfaceImpl::GetDebugBorderColor() const { return DebugColors::SurfaceBorderColor(); } diff --git a/chromium/cc/layers/render_surface_impl.h b/chromium/cc/layers/render_surface_impl.h index 351ad5969f8..72587093ba7 100644 --- a/chromium/cc/layers/render_surface_impl.h +++ b/chromium/cc/layers/render_surface_impl.h @@ -62,7 +62,6 @@ class CC_EXPORT RenderSurfaceImpl { } SkBlendMode BlendMode() const; - bool UsesDefaultBlendMode() const; void SetNearestOcclusionImmuneAncestor(const RenderSurfaceImpl* surface) { nearest_occlusion_immune_ancestor_ = surface; diff --git a/chromium/cc/layers/scrollbar_layer_base.cc b/chromium/cc/layers/scrollbar_layer_base.cc index e676c3eed1e..aa7c2f0c435 100644 --- a/chromium/cc/layers/scrollbar_layer_base.cc +++ b/chromium/cc/layers/scrollbar_layer_base.cc @@ -4,19 +4,60 @@ #include "cc/layers/scrollbar_layer_base.h" +#include "cc/layers/painted_overlay_scrollbar_layer.h" +#include "cc/layers/painted_scrollbar_layer.h" #include "cc/layers/scrollbar_layer_impl_base.h" +#include "cc/layers/solid_color_scrollbar_layer.h" namespace cc { ScrollbarLayerBase::ScrollbarLayerBase(ScrollbarOrientation orientation, bool is_left_side_vertical_scrollbar) : orientation_(orientation), - is_left_side_vertical_scrollbar_(is_left_side_vertical_scrollbar) { - SetIsScrollbar(true); -} + is_left_side_vertical_scrollbar_(is_left_side_vertical_scrollbar) {} ScrollbarLayerBase::~ScrollbarLayerBase() = default; +scoped_refptr<ScrollbarLayerBase> ScrollbarLayerBase::CreateOrReuse( + scoped_refptr<Scrollbar> scrollbar, + ScrollbarLayerBase* existing_layer) { + DCHECK(scrollbar); + ScrollbarLayerType needed_type = kPainted; + if (scrollbar->IsSolidColor()) { + needed_type = kSolidColor; + } else if (scrollbar->UsesNinePatchThumbResource()) { + DCHECK(scrollbar->IsOverlay()); + needed_type = kPaintedOverlay; + } + + if (existing_layer && + (existing_layer->GetScrollbarLayerType() != needed_type || + // We don't support change of these fields in a layer. + existing_layer->orientation() != scrollbar->Orientation() || + existing_layer->is_left_side_vertical_scrollbar() != + scrollbar->IsLeftSideVerticalScrollbar())) { + existing_layer = nullptr; + } + + switch (needed_type) { + case kSolidColor: + return SolidColorScrollbarLayer::CreateOrReuse( + std::move(scrollbar), + static_cast<SolidColorScrollbarLayer*>(existing_layer)); + case kPainted: + return PaintedScrollbarLayer::CreateOrReuse( + std::move(scrollbar), + static_cast<PaintedScrollbarLayer*>(existing_layer)); + case kPaintedOverlay: + return PaintedOverlayScrollbarLayer::CreateOrReuse( + std::move(scrollbar), + static_cast<PaintedOverlayScrollbarLayer*>(existing_layer)); + } + + NOTREACHED(); + return nullptr; +} + void ScrollbarLayerBase::SetScrollElementId(ElementId element_id) { if (element_id == scroll_element_id_) return; @@ -35,4 +76,8 @@ void ScrollbarLayerBase::PushPropertiesTo(LayerImpl* layer) { scrollbar_layer_impl->SetScrollElementId(scroll_element_id_); } +bool ScrollbarLayerBase::IsScrollbarLayerForTesting() const { + return true; +} + } // namespace cc diff --git a/chromium/cc/layers/scrollbar_layer_base.h b/chromium/cc/layers/scrollbar_layer_base.h index b7c6efe3967..394e5087acb 100644 --- a/chromium/cc/layers/scrollbar_layer_base.h +++ b/chromium/cc/layers/scrollbar_layer_base.h @@ -12,6 +12,10 @@ namespace cc { class CC_EXPORT ScrollbarLayerBase : public Layer { public: + static scoped_refptr<ScrollbarLayerBase> CreateOrReuse( + scoped_refptr<Scrollbar>, + ScrollbarLayerBase* existing_layer); + void SetScrollElementId(ElementId element_id); ElementId scroll_element_id() const { return scroll_element_id_; } @@ -27,7 +31,7 @@ class CC_EXPORT ScrollbarLayerBase : public Layer { kPainted, kPaintedOverlay, }; - virtual ScrollbarLayerType ScrollbarLayerTypeForTesting() const = 0; + virtual ScrollbarLayerType GetScrollbarLayerType() const = 0; protected: ScrollbarLayerBase(ScrollbarOrientation orientation, @@ -35,6 +39,8 @@ class CC_EXPORT ScrollbarLayerBase : public Layer { ~ScrollbarLayerBase() override; private: + bool IsScrollbarLayerForTesting() const final; + const ScrollbarOrientation orientation_; const bool is_left_side_vertical_scrollbar_; ElementId scroll_element_id_; diff --git a/chromium/cc/layers/scrollbar_layer_impl_base.cc b/chromium/cc/layers/scrollbar_layer_impl_base.cc index bc9eabcedbe..2d9e885c030 100644 --- a/chromium/cc/layers/scrollbar_layer_impl_base.cc +++ b/chromium/cc/layers/scrollbar_layer_impl_base.cc @@ -28,9 +28,7 @@ ScrollbarLayerImplBase::ScrollbarLayerImplBase( scroll_layer_length_(0.f), orientation_(orientation), is_left_side_vertical_scrollbar_(is_left_side_vertical_scrollbar), - vertical_adjust_(0.f) { - set_is_scrollbar(true); -} + vertical_adjust_(0.f) {} ScrollbarLayerImplBase::~ScrollbarLayerImplBase() { layer_tree_impl()->UnregisterScrollbar(this); @@ -38,13 +36,14 @@ ScrollbarLayerImplBase::~ScrollbarLayerImplBase() { void ScrollbarLayerImplBase::PushPropertiesTo(LayerImpl* layer) { LayerImpl::PushPropertiesTo(layer); - DCHECK(layer->ToScrollbarLayer()); - layer->ToScrollbarLayer()->set_is_overlay_scrollbar(is_overlay_scrollbar_); - layer->ToScrollbarLayer()->SetScrollElementId(scroll_element_id()); + DCHECK(layer->IsScrollbarLayer()); + ScrollbarLayerImplBase* scrollbar_layer = ToScrollbarLayer(layer); + scrollbar_layer->set_is_overlay_scrollbar(is_overlay_scrollbar_); + scrollbar_layer->SetScrollElementId(scroll_element_id()); } -ScrollbarLayerImplBase* ScrollbarLayerImplBase::ToScrollbarLayer() { - return this; +bool ScrollbarLayerImplBase::IsScrollbarLayer() const { + return true; } void ScrollbarLayerImplBase::SetScrollElementId(ElementId scroll_element_id) { diff --git a/chromium/cc/layers/scrollbar_layer_impl_base.h b/chromium/cc/layers/scrollbar_layer_impl_base.h index 23a31d35754..3b508f3c17a 100644 --- a/chromium/cc/layers/scrollbar_layer_impl_base.h +++ b/chromium/cc/layers/scrollbar_layer_impl_base.h @@ -49,7 +49,6 @@ class CC_EXPORT ScrollbarLayerImplBase : public LayerImpl { bool CanScrollOrientation() const; void PushPropertiesTo(LayerImpl* layer) override; - ScrollbarLayerImplBase* ToScrollbarLayer() override; // Thumb quad rect in layer space. gfx::Rect ComputeThumbQuadRect() const; @@ -99,6 +98,8 @@ class CC_EXPORT ScrollbarLayerImplBase : public LayerImpl { virtual bool IsThumbResizable() const = 0; private: + bool IsScrollbarLayer() const final; + gfx::Rect ComputeThumbQuadRectWithThumbThicknessScale( float thumb_thickness_scale_factor) const; @@ -120,6 +121,11 @@ class CC_EXPORT ScrollbarLayerImplBase : public LayerImpl { ScrollElementIdPushedAcrossCommit); }; +inline ScrollbarLayerImplBase* ToScrollbarLayer(LayerImpl* layer) { + DCHECK(layer->IsScrollbarLayer()); + return static_cast<ScrollbarLayerImplBase*>(layer); +} + using ScrollbarSet = base::flat_set<ScrollbarLayerImplBase*>; } // namespace cc diff --git a/chromium/cc/layers/solid_color_scrollbar_layer.cc b/chromium/cc/layers/solid_color_scrollbar_layer.cc index e910b1c72ee..d446a39fc2f 100644 --- a/chromium/cc/layers/solid_color_scrollbar_layer.cc +++ b/chromium/cc/layers/solid_color_scrollbar_layer.cc @@ -18,6 +18,32 @@ std::unique_ptr<LayerImpl> SolidColorScrollbarLayer::CreateLayerImpl( is_left_side_vertical_scrollbar()); } +scoped_refptr<SolidColorScrollbarLayer> SolidColorScrollbarLayer::CreateOrReuse( + scoped_refptr<Scrollbar> scrollbar, + SolidColorScrollbarLayer* existing_layer) { + DCHECK(scrollbar->IsOverlay()); + bool is_horizontal = scrollbar->Orientation() == HORIZONTAL; + gfx::Rect thumb_rect = scrollbar->ThumbRect(); + int thumb_thickness = + is_horizontal ? thumb_rect.height() : thumb_rect.width(); + gfx::Rect track_rect = scrollbar->TrackRect(); + int track_start = is_horizontal ? track_rect.x() : track_rect.y(); + + if (existing_layer && + // We don't support change of these fields in a layer. + existing_layer->thumb_thickness() == thumb_thickness && + existing_layer->track_start() == track_start) { + // These fields have been checked in ScrollbarLayerBase::CreateOrReuse(). + DCHECK_EQ(scrollbar->Orientation(), existing_layer->orientation()); + DCHECK_EQ(scrollbar->IsLeftSideVerticalScrollbar(), + existing_layer->is_left_side_vertical_scrollbar()); + return existing_layer; + } + + return Create(scrollbar->Orientation(), thumb_thickness, track_start, + scrollbar->IsLeftSideVerticalScrollbar()); +} + scoped_refptr<SolidColorScrollbarLayer> SolidColorScrollbarLayer::Create( ScrollbarOrientation orientation, int thumb_thickness, @@ -62,7 +88,7 @@ bool SolidColorScrollbarLayer::HitTestable() const { } ScrollbarLayerBase::ScrollbarLayerType -SolidColorScrollbarLayer::ScrollbarLayerTypeForTesting() const { +SolidColorScrollbarLayer::GetScrollbarLayerType() const { return kSolidColor; } diff --git a/chromium/cc/layers/solid_color_scrollbar_layer.h b/chromium/cc/layers/solid_color_scrollbar_layer.h index 8931b9c90b6..c0cd2ee49f5 100644 --- a/chromium/cc/layers/solid_color_scrollbar_layer.h +++ b/chromium/cc/layers/solid_color_scrollbar_layer.h @@ -17,6 +17,10 @@ class CC_EXPORT SolidColorScrollbarLayer : public ScrollbarLayerBase { public: std::unique_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) override; + static scoped_refptr<SolidColorScrollbarLayer> CreateOrReuse( + scoped_refptr<Scrollbar>, + SolidColorScrollbarLayer* existing_layer); + static scoped_refptr<SolidColorScrollbarLayer> Create( ScrollbarOrientation orientation, int thumb_thickness, @@ -35,7 +39,7 @@ class CC_EXPORT SolidColorScrollbarLayer : public ScrollbarLayerBase { int thumb_thickness() const { return thumb_thickness_; } int track_start() const { return track_start_; } - ScrollbarLayerType ScrollbarLayerTypeForTesting() const override; + ScrollbarLayerType GetScrollbarLayerType() const override; private: SolidColorScrollbarLayer(ScrollbarOrientation orientation, diff --git a/chromium/cc/layers/surface_layer.cc b/chromium/cc/layers/surface_layer.cc index ec1c8646a40..baf7ef5f8de 100644 --- a/chromium/cc/layers/surface_layer.cc +++ b/chromium/cc/layers/surface_layer.cc @@ -119,13 +119,13 @@ void SurfaceLayer::SetIsReflection(bool is_reflection) { void SurfaceLayer::SetMayContainVideo(bool may_contain_video) { may_contain_video_ = may_contain_video; + SetNeedsCommit(); } std::unique_ptr<LayerImpl> SurfaceLayer::CreateLayerImpl( LayerTreeImpl* tree_impl) { auto layer_impl = SurfaceLayerImpl::Create(tree_impl, id(), update_submission_state_callback_); - layer_impl->set_may_contain_video(may_contain_video_); return layer_impl; } @@ -158,6 +158,7 @@ void SurfaceLayer::PushPropertiesTo(LayerImpl* layer) { layer_impl->SetStretchContentToFillBounds(stretch_content_to_fill_bounds_); layer_impl->SetSurfaceHitTestable(surface_hit_testable_); layer_impl->SetHasPointerEventsNone(has_pointer_events_none_); + layer_impl->set_may_contain_video(may_contain_video_); } } // namespace cc diff --git a/chromium/cc/layers/video_layer_impl.cc b/chromium/cc/layers/video_layer_impl.cc index b5f67bad210..4441f94fcfb 100644 --- a/chromium/cc/layers/video_layer_impl.cc +++ b/chromium/cc/layers/video_layer_impl.cc @@ -10,7 +10,7 @@ #include <utility> #include "base/bind.h" -#include "base/logging.h" +#include "base/check.h" #include "base/memory/ptr_util.h" #include "cc/layers/video_frame_provider_client_impl.h" #include "cc/trees/layer_tree_frame_sink.h" diff --git a/chromium/cc/layers/viewport.cc b/chromium/cc/layers/viewport.cc index fab6235d29f..46b5f15dd64 100644 --- a/chromium/cc/layers/viewport.cc +++ b/chromium/cc/layers/viewport.cc @@ -4,7 +4,7 @@ #include "cc/layers/viewport.h" -#include "base/logging.h" +#include "base/check.h" #include "base/memory/ptr_util.h" #include "cc/input/browser_controls_offset_manager.h" #include "cc/trees/layer_tree_host_impl.h" diff --git a/chromium/cc/metrics/compositor_frame_reporter.cc b/chromium/cc/metrics/compositor_frame_reporter.cc index 68592504171..13c15d9b1dd 100644 --- a/chromium/cc/metrics/compositor_frame_reporter.cc +++ b/chromium/cc/metrics/compositor_frame_reporter.cc @@ -21,11 +21,12 @@ namespace cc { namespace { using StageType = CompositorFrameReporter::StageType; +using FrameReportType = CompositorFrameReporter::FrameReportType; using BlinkBreakdown = CompositorFrameReporter::BlinkBreakdown; using VizBreakdown = CompositorFrameReporter::VizBreakdown; constexpr int kFrameReportTypeCount = - static_cast<int>(CompositorFrameReporter::FrameReportType::kMaxValue) + 1; + static_cast<int>(FrameReportType::kMaxValue) + 1; constexpr int kStageTypeCount = static_cast<int>(StageType::kStageTypeCount); constexpr int kAllBreakdownCount = static_cast<int>(VizBreakdown::kBreakdownCount) + @@ -62,11 +63,16 @@ constexpr const char* GetVizBreakdownName(VizBreakdown stage) { // Names for CompositorFrameReporter::StageType, which should be updated in case // of changes to the enum. -constexpr const char* GetStageName(int stage_type_index) { +constexpr const char* GetStageName(int stage_type_index, + bool impl_only = false) { switch (stage_type_index) { case static_cast<int>(StageType::kBeginImplFrameToSendBeginMainFrame): + if (impl_only) + return "BeginImplFrameToFinishImpl"; return "BeginImplFrameToSendBeginMainFrame"; case static_cast<int>(StageType::kSendBeginMainFrameToCommit): + if (impl_only) + return "SendBeginMainFrameToBeginMainAbort"; return "SendBeginMainFrameToCommit"; case static_cast<int>(StageType::kCommit): return "Commit"; @@ -75,6 +81,8 @@ constexpr const char* GetStageName(int stage_type_index) { case static_cast<int>(StageType::kActivation): return "Activation"; case static_cast<int>(StageType::kEndActivateToSubmitCompositorFrame): + if (impl_only) + return "ImplFrameDoneToSubmitCompositorFrame"; return "EndActivateToSubmitCompositorFrame"; case static_cast<int>( StageType::kSubmitCompositorFrameToPresentationCompositorFrame): @@ -140,8 +148,8 @@ constexpr const char* GetStageName(int stage_type_index) { // Names for CompositorFrameReporter::FrameReportType, which should be // updated in case of changes to the enum. -constexpr const char* kReportTypeNames[]{"", "MissedDeadlineFrame.", - "DroppedFrame."}; +constexpr const char* kReportTypeNames[]{ + "", "MissedDeadlineFrame.", "DroppedFrame.", "CompositorOnlyFrame."}; static_assert(base::size(kReportTypeNames) == kFrameReportTypeCount, "Compositor latency report types has changed."); @@ -159,9 +167,11 @@ constexpr int kCompositorLatencyHistogramBucketCount = 50; constexpr int kEventLatencyEventTypeCount = static_cast<int>(ui::EventType::ET_LAST); constexpr int kEventLatencyScrollTypeCount = - static_cast<int>(ScrollInputType::kMaxValue) + 1; -constexpr int kMaxEventLatencyHistogramIndex = + static_cast<int>(ui::ScrollInputType::kMaxValue) + 1; +constexpr int kMaxEventLatencyHistogramBaseIndex = kEventLatencyEventTypeCount * kEventLatencyScrollTypeCount; +constexpr int kMaxEventLatencyHistogramIndex = + kMaxEventLatencyHistogramBaseIndex * (kStageTypeCount + kAllBreakdownCount); constexpr int kEventLatencyHistogramMin = 1; constexpr int kEventLatencyHistogramMax = 5000000; constexpr int kEventLatencyHistogramBucketCount = 100; @@ -182,10 +192,13 @@ std::string GetCompositorLatencyHistogramName( FrameSequenceTracker::GetFrameSequenceTrackerTypeName( frame_sequence_tracker_type); DCHECK(tracker_type_name); + bool impl_only_frame = + (report_type_index == + static_cast<int>(FrameReportType::kCompositorOnlyFrame)); return base::StrCat({"CompositorLatency.", kReportTypeNames[report_type_index], tracker_type_name, *tracker_type_name ? "." : "", - GetStageName(stage_type_index)}); + GetStageName(stage_type_index, impl_only_frame)}); } std::string GetEventLatencyHistogramBaseName( @@ -199,7 +212,7 @@ std::string GetEventLatencyHistogramBaseName( } // namespace CompositorFrameReporter::CompositorFrameReporter( - const base::flat_set<FrameSequenceTrackerType>* active_trackers, + const ActiveTrackers& active_trackers, const viz::BeginFrameId& id, const base::TimeTicks frame_deadline, LatencyUkmReporter* latency_ukm_reporter, @@ -215,16 +228,19 @@ CompositorFrameReporter::CopyReporterAtBeginImplStage() const { if (stage_history_.empty() || stage_history_.front().stage_type != StageType::kBeginImplFrameToSendBeginMainFrame || - !did_finish_impl_frame()) + !did_finish_impl_frame()) { return nullptr; + } auto new_reporter = std::make_unique<CompositorFrameReporter>( active_trackers_, frame_id_, frame_deadline_, latency_ukm_reporter_, should_report_metrics_); new_reporter->did_finish_impl_frame_ = did_finish_impl_frame_; new_reporter->impl_frame_finish_time_ = impl_frame_finish_time_; + new_reporter->main_frame_abort_time_ = main_frame_abort_time_; new_reporter->current_stage_.stage_type = StageType::kBeginImplFrameToSendBeginMainFrame; new_reporter->current_stage_.start_time = stage_history_.front().start_time; + new_reporter->set_tick_clock(tick_clock_); return new_reporter; } @@ -248,14 +264,18 @@ void CompositorFrameReporter::StartStage( EndCurrentStage(start_time); current_stage_.stage_type = stage_type; current_stage_.start_time = start_time; -} - -void CompositorFrameReporter::EndCurrentStage(base::TimeTicks end_time) { - if (current_stage_.start_time == base::TimeTicks()) - return; - current_stage_.end_time = end_time; - stage_history_.push_back(current_stage_); - current_stage_.start_time = base::TimeTicks(); + switch (stage_type) { + case StageType::kSendBeginMainFrameToCommit: + DCHECK(blink_start_time_.is_null()); + blink_start_time_ = start_time; + break; + case StageType::kSubmitCompositorFrameToPresentationCompositorFrame: + DCHECK(viz_start_time_.is_null()); + viz_start_time_ = start_time; + break; + default: + break; + } } void CompositorFrameReporter::TerminateFrame( @@ -279,16 +299,21 @@ void CompositorFrameReporter::OnFinishImplFrame(base::TimeTicks timestamp) { } void CompositorFrameReporter::OnAbortBeginMainFrame(base::TimeTicks timestamp) { - DCHECK(!did_abort_main_frame_); - - did_abort_main_frame_ = true; + DCHECK(!main_frame_abort_time_.has_value()); + main_frame_abort_time_ = timestamp; impl_frame_finish_time_ = timestamp; // impl_frame_finish_time_ can be used for the end of BeginMain to Commit // stage } -void CompositorFrameReporter::OnDidNotProduceFrame() { - did_not_produce_frame_ = true; +void CompositorFrameReporter::OnDidNotProduceFrame( + FrameSkippedReason skip_reason) { + did_not_produce_frame_time_ = Now(); + frame_skip_reason_ = skip_reason; +} + +void CompositorFrameReporter::EnableCompositorOnlyReporting() { + EnableReportType(FrameReportType::kCompositorOnlyFrame); } void CompositorFrameReporter::SetBlinkBreakdown( @@ -316,42 +341,37 @@ void CompositorFrameReporter::SetEventsMetrics( events_metrics_ = std::move(events_metrics); } -void CompositorFrameReporter::DroppedFrame() { - report_type_ = FrameReportType::kDroppedFrame; -} - -void CompositorFrameReporter::MissedDeadlineFrame() { - report_type_ = FrameReportType::kMissedDeadlineFrame; -} - void CompositorFrameReporter::TerminateReporter() { if (frame_termination_status_ == FrameTerminationStatus::kUnknown) - TerminateFrame(FrameTerminationStatus::kUnknown, base::TimeTicks::Now()); + TerminateFrame(FrameTerminationStatus::kUnknown, Now()); + + PopulateBlinkBreakdownList(); + PopulateVizBreakdownList(); + DCHECK_EQ(current_stage_.start_time, base::TimeTicks()); - bool report_compositor_latency = false; - bool report_event_latency = false; - bool report_missed_deadline_frame = false; const char* termination_status_str = nullptr; switch (frame_termination_status_) { case FrameTerminationStatus::kPresentedFrame: - report_compositor_latency = true; - report_event_latency = true; + EnableReportType(FrameReportType::kNonDroppedFrame); termination_status_str = "presented_frame"; if (frame_deadline_ < frame_termination_time_) - report_missed_deadline_frame = true; + EnableReportType(FrameReportType::kMissedDeadlineFrame); break; case FrameTerminationStatus::kDidNotPresentFrame: - report_compositor_latency = true; - DroppedFrame(); + EnableReportType(FrameReportType::kDroppedFrame); termination_status_str = "did_not_present_frame"; break; case FrameTerminationStatus::kReplacedByNewReporter: - report_compositor_latency = true; - DroppedFrame(); + EnableReportType(FrameReportType::kDroppedFrame); termination_status_str = "replaced_by_new_reporter_at_same_stage"; break; case FrameTerminationStatus::kDidNotProduceFrame: termination_status_str = "did_not_produce_frame"; + if (!frame_skip_reason_.has_value() || + frame_skip_reason() != FrameSkippedReason::kNoDamage) { + EnableReportType(FrameReportType::kDroppedFrame); + termination_status_str = "dropped_frame"; + } break; case FrameTerminationStatus::kUnknown: termination_status_str = "terminated_before_ending"; @@ -361,46 +381,91 @@ void CompositorFrameReporter::TerminateReporter() { ReportAllTraceEvents(termination_status_str); // Only report compositor latency histograms if the frame was produced. - if (should_report_metrics_ && report_compositor_latency) { + if (should_report_metrics_ && report_types_.any()) { DCHECK(stage_history_.size()); DCHECK_EQ(SumOfStageHistory(), stage_history_.back().end_time - stage_history_.front().start_time); stage_history_.emplace_back(StageType::kTotalLatency, stage_history_.front().start_time, stage_history_.back().end_time); - ReportLatencyHistograms(report_event_latency, report_missed_deadline_frame); - } -} - -void CompositorFrameReporter::ReportLatencyHistograms( - bool report_event_latency, - bool report_delayed_latency) { - ReportCompositorLatencyHistograms(); - if (report_delayed_latency) { - // If the frames are delayed also report them under MissedDeadlineFrame. - MissedDeadlineFrame(); ReportCompositorLatencyHistograms(); + // Only report event latency histograms if the frame was presented. + if (TestReportType(FrameReportType::kNonDroppedFrame)) + ReportEventLatencyHistograms(); } +} - // Only report event latency histograms if the frame was presented. - if (report_event_latency) - ReportEventLatencyHistograms(); +void CompositorFrameReporter::EndCurrentStage(base::TimeTicks end_time) { + if (current_stage_.start_time == base::TimeTicks()) + return; + current_stage_.end_time = end_time; + stage_history_.push_back(current_stage_); + current_stage_.start_time = base::TimeTicks(); } void CompositorFrameReporter::ReportCompositorLatencyHistograms() const { - UMA_HISTOGRAM_ENUMERATION("CompositorLatency.Type", report_type_); for (const StageData& stage : stage_history_) { ReportStageHistogramWithBreakdown(stage); - - for (const auto& frame_sequence_tracker_type : *active_trackers_) { - // Report stage breakdowns. - ReportStageHistogramWithBreakdown(stage, frame_sequence_tracker_type); + for (size_t type = 0; type < active_trackers_.size(); ++type) { + if (active_trackers_.test(type)) { + // Report stage breakdowns. + ReportStageHistogramWithBreakdown( + stage, static_cast<FrameSequenceTrackerType>(type)); + } } } - if (latency_ukm_reporter_) { - latency_ukm_reporter_->ReportLatencyUkm(report_type_, stage_history_, - active_trackers_, viz_breakdown_); + for (size_t type = 0; type < report_types_.size(); ++type) { + if (!report_types_.test(type)) + continue; + FrameReportType report_type = static_cast<FrameReportType>(type); + UMA_HISTOGRAM_ENUMERATION("CompositorLatency.Type", report_type); + if (latency_ukm_reporter_) { + latency_ukm_reporter_->ReportLatencyUkm(report_type, stage_history_, + active_trackers_, viz_breakdown_); + } + for (size_t fst_type = 0; fst_type < active_trackers_.size(); ++fst_type) { + if (!active_trackers_.test(fst_type)) { + continue; + } + switch (static_cast<FrameSequenceTrackerType>(fst_type)) { + case FrameSequenceTrackerType::kCompositorAnimation: + UMA_HISTOGRAM_ENUMERATION( + "CompositorLatency.Type.CompositorAnimation", report_type); + break; + case FrameSequenceTrackerType::kMainThreadAnimation: + UMA_HISTOGRAM_ENUMERATION( + "CompositorLatency.Type.MainThreadAnimation", report_type); + break; + case FrameSequenceTrackerType::kPinchZoom: + UMA_HISTOGRAM_ENUMERATION("CompositorLatency.Type.PinchZoom", + report_type); + break; + case FrameSequenceTrackerType::kRAF: + UMA_HISTOGRAM_ENUMERATION("CompositorLatency.Type.RAF", report_type); + break; + case FrameSequenceTrackerType::kTouchScroll: + UMA_HISTOGRAM_ENUMERATION("CompositorLatency.Type.TouchScroll", + report_type); + break; + case FrameSequenceTrackerType::kVideo: + UMA_HISTOGRAM_ENUMERATION("CompositorLatency.Type.Video", + report_type); + break; + case FrameSequenceTrackerType::kWheelScroll: + UMA_HISTOGRAM_ENUMERATION("CompositorLatency.Type.WheelScroll", + report_type); + break; + case FrameSequenceTrackerType::kScrollbarScroll: + UMA_HISTOGRAM_ENUMERATION("CompositorLatency.Type.ScrollbarScroll", + report_type); + break; + case FrameSequenceTrackerType::kUniversal: + case FrameSequenceTrackerType::kCustom: + case FrameSequenceTrackerType::kMaxType: + break; + } + } } } @@ -415,123 +480,79 @@ void CompositorFrameReporter::ReportStageHistogramWithBreakdown( stage_delta); switch (stage.stage_type) { case StageType::kSendBeginMainFrameToCommit: - ReportBlinkBreakdowns(stage.start_time, frame_sequence_tracker_type); + ReportCompositorLatencyBlinkBreakdowns(frame_sequence_tracker_type); break; case StageType::kSubmitCompositorFrameToPresentationCompositorFrame: - ReportVizBreakdowns(stage.start_time, frame_sequence_tracker_type); + ReportCompositorLatencyVizBreakdowns(frame_sequence_tracker_type); break; default: break; } } -void CompositorFrameReporter::ReportBlinkBreakdowns( - base::TimeTicks start_time, +void CompositorFrameReporter::ReportCompositorLatencyBlinkBreakdowns( FrameSequenceTrackerType frame_sequence_tracker_type) const { - std::vector<std::pair<BlinkBreakdown, base::TimeDelta>> breakdowns = { - {BlinkBreakdown::kHandleInputEvents, - blink_breakdown_.handle_input_events}, - {BlinkBreakdown::kAnimate, blink_breakdown_.animate}, - {BlinkBreakdown::kStyleUpdate, blink_breakdown_.style_update}, - {BlinkBreakdown::kLayoutUpdate, blink_breakdown_.layout_update}, - {BlinkBreakdown::kPrepaint, blink_breakdown_.prepaint}, - {BlinkBreakdown::kComposite, blink_breakdown_.composite}, - {BlinkBreakdown::kPaint, blink_breakdown_.paint}, - {BlinkBreakdown::kScrollingCoordinator, - blink_breakdown_.scrolling_coordinator}, - {BlinkBreakdown::kCompositeCommit, blink_breakdown_.composite_commit}, - {BlinkBreakdown::kUpdateLayers, blink_breakdown_.update_layers}, - {BlinkBreakdown::kBeginMainSentToStarted, - begin_main_frame_start_ - start_time}}; - - for (const auto& pair : breakdowns) { - ReportCompositorLatencyHistogram( - frame_sequence_tracker_type, - kBlinkBreakdownInitialIndex + static_cast<int>(pair.first), - pair.second); + for (size_t i = 0; i < base::size(blink_breakdown_list_); i++) { + ReportCompositorLatencyHistogram(frame_sequence_tracker_type, + kBlinkBreakdownInitialIndex + i, + blink_breakdown_list_[i]); } } -void CompositorFrameReporter::ReportVizBreakdownStage( - VizBreakdown stage, - const base::TimeTicks start_time, - const base::TimeTicks end_time, +void CompositorFrameReporter::ReportCompositorLatencyVizBreakdowns( FrameSequenceTrackerType frame_sequence_tracker_type) const { - base::TimeDelta time_delta = end_time - start_time; - ReportCompositorLatencyHistogram( - frame_sequence_tracker_type, - kVizBreakdownInitialIndex + static_cast<int>(stage), time_delta); -} - -void CompositorFrameReporter::ReportVizBreakdowns( - base::TimeTicks start_time, - FrameSequenceTrackerType frame_sequence_tracker_type) const { - // Check if viz_breakdown is set. Testing indicates that sometimes the - // received_compositor_frame_timestamp can be earlier than the given - // start_time. Avoid reporting negative times. - if (viz_breakdown_.received_compositor_frame_timestamp.is_null() || - viz_breakdown_.received_compositor_frame_timestamp < start_time) { - return; + for (size_t i = 0; i < base::size(viz_breakdown_list_); i++) { + if (!viz_breakdown_list_[i]) { +#if DCHECK_IS_ON() + // Remaining breakdowns should be unset. + for (; i < base::size(viz_breakdown_list_); i++) + DCHECK(!viz_breakdown_list_[i]); +#endif + break; + } + ReportCompositorLatencyHistogram(frame_sequence_tracker_type, + kVizBreakdownInitialIndex + i, + *viz_breakdown_list_[i]); } - ReportVizBreakdownStage(VizBreakdown::kSubmitToReceiveCompositorFrame, - start_time, - viz_breakdown_.received_compositor_frame_timestamp, - frame_sequence_tracker_type); - - if (viz_breakdown_.draw_start_timestamp.is_null()) - return; - ReportVizBreakdownStage(VizBreakdown::kReceivedCompositorFrameToStartDraw, - viz_breakdown_.received_compositor_frame_timestamp, - viz_breakdown_.draw_start_timestamp, - frame_sequence_tracker_type); - - if (viz_breakdown_.swap_timings.is_null()) - return; - ReportVizBreakdownStage( - VizBreakdown::kStartDrawToSwapStart, viz_breakdown_.draw_start_timestamp, - viz_breakdown_.swap_timings.swap_start, frame_sequence_tracker_type); - - ReportVizBreakdownStage( - VizBreakdown::kSwapStartToSwapEnd, viz_breakdown_.swap_timings.swap_start, - viz_breakdown_.swap_timings.swap_end, frame_sequence_tracker_type); - - ReportVizBreakdownStage(VizBreakdown::kSwapEndToPresentationCompositorFrame, - viz_breakdown_.swap_timings.swap_end, - viz_breakdown_.presentation_feedback.timestamp, - frame_sequence_tracker_type); } void CompositorFrameReporter::ReportCompositorLatencyHistogram( FrameSequenceTrackerType frame_sequence_tracker_type, const int stage_type_index, base::TimeDelta time_delta) const { - const int report_type_index = static_cast<int>(report_type_); - const int frame_sequence_tracker_type_index = - static_cast<int>(frame_sequence_tracker_type); - const int histogram_index = - (stage_type_index * kFrameSequenceTrackerTypeCount + - frame_sequence_tracker_type_index) * - kFrameReportTypeCount + - report_type_index; - - CHECK_LT(stage_type_index, kStageTypeCount + kAllBreakdownCount); - CHECK_GE(stage_type_index, 0); - CHECK_LT(report_type_index, kFrameReportTypeCount); - CHECK_GE(report_type_index, 0); - CHECK_LT(histogram_index, kMaxCompositorLatencyHistogramIndex); - CHECK_GE(histogram_index, 0); + for (size_t type = 0; type < report_types_.size(); ++type) { + if (!report_types_.test(type)) + continue; + FrameReportType report_type = static_cast<FrameReportType>(type); + const int report_type_index = static_cast<int>(report_type); + const int frame_sequence_tracker_type_index = + static_cast<int>(frame_sequence_tracker_type); + const int histogram_index = + (stage_type_index * kFrameSequenceTrackerTypeCount + + frame_sequence_tracker_type_index) * + kFrameReportTypeCount + + report_type_index; + + CHECK_LT(stage_type_index, kStageTypeCount + kAllBreakdownCount); + CHECK_GE(stage_type_index, 0); + CHECK_LT(report_type_index, kFrameReportTypeCount); + CHECK_GE(report_type_index, 0); + CHECK_LT(histogram_index, kMaxCompositorLatencyHistogramIndex); + CHECK_GE(histogram_index, 0); - STATIC_HISTOGRAM_POINTER_GROUP( - GetCompositorLatencyHistogramName( - report_type_index, frame_sequence_tracker_type, stage_type_index), - histogram_index, kMaxCompositorLatencyHistogramIndex, - AddTimeMicrosecondsGranularity(time_delta), - base::Histogram::FactoryGet( - GetCompositorLatencyHistogramName( - report_type_index, frame_sequence_tracker_type, stage_type_index), - kCompositorLatencyHistogramMin, kCompositorLatencyHistogramMax, - kCompositorLatencyHistogramBucketCount, - base::HistogramBase::kUmaTargetedHistogramFlag)); + STATIC_HISTOGRAM_POINTER_GROUP( + GetCompositorLatencyHistogramName( + report_type_index, frame_sequence_tracker_type, stage_type_index), + histogram_index, kMaxCompositorLatencyHistogramIndex, + AddTimeMicrosecondsGranularity(time_delta), + base::Histogram::FactoryGet( + GetCompositorLatencyHistogramName(report_type_index, + frame_sequence_tracker_type, + stage_type_index), + kCompositorLatencyHistogramMin, kCompositorLatencyHistogramMax, + kCompositorLatencyHistogramBucketCount, + base::HistogramBase::kUmaTargetedHistogramFlag)); + } } void CompositorFrameReporter::ReportEventLatencyHistograms() const { @@ -543,7 +564,7 @@ void CompositorFrameReporter::ReportEventLatencyHistograms() const { event_metrics.scroll_input_type() ? static_cast<int>(*event_metrics.scroll_input_type()) : 0; - const int histogram_index = + const int histogram_base_index = event_type_index * kEventLatencyScrollTypeCount + scroll_type_index; // For scroll events, report total latency up to gpu-swap-end. This is @@ -556,8 +577,8 @@ void CompositorFrameReporter::ReportEventLatencyHistograms() const { const std::string swap_end_histogram_name = histogram_base_name + ".TotalLatencyToSwapEnd"; STATIC_HISTOGRAM_POINTER_GROUP( - swap_end_histogram_name, histogram_index, - kMaxEventLatencyHistogramIndex, + swap_end_histogram_name, histogram_base_index, + kMaxEventLatencyHistogramBaseIndex, AddTimeMicrosecondsGranularity(swap_end_latency), base::Histogram::FactoryGet( swap_end_histogram_name, kEventLatencyHistogramMin, @@ -565,59 +586,130 @@ void CompositorFrameReporter::ReportEventLatencyHistograms() const { base::HistogramBase::kUmaTargetedHistogramFlag)); } - base::TimeDelta total_latency = - frame_termination_time_ - event_metrics.time_stamp(); - const std::string histogram_name = histogram_base_name + ".TotalLatency"; - STATIC_HISTOGRAM_POINTER_GROUP( - histogram_name, histogram_index, kMaxEventLatencyHistogramIndex, - AddTimeMicrosecondsGranularity(total_latency), - base::Histogram::FactoryGet( - histogram_name, kEventLatencyHistogramMin, - kEventLatencyHistogramMax, kEventLatencyHistogramBucketCount, - base::HistogramBase::kUmaTargetedHistogramFlag)); - const auto trace_id = TRACE_ID_LOCAL(&event_metrics); TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP1( "cc,input", "EventLatency", trace_id, event_metrics.time_stamp(), "event", event_metrics.GetTypeName()); - // Report the breakdowns. // It is possible for an event to arrive in the compositor in the middle of // a frame (e.g. the browser received the event *after* renderer received a // begin-impl, and the event reached the compositor before that frame // ended). To handle such cases, find the first stage that happens after the // event's arrival in the browser. - // TODO(mohsen): Report the breakdowns in UMA too. size_t index = 0; for (; index < stage_history_.size(); ++index) { const auto& stage = stage_history_[index]; if (stage.start_time > event_metrics.time_stamp()) { + const char stage_type_name[] = "BrowserToRendererCompositor"; + + const base::TimeDelta latency = + stage.start_time - event_metrics.time_stamp(); + const std::string histogram_name = + base::StrCat({histogram_base_name, ".", stage_type_name}); + STATIC_HISTOGRAM_POINTER_GROUP( + histogram_name, histogram_base_index, + kMaxEventLatencyHistogramBaseIndex, + AddTimeMicrosecondsGranularity(latency), + base::Histogram::FactoryGet( + histogram_name, kEventLatencyHistogramMin, + kEventLatencyHistogramMax, kEventLatencyHistogramBucketCount, + base::HistogramBase::kUmaTargetedHistogramFlag)); + TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0( - "cc,input", "BrowserToRendererCompositor", trace_id, - event_metrics.time_stamp()); + "cc,input", stage_type_name, trace_id, event_metrics.time_stamp()); TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP0( - "cc,input", "BrowserToRendererCompositor", trace_id, - stage.start_time); + "cc,input", stage_type_name, trace_id, stage.start_time); break; } } for (; index < stage_history_.size(); ++index) { const auto& stage = stage_history_[index]; - if (stage.stage_type == StageType::kTotalLatency) - break; - TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0( - "cc,input", GetStageName(static_cast<int>(stage.stage_type)), - trace_id, stage.start_time); - TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP0( - "cc,input", GetStageName(static_cast<int>(stage.stage_type)), - trace_id, stage.end_time); + + // Total latency is calculated since the event timestamp. + const base::TimeTicks start_time = + stage.stage_type == StageType::kTotalLatency + ? event_metrics.time_stamp() + : stage.start_time; + const base::TimeDelta latency = stage.end_time - start_time; + const int stage_type_index = static_cast<int>(stage.stage_type); + ReportEventLatencyHistogram(histogram_base_index, histogram_base_name, + stage_type_index, latency); + + switch (stage.stage_type) { + case StageType::kSendBeginMainFrameToCommit: + ReportEventLatencyBlinkBreakdowns(histogram_base_index, + histogram_base_name); + break; + case StageType::kSubmitCompositorFrameToPresentationCompositorFrame: + ReportEventLatencyVizBreakdowns(histogram_base_index, + histogram_base_name); + break; + default: + break; + } + + if (stage.stage_type != StageType::kTotalLatency) { + TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0( + "cc,input", GetStageName(stage_type_index), trace_id, + stage.start_time); + TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP0( + "cc,input", GetStageName(stage_type_index), trace_id, + stage.end_time); + } } TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP0( "cc,input", "EventLatency", trace_id, frame_termination_time_); } } +void CompositorFrameReporter::ReportEventLatencyBlinkBreakdowns( + int histogram_base_index, + const std::string& histogram_base_name) const { + for (size_t i = 0; i < base::size(blink_breakdown_list_); i++) { + ReportEventLatencyHistogram(histogram_base_index, histogram_base_name, + kBlinkBreakdownInitialIndex + i, + blink_breakdown_list_[i]); + } +} + +void CompositorFrameReporter::ReportEventLatencyVizBreakdowns( + int histogram_base_index, + const std::string& histogram_base_name) const { + for (size_t i = 0; i < base::size(viz_breakdown_list_); i++) { + if (!viz_breakdown_list_[i]) { +#if DCHECK_IS_ON() + // Remaining breakdowns should be unset. + for (; i < base::size(viz_breakdown_list_); i++) + DCHECK(!viz_breakdown_list_[i]); +#endif + break; + } + ReportEventLatencyHistogram(histogram_base_index, histogram_base_name, + kVizBreakdownInitialIndex + i, + *viz_breakdown_list_[i]); + } +} + +void CompositorFrameReporter::ReportEventLatencyHistogram( + int histogram_base_index, + const std::string& histogram_base_name, + int stage_type_index, + base::TimeDelta latency) const { + const std::string histogram_name = + base::StrCat({histogram_base_name, ".", GetStageName(stage_type_index)}); + const int histogram_index = + histogram_base_index * (kStageTypeCount + kAllBreakdownCount) + + stage_type_index; + STATIC_HISTOGRAM_POINTER_GROUP( + histogram_name, histogram_index, kMaxEventLatencyHistogramIndex, + AddTimeMicrosecondsGranularity(latency), + base::Histogram::FactoryGet( + histogram_name, kEventLatencyHistogramMin, kEventLatencyHistogramMax, + kEventLatencyHistogramBucketCount, + base::HistogramBase::kUmaTargetedHistogramFlag)); +} + void CompositorFrameReporter::ReportVizBreakdownTrace( VizBreakdown substage, const base::TimeTicks start_time, @@ -653,6 +745,11 @@ void CompositorFrameReporter::ReportAllTraceEvents( const int stage_type_index = static_cast<int>(stage.stage_type); CHECK_LT(stage_type_index, static_cast<int>(StageType::kStageTypeCount)); CHECK_GE(stage_type_index, 0); + if (stage.start_time >= frame_termination_time_) + break; + DCHECK_GE(stage.end_time, stage.start_time); + if (stage.start_time == stage.end_time) + continue; const char* name = GetStageName(stage_type_index); TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0( "cc,benchmark", name, trace_id, stage.start_time); @@ -691,13 +788,80 @@ void CompositorFrameReporter::ReportAllTraceEvents( } const char* submission_status_str = - report_type_ == FrameReportType::kDroppedFrame ? "dropped_frame" + TestReportType(FrameReportType::kDroppedFrame) ? "dropped_frame" : "non_dropped_frame"; TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP2( - "cc,benchmark", "PipelineReporter", trace_id, - stage_history_.back().end_time, "termination_status", - termination_status_str, "compositor_frame_submission_status", - submission_status_str); + "cc,benchmark", "PipelineReporter", trace_id, frame_termination_time_, + "termination_status", termination_status_str, + "compositor_frame_submission_status", submission_status_str); +} + +void CompositorFrameReporter::PopulateBlinkBreakdownList() { + if (blink_start_time_.is_null()) + return; + + blink_breakdown_list_[static_cast<int>(BlinkBreakdown::kHandleInputEvents)] = + blink_breakdown_.handle_input_events; + blink_breakdown_list_[static_cast<int>(BlinkBreakdown::kAnimate)] = + blink_breakdown_.animate; + blink_breakdown_list_[static_cast<int>(BlinkBreakdown::kStyleUpdate)] = + blink_breakdown_.style_update; + blink_breakdown_list_[static_cast<int>(BlinkBreakdown::kLayoutUpdate)] = + blink_breakdown_.layout_update; + blink_breakdown_list_[static_cast<int>(BlinkBreakdown::kPrepaint)] = + blink_breakdown_.prepaint; + blink_breakdown_list_[static_cast<int>(BlinkBreakdown::kComposite)] = + blink_breakdown_.composite; + blink_breakdown_list_[static_cast<int>(BlinkBreakdown::kPaint)] = + blink_breakdown_.paint; + blink_breakdown_list_[static_cast<int>( + BlinkBreakdown::kScrollingCoordinator)] = + blink_breakdown_.scrolling_coordinator; + blink_breakdown_list_[static_cast<int>(BlinkBreakdown::kCompositeCommit)] = + blink_breakdown_.composite_commit; + blink_breakdown_list_[static_cast<int>(BlinkBreakdown::kUpdateLayers)] = + blink_breakdown_.update_layers; + blink_breakdown_list_[static_cast<int>( + BlinkBreakdown::kBeginMainSentToStarted)] = + begin_main_frame_start_ - blink_start_time_; +} + +void CompositorFrameReporter::PopulateVizBreakdownList() { + if (viz_start_time_.is_null()) + return; + + // Check if viz_breakdown is set. Testing indicates that sometimes the + // received_compositor_frame_timestamp can be earlier than the given + // |start_time|. Avoid reporting negative times. + if (viz_breakdown_.received_compositor_frame_timestamp.is_null() || + viz_breakdown_.received_compositor_frame_timestamp < viz_start_time_) { + return; + } + viz_breakdown_list_[static_cast<int>( + VizBreakdown::kSubmitToReceiveCompositorFrame)] = + viz_breakdown_.received_compositor_frame_timestamp - viz_start_time_; + + if (viz_breakdown_.draw_start_timestamp.is_null()) + return; + viz_breakdown_list_[static_cast<int>( + VizBreakdown::kReceivedCompositorFrameToStartDraw)] = + viz_breakdown_.draw_start_timestamp - + viz_breakdown_.received_compositor_frame_timestamp; + + if (viz_breakdown_.swap_timings.is_null()) + return; + viz_breakdown_list_[static_cast<int>(VizBreakdown::kStartDrawToSwapStart)] = + viz_breakdown_.swap_timings.swap_start - + viz_breakdown_.draw_start_timestamp; + + viz_breakdown_list_[static_cast<int>(VizBreakdown::kSwapStartToSwapEnd)] = + viz_breakdown_.swap_timings.swap_end - + viz_breakdown_.swap_timings.swap_start; + + viz_breakdown_list_[static_cast<int>( + VizBreakdown::kSwapEndToPresentationCompositorFrame)] = + viz_breakdown_.presentation_feedback.timestamp - + viz_breakdown_.swap_timings.swap_end; } base::TimeDelta CompositorFrameReporter::SumOfStageHistory() const { @@ -707,4 +871,8 @@ base::TimeDelta CompositorFrameReporter::SumOfStageHistory() const { return sum; } +base::TimeTicks CompositorFrameReporter::Now() const { + return tick_clock_->NowTicks(); +} + } // namespace cc diff --git a/chromium/cc/metrics/compositor_frame_reporter.h b/chromium/cc/metrics/compositor_frame_reporter.h index ebeb2cdee2f..9cae550ba76 100644 --- a/chromium/cc/metrics/compositor_frame_reporter.h +++ b/chromium/cc/metrics/compositor_frame_reporter.h @@ -5,16 +5,19 @@ #ifndef CC_METRICS_COMPOSITOR_FRAME_REPORTER_H_ #define CC_METRICS_COMPOSITOR_FRAME_REPORTER_H_ +#include <bitset> #include <memory> #include <vector> -#include "base/containers/flat_set.h" +#include "base/optional.h" +#include "base/time/default_tick_clock.h" #include "base/time/time.h" #include "cc/base/base_export.h" #include "cc/cc_export.h" #include "cc/metrics/begin_main_frame_metrics.h" #include "cc/metrics/event_metrics.h" -#include "cc/metrics/frame_sequence_tracker.h" +#include "cc/metrics/frame_sequence_metrics.h" +#include "cc/scheduler/scheduler.h" #include "components/viz/common/frame_sinks/begin_frame_args.h" #include "components/viz/common/frame_timing_details.h" @@ -67,7 +70,8 @@ class CC_EXPORT CompositorFrameReporter { kNonDroppedFrame = 0, kMissedDeadlineFrame = 1, kDroppedFrame = 2, - kMaxValue = kDroppedFrame + kCompositorOnlyFrame = 3, + kMaxValue = kCompositorOnlyFrame }; // These values are used for indexing the UMA histograms. @@ -119,12 +123,14 @@ class CC_EXPORT CompositorFrameReporter { ~StageData(); }; - CompositorFrameReporter( - const base::flat_set<FrameSequenceTrackerType>* active_trackers, - const viz::BeginFrameId& id, - const base::TimeTicks frame_deadline, - LatencyUkmReporter* latency_ukm_reporter, - bool should_report_metrics); + using ActiveTrackers = + std::bitset<static_cast<size_t>(FrameSequenceTrackerType::kMaxType)>; + + CompositorFrameReporter(const ActiveTrackers& active_trackers, + const viz::BeginFrameId& id, + const base::TimeTicks frame_deadline, + LatencyUkmReporter* latency_ukm_reporter, + bool should_report_metrics); ~CompositorFrameReporter(); CompositorFrameReporter(const CompositorFrameReporter& reporter) = delete; @@ -149,46 +155,63 @@ class CC_EXPORT CompositorFrameReporter { void OnFinishImplFrame(base::TimeTicks timestamp); void OnAbortBeginMainFrame(base::TimeTicks timestamp); - void OnDidNotProduceFrame(); + void OnDidNotProduceFrame(FrameSkippedReason skip_reason); + void EnableCompositorOnlyReporting(); bool did_finish_impl_frame() const { return did_finish_impl_frame_; } - bool did_abort_main_frame() const { return did_abort_main_frame_; } - bool did_not_produce_frame() const { return did_not_produce_frame_; } base::TimeTicks impl_frame_finish_time() const { return impl_frame_finish_time_; } - private: - void DroppedFrame(); - void MissedDeadlineFrame(); + bool did_not_produce_frame() const { + return did_not_produce_frame_time_.has_value(); + } + base::TimeTicks did_not_produce_frame_time() const { + return *did_not_produce_frame_time_; + } + + bool did_abort_main_frame() const { + return main_frame_abort_time_.has_value(); + } + base::TimeTicks main_frame_abort_time() const { + return *main_frame_abort_time_; + } + + FrameSkippedReason frame_skip_reason() const { return *frame_skip_reason_; } + void set_tick_clock(const base::TickClock* tick_clock) { + DCHECK(tick_clock); + tick_clock_ = tick_clock; + } + + private: void TerminateReporter(); void EndCurrentStage(base::TimeTicks end_time); + void ReportCompositorLatencyHistograms() const; - void ReportLatencyHistograms(bool report_event_latency = false, - bool report_delayed_latency = false); void ReportStageHistogramWithBreakdown( const StageData& stage, FrameSequenceTrackerType frame_sequence_tracker_type = FrameSequenceTrackerType::kMaxType) const; - void ReportBlinkBreakdowns( - const base::TimeTicks start_time, + void ReportCompositorLatencyBlinkBreakdowns( FrameSequenceTrackerType frame_sequence_tracker_type) const; - - // Report histogram and trace event stage for one Viz breakdown - void ReportVizBreakdownStage( - VizBreakdown stage, - const base::TimeTicks start_time, - const base::TimeTicks end_time, - FrameSequenceTrackerType frame_sequence_tracker_type) const; - - void ReportVizBreakdowns( - const base::TimeTicks start_time, + void ReportCompositorLatencyVizBreakdowns( FrameSequenceTrackerType frame_sequence_tracker_type) const; void ReportCompositorLatencyHistogram( FrameSequenceTrackerType intraction_type, const int stage_type_index, base::TimeDelta time_delta) const; + void ReportEventLatencyHistograms() const; + void ReportEventLatencyBlinkBreakdowns( + int histogram_base_index, + const std::string& histogram_base_name) const; + void ReportEventLatencyVizBreakdowns( + int histogram_base_index, + const std::string& histogram_base_name) const; + void ReportEventLatencyHistogram(int histogram_base_index, + const std::string& histogram_base_name, + int stage_type_index, + base::TimeDelta latency) const; // Generate a trace event corresponding to a Viz breakdown under // SubmitCompositorFrameToPresentationCompositorFrame stage in @@ -200,43 +223,69 @@ class CC_EXPORT CompositorFrameReporter { void ReportAllTraceEvents(const char* termination_status_str) const; + void EnableReportType(FrameReportType report_type) { + report_types_.set(static_cast<size_t>(report_type)); + } + bool TestReportType(FrameReportType report_type) const { + return report_types_.test(static_cast<size_t>(report_type)); + } + + void PopulateBlinkBreakdownList(); + void PopulateVizBreakdownList(); + + // This method is only used for DCheck + base::TimeDelta SumOfStageHistory() const; + + base::TimeTicks Now() const; + const bool should_report_metrics_; StageData current_stage_; + BeginMainFrameMetrics blink_breakdown_; + base::TimeTicks blink_start_time_; + base::TimeDelta + blink_breakdown_list_[static_cast<int>(BlinkBreakdown::kBreakdownCount)]; + viz::FrameTimingDetails viz_breakdown_; + base::TimeTicks viz_start_time_; + base::Optional<base::TimeDelta> + viz_breakdown_list_[static_cast<int>(VizBreakdown::kBreakdownCount)]; // Stage data is recorded here. On destruction these stages will be reported // to UMA if the termination status is |kPresentedFrame|. Reported data will // be divided based on the frame submission status. std::vector<StageData> stage_history_; - // This method is only used for DCheck - base::TimeDelta SumOfStageHistory() const; - // List of metrics for events affecting this frame. std::vector<EventMetrics> events_metrics_; - FrameReportType report_type_ = FrameReportType::kNonDroppedFrame; + std::bitset<static_cast<size_t>(FrameReportType::kMaxValue) + 1> + report_types_; + base::TimeTicks frame_termination_time_; base::TimeTicks begin_main_frame_start_; FrameTerminationStatus frame_termination_status_ = FrameTerminationStatus::kUnknown; - const base::flat_set<FrameSequenceTrackerType>* active_trackers_; + const ActiveTrackers active_trackers_; LatencyUkmReporter* latency_ukm_reporter_; // Indicates if work on Impl frame is finished. bool did_finish_impl_frame_ = false; - // Indicates if main frame is aborted after begin. - bool did_abort_main_frame_ = false; - // Flag indicating if DidNotProduceFrame is called for this reporter - bool did_not_produce_frame_ = false; // The time that work on Impl frame is finished. It's only valid if the // reporter is in a stage other than begin impl frame. base::TimeTicks impl_frame_finish_time_; base::TimeTicks frame_deadline_; + + // The timestamp of when the frame was marked as not having produced a frame + // (through a call to DidNotProduceFrame()). + base::Optional<base::TimeTicks> did_not_produce_frame_time_; + base::Optional<FrameSkippedReason> frame_skip_reason_; + base::Optional<base::TimeTicks> main_frame_abort_time_; + + const base::TickClock* tick_clock_ = base::DefaultTickClock::GetInstance(); }; } // namespace cc diff --git a/chromium/cc/metrics/compositor_frame_reporter_unittest.cc b/chromium/cc/metrics/compositor_frame_reporter_unittest.cc index 1f4ffee37e7..afd8830e3e3 100644 --- a/chromium/cc/metrics/compositor_frame_reporter_unittest.cc +++ b/chromium/cc/metrics/compositor_frame_reporter_unittest.cc @@ -10,60 +10,76 @@ #include "base/strings/strcat.h" #include "base/test/metrics/histogram_tester.h" -#include "cc/input/scroll_input_type.h" +#include "base/test/simple_test_tick_clock.h" #include "cc/metrics/compositor_frame_reporting_controller.h" #include "cc/metrics/event_metrics.h" #include "components/viz/common/frame_timing_details.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/events/types/scroll_input_type.h" namespace cc { namespace { -MATCHER(IsWhitelisted, - base::StrCat({negation ? "isn't" : "is", " whitelisted"})) { - return arg.IsWhitelisted(); -} +using ::testing::Each; +using ::testing::IsEmpty; +using ::testing::NotNull; class CompositorFrameReporterTest : public testing::Test { public: - const base::flat_set<FrameSequenceTrackerType> active_trackers = {}; CompositorFrameReporterTest() : pipeline_reporter_(std::make_unique<CompositorFrameReporter>( - &active_trackers, + CompositorFrameReporter::ActiveTrackers(), viz::BeginFrameId(), base::TimeTicks() + base::TimeDelta::FromMilliseconds(16), nullptr, /*should_report_metrics=*/true)) { + pipeline_reporter_->set_tick_clock(&test_tick_clock_); AdvanceNowByMs(1); } protected: - void AdvanceNowByMs(int advance_ms) { - now_ += base::TimeDelta::FromMicroseconds(advance_ms); + base::TimeTicks AdvanceNowByMs(int advance_ms) { + test_tick_clock_.Advance(base::TimeDelta::FromMicroseconds(advance_ms)); + return test_tick_clock_.NowTicks(); } - base::TimeTicks Now() { return now_; } + base::TimeTicks Now() { return test_tick_clock_.NowTicks(); } + + std::unique_ptr<BeginMainFrameMetrics> BuildBlinkBreakdown() { + auto breakdown = std::make_unique<BeginMainFrameMetrics>(); + breakdown->handle_input_events = base::TimeDelta::FromMicroseconds(10); + breakdown->animate = base::TimeDelta::FromMicroseconds(9); + breakdown->style_update = base::TimeDelta::FromMicroseconds(8); + breakdown->layout_update = base::TimeDelta::FromMicroseconds(7); + breakdown->prepaint = base::TimeDelta::FromMicroseconds(6); + breakdown->composite = base::TimeDelta::FromMicroseconds(5); + breakdown->paint = base::TimeDelta::FromMicroseconds(4); + breakdown->scrolling_coordinator = base::TimeDelta::FromMicroseconds(3); + breakdown->composite_commit = base::TimeDelta::FromMicroseconds(2); + breakdown->update_layers = base::TimeDelta::FromMicroseconds(1); + + // Advance now by the sum of the breakdowns. + AdvanceNowByMs(10 + 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1); + + return breakdown; + } - viz::FrameTimingDetails BuildFrameTimingDetails() { - viz::FrameTimingDetails frame_timing_details; - AdvanceNowByMs(1); - frame_timing_details.received_compositor_frame_timestamp = Now(); - AdvanceNowByMs(1); - frame_timing_details.draw_start_timestamp = Now(); - AdvanceNowByMs(1); - frame_timing_details.swap_timings.swap_start = Now(); - AdvanceNowByMs(1); - frame_timing_details.swap_timings.swap_end = Now(); - AdvanceNowByMs(1); - frame_timing_details.presentation_feedback.timestamp = Now(); - return frame_timing_details; + 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); + return viz_breakdown; } - std::unique_ptr<CompositorFrameReporter> pipeline_reporter_; + // This should be defined before |pipeline_reporter_| so it is created before + // and destroyed after that. + base::SimpleTestTickClock test_tick_clock_; - private: - base::TimeTicks now_; + std::unique_ptr<CompositorFrameReporter> pipeline_reporter_; }; TEST_F(CompositorFrameReporterTest, MainFrameAbortedReportingTest) { @@ -209,18 +225,21 @@ TEST_F(CompositorFrameReporterTest, SubmittedDroppedFrameReportingTest) { "CompositorLatency.DroppedFrame.TotalLatency", 5, 1); } -// Tests that when a frame is presented to the user, event latency metrics are -// reported properly. -TEST_F(CompositorFrameReporterTest, EventLatencyForPresentedFrameReported) { +// Tests that when a frame is presented to the user, total event latency metrics +// are reported properly. +TEST_F(CompositorFrameReporterTest, + EventLatencyTotalForPresentedFrameReported) { base::HistogramTester histogram_tester; const base::TimeTicks event_time = Now(); - std::vector<EventMetrics> events_metrics = { - {ui::ET_TOUCH_PRESSED, event_time, base::nullopt}, - {ui::ET_TOUCH_MOVED, event_time, base::nullopt}, - {ui::ET_TOUCH_MOVED, event_time, base::nullopt}, + std::unique_ptr<EventMetrics> event_metrics_ptrs[] = { + EventMetrics::Create(ui::ET_TOUCH_PRESSED, event_time, base::nullopt), + EventMetrics::Create(ui::ET_TOUCH_MOVED, event_time, base::nullopt), + EventMetrics::Create(ui::ET_TOUCH_MOVED, event_time, base::nullopt), }; - EXPECT_THAT(events_metrics, ::testing::Each(IsWhitelisted())); + EXPECT_THAT(event_metrics_ptrs, Each(NotNull())); + std::vector<EventMetrics> events_metrics = { + *event_metrics_ptrs[0], *event_metrics_ptrs[1], *event_metrics_ptrs[2]}; AdvanceNowByMs(3); pipeline_reporter_->StartStage( @@ -257,19 +276,166 @@ TEST_F(CompositorFrameReporterTest, EventLatencyForPresentedFrameReported) { latency_ms, 2); } -// Tests that when a frame is presented to the user, scroll event latency +// Tests that when a frame is presented to the user, event latency breakdown // metrics are reported properly. TEST_F(CompositorFrameReporterTest, - EventLatencyScrollForPresentedFrameReported) { + EventLatencyBreakdownsForPresentedFrameReported) { base::HistogramTester histogram_tester; const base::TimeTicks event_time = Now(); - std::vector<EventMetrics> events_metrics = { - {ui::ET_GESTURE_SCROLL_BEGIN, event_time, ScrollInputType::kWheel}, - {ui::ET_GESTURE_SCROLL_UPDATE, event_time, ScrollInputType::kWheel}, - {ui::ET_GESTURE_SCROLL_UPDATE, event_time, ScrollInputType::kWheel}, + std::unique_ptr<EventMetrics> event_metrics_ptrs[] = { + EventMetrics::Create(ui::ET_TOUCH_PRESSED, event_time, base::nullopt), }; - EXPECT_THAT(events_metrics, ::testing::Each(IsWhitelisted())); + EXPECT_THAT(event_metrics_ptrs, Each(NotNull())); + std::vector<EventMetrics> events_metrics = {*event_metrics_ptrs[0]}; + + auto begin_impl_time = AdvanceNowByMs(2); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame, + begin_impl_time); + + auto begin_main_time = AdvanceNowByMs(3); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kSendBeginMainFrameToCommit, + begin_main_time); + + auto begin_main_start_time = AdvanceNowByMs(4); + std::unique_ptr<BeginMainFrameMetrics> blink_breakdown = + BuildBlinkBreakdown(); + // Make a copy of the breakdown to use in verifying expectations in the end. + BeginMainFrameMetrics blink_breakdown_copy = *blink_breakdown; + pipeline_reporter_->SetBlinkBreakdown(std::move(blink_breakdown), + begin_main_start_time); + auto begin_commit_time = AdvanceNowByMs(5); + pipeline_reporter_->StartStage(CompositorFrameReporter::StageType::kCommit, + begin_commit_time); + + auto end_commit_time = AdvanceNowByMs(6); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kEndCommitToActivation, + end_commit_time); + + auto begin_activation_time = AdvanceNowByMs(7); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kActivation, begin_activation_time); + + auto end_activation_time = AdvanceNowByMs(8); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame, + end_activation_time); + + auto submit_time = AdvanceNowByMs(9); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType:: + kSubmitCompositorFrameToPresentationCompositorFrame, + submit_time); + pipeline_reporter_->SetEventsMetrics(std::move(events_metrics)); + + AdvanceNowByMs(10); + viz::FrameTimingDetails viz_breakdown = BuildVizBreakdown(); + pipeline_reporter_->SetVizBreakdown(viz_breakdown); + pipeline_reporter_->TerminateFrame( + CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame, + viz_breakdown.presentation_feedback.timestamp); + + pipeline_reporter_ = nullptr; + + struct { + const char* name; + const base::TimeDelta latency; + } expected_latencies[] = { + {"EventLatency.TouchPressed.BrowserToRendererCompositor", + begin_impl_time - event_time}, + {"EventLatency.TouchPressed.BeginImplFrameToSendBeginMainFrame", + begin_main_time - begin_impl_time}, + {"EventLatency.TouchPressed.SendBeginMainFrameToCommit", + begin_commit_time - begin_main_time}, + {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.HandleInputEvents", + blink_breakdown_copy.handle_input_events}, + {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.Animate", + blink_breakdown_copy.animate}, + {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.StyleUpdate", + blink_breakdown_copy.style_update}, + {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.LayoutUpdate", + blink_breakdown_copy.layout_update}, + {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.Prepaint", + blink_breakdown_copy.prepaint}, + {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.Composite", + blink_breakdown_copy.composite}, + {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.Paint", + blink_breakdown_copy.paint}, + {"EventLatency.TouchPressed.SendBeginMainFrameToCommit." + "ScrollingCoordinator", + blink_breakdown_copy.scrolling_coordinator}, + {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.CompositeCommit", + blink_breakdown_copy.composite_commit}, + {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.UpdateLayers", + blink_breakdown_copy.update_layers}, + {"EventLatency.TouchPressed.SendBeginMainFrameToCommit." + "BeginMainSentToStarted", + begin_main_start_time - begin_main_time}, + {"EventLatency.TouchPressed.Commit", end_commit_time - begin_commit_time}, + {"EventLatency.TouchPressed.EndCommitToActivation", + begin_activation_time - end_commit_time}, + {"EventLatency.TouchPressed.Activation", + end_activation_time - begin_activation_time}, + {"EventLatency.TouchPressed.EndActivateToSubmitCompositorFrame", + submit_time - end_activation_time}, + {"EventLatency.TouchPressed." + "SubmitCompositorFrameToPresentationCompositorFrame", + viz_breakdown.presentation_feedback.timestamp - submit_time}, + {"EventLatency.TouchPressed." + "SubmitCompositorFrameToPresentationCompositorFrame." + "SubmitToReceiveCompositorFrame", + viz_breakdown.received_compositor_frame_timestamp - submit_time}, + {"EventLatency.TouchPressed." + "SubmitCompositorFrameToPresentationCompositorFrame." + "ReceivedCompositorFrameToStartDraw", + viz_breakdown.draw_start_timestamp - + viz_breakdown.received_compositor_frame_timestamp}, + {"EventLatency.TouchPressed." + "SubmitCompositorFrameToPresentationCompositorFrame." + "StartDrawToSwapStart", + viz_breakdown.swap_timings.swap_start - + viz_breakdown.draw_start_timestamp}, + {"EventLatency.TouchPressed." + "SubmitCompositorFrameToPresentationCompositorFrame.SwapStartToSwapEnd", + viz_breakdown.swap_timings.swap_end - + viz_breakdown.swap_timings.swap_start}, + {"EventLatency.TouchPressed." + "SubmitCompositorFrameToPresentationCompositorFrame." + "SwapEndToPresentationCompositorFrame", + viz_breakdown.presentation_feedback.timestamp - + viz_breakdown.swap_timings.swap_end}, + {"EventLatency.TouchPressed.TotalLatency", + viz_breakdown.presentation_feedback.timestamp - event_time}, + }; + + for (const auto& expected_latency : expected_latencies) { + histogram_tester.ExpectTotalCount(expected_latency.name, 1); + histogram_tester.ExpectBucketCount( + expected_latency.name, expected_latency.latency.InMicroseconds(), 1); + } +} + +// Tests that when a frame is presented to the user, total scroll event latency +// metrics are reported properly. +TEST_F(CompositorFrameReporterTest, + EventLatencyScrollTotalForPresentedFrameReported) { + base::HistogramTester histogram_tester; + + const base::TimeTicks event_time = Now(); + std::unique_ptr<EventMetrics> event_metrics_ptrs[] = { + EventMetrics::Create(ui::ET_GESTURE_SCROLL_BEGIN, event_time, + ui::ScrollInputType::kWheel), + EventMetrics::Create(ui::ET_GESTURE_SCROLL_UPDATE, event_time, + ui::ScrollInputType::kWheel), + EventMetrics::Create(ui::ET_GESTURE_SCROLL_UPDATE, event_time, + ui::ScrollInputType::kWheel), + }; + EXPECT_THAT(event_metrics_ptrs, Each(NotNull())); + std::vector<EventMetrics> events_metrics = { + *event_metrics_ptrs[0], *event_metrics_ptrs[1], *event_metrics_ptrs[2]}; AdvanceNowByMs(3); pipeline_reporter_->StartStage( @@ -289,40 +455,39 @@ TEST_F(CompositorFrameReporterTest, pipeline_reporter_->SetEventsMetrics(std::move(events_metrics)); AdvanceNowByMs(3); - viz::FrameTimingDetails frame_timing_details = BuildFrameTimingDetails(); - pipeline_reporter_->SetVizBreakdown(frame_timing_details); + viz::FrameTimingDetails viz_breakdown = BuildVizBreakdown(); + pipeline_reporter_->SetVizBreakdown(viz_breakdown); pipeline_reporter_->TerminateFrame( CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame, - frame_timing_details.presentation_feedback.timestamp); + viz_breakdown.presentation_feedback.timestamp); pipeline_reporter_ = nullptr; const int total_latency_ms = - (frame_timing_details.presentation_feedback.timestamp - event_time) + (viz_breakdown.presentation_feedback.timestamp - event_time) .InMicroseconds(); const int swap_end_latency_ms = - (frame_timing_details.swap_timings.swap_end - event_time) - .InMicroseconds(); - histogram_tester.ExpectTotalCount( - "EventLatency.GestureScrollBegin.Wheel.TotalLatency", 1); - histogram_tester.ExpectTotalCount( - "EventLatency.GestureScrollBegin.Wheel.TotalLatencyToSwapEnd", 1); - histogram_tester.ExpectTotalCount( - "EventLatency.GestureScrollUpdate.Wheel.TotalLatency", 2); - histogram_tester.ExpectTotalCount( - "EventLatency.GestureScrollUpdate.Wheel.TotalLatencyToSwapEnd", 2); - histogram_tester.ExpectBucketCount( - "EventLatency.GestureScrollBegin.Wheel.TotalLatency", total_latency_ms, - 1); - histogram_tester.ExpectBucketCount( - "EventLatency.GestureScrollBegin.Wheel.TotalLatencyToSwapEnd", - swap_end_latency_ms, 1); - histogram_tester.ExpectBucketCount( - "EventLatency.GestureScrollUpdate.Wheel.TotalLatency", total_latency_ms, - 2); - histogram_tester.ExpectBucketCount( - "EventLatency.GestureScrollUpdate.Wheel.TotalLatencyToSwapEnd", - swap_end_latency_ms, 2); + (viz_breakdown.swap_timings.swap_end - event_time).InMicroseconds(); + struct { + const char* name; + const int64_t latency_ms; + const int count; + } expected_counts[] = { + {"EventLatency.GestureScrollBegin.Wheel.TotalLatency", total_latency_ms, + 1}, + {"EventLatency.GestureScrollBegin.Wheel.TotalLatencyToSwapEnd", + swap_end_latency_ms, 1}, + {"EventLatency.GestureScrollUpdate.Wheel.TotalLatency", total_latency_ms, + 2}, + {"EventLatency.GestureScrollUpdate.Wheel.TotalLatencyToSwapEnd", + swap_end_latency_ms, 2}, + }; + for (const auto& expected_count : expected_counts) { + histogram_tester.ExpectTotalCount(expected_count.name, + expected_count.count); + histogram_tester.ExpectBucketCount( + expected_count.name, expected_count.latency_ms, expected_count.count); + } } // Tests that when the frame is not presented to the user, event latency metrics @@ -332,12 +497,14 @@ TEST_F(CompositorFrameReporterTest, base::HistogramTester histogram_tester; const base::TimeTicks event_time = Now(); - std::vector<EventMetrics> events_metrics = { - {ui::ET_TOUCH_PRESSED, event_time, base::nullopt}, - {ui::ET_TOUCH_MOVED, event_time, base::nullopt}, - {ui::ET_TOUCH_MOVED, event_time, base::nullopt}, + std::unique_ptr<EventMetrics> event_metrics_ptrs[] = { + EventMetrics::Create(ui::ET_TOUCH_PRESSED, event_time, base::nullopt), + EventMetrics::Create(ui::ET_TOUCH_MOVED, event_time, base::nullopt), + EventMetrics::Create(ui::ET_TOUCH_MOVED, event_time, base::nullopt), }; - EXPECT_THAT(events_metrics, ::testing::Each(IsWhitelisted())); + EXPECT_THAT(event_metrics_ptrs, Each(NotNull())); + std::vector<EventMetrics> events_metrics = { + *event_metrics_ptrs[0], *event_metrics_ptrs[1], *event_metrics_ptrs[2]}; AdvanceNowByMs(3); pipeline_reporter_->StartStage( @@ -363,9 +530,8 @@ TEST_F(CompositorFrameReporterTest, pipeline_reporter_ = nullptr; - histogram_tester.ExpectTotalCount("EventLatency.TouchPressed.TotalLatency", - 0); - histogram_tester.ExpectTotalCount("EventLatency.TouchMoved.TotalLatency", 0); + EXPECT_THAT(histogram_tester.GetTotalCountsForPrefix("EventLaterncy."), + IsEmpty()); } } // namespace diff --git a/chromium/cc/metrics/compositor_frame_reporting_controller.cc b/chromium/cc/metrics/compositor_frame_reporting_controller.cc index 8a0f6dcb913..cd0ae991606 100644 --- a/chromium/cc/metrics/compositor_frame_reporting_controller.cc +++ b/chromium/cc/metrics/compositor_frame_reporting_controller.cc @@ -49,23 +49,28 @@ CompositorFrameReportingController::SubmittedCompositorFrame:: SubmittedCompositorFrame(SubmittedCompositorFrame&& other) = default; base::TimeTicks CompositorFrameReportingController::Now() const { - return base::TimeTicks::Now(); + return tick_clock_->NowTicks(); +} + +bool CompositorFrameReportingController::HasReporterAt( + PipelineStage stage) const { + return !!reporters_[stage].get(); } void CompositorFrameReportingController::WillBeginImplFrame( const viz::BeginFrameArgs& args) { base::TimeTicks begin_time = Now(); if (reporters_[PipelineStage::kBeginImplFrame]) { - // If the the reporter is replaced in this stage, it means that Impl frame - // caused no damage. - reporters_[PipelineStage::kBeginImplFrame]->TerminateFrame( - FrameTerminationStatus::kDidNotProduceFrame, begin_time); + auto& reporter = reporters_[PipelineStage::kBeginImplFrame]; + DCHECK(reporter->did_finish_impl_frame()); + DCHECK(reporter->did_not_produce_frame()); + reporter->TerminateFrame(FrameTerminationStatus::kDidNotProduceFrame, + reporter->did_not_produce_frame_time()); } - std::unique_ptr<CompositorFrameReporter> reporter = - std::make_unique<CompositorFrameReporter>( - &active_trackers_, args.frame_id, - args.frame_time + (args.interval * 1.5), latency_ukm_reporter_.get(), - should_report_metrics_); + auto reporter = std::make_unique<CompositorFrameReporter>( + active_trackers_, args.frame_id, args.frame_time + (args.interval * 1.5), + latency_ukm_reporter_.get(), should_report_metrics_); + reporter->set_tick_clock(tick_clock_); reporter->StartStage(StageType::kBeginImplFrameToSendBeginMainFrame, begin_time); reporters_[PipelineStage::kBeginImplFrame] = std::move(reporter); @@ -88,11 +93,11 @@ void CompositorFrameReportingController::WillBeginMainFrame( // In this case we have already submitted the ImplFrame, but we received // beginMain frame before next BeginImplFrame (Not reached the ImplFrame // deadline yet). So will start a new reporter at BeginMainFrame. - std::unique_ptr<CompositorFrameReporter> reporter = - std::make_unique<CompositorFrameReporter>( - &active_trackers_, args.frame_id, - args.frame_time + (args.interval * 1.5), - latency_ukm_reporter_.get(), should_report_metrics_); + auto reporter = std::make_unique<CompositorFrameReporter>( + active_trackers_, args.frame_id, + args.frame_time + (args.interval * 1.5), latency_ukm_reporter_.get(), + should_report_metrics_); + reporter->set_tick_clock(tick_clock_); reporter->StartStage(StageType::kSendBeginMainFrameToCommit, Now()); reporters_[PipelineStage::kBeginMainFrame] = std::move(reporter); } @@ -236,6 +241,7 @@ void CompositorFrameReportingController::DidSubmitCompositorFrame( } if (impl_reporter) { + impl_reporter->EnableCompositorOnlyReporting(); impl_reporter->StartStage( StageType::kSubmitCompositorFrameToPresentationCompositorFrame, Now()); impl_reporter->SetEventsMetrics( @@ -246,7 +252,8 @@ void CompositorFrameReportingController::DidSubmitCompositorFrame( } void CompositorFrameReportingController::DidNotProduceFrame( - const viz::BeginFrameId& id) { + const viz::BeginFrameId& id, + FrameSkippedReason skip_reason) { for (auto& stage_reporter : reporters_) { if (stage_reporter && stage_reporter->frame_id_ == id) { // The reporter will be flagged and terminated when replaced by another @@ -256,7 +263,7 @@ void CompositorFrameReportingController::DidNotProduceFrame( // long, then DidNotProduceFrame() is called for the reporter in the // BeginMain stage, but the main-thread can make updates, which can be // submitted with the next frame. - stage_reporter->OnDidNotProduceFrame(); + stage_reporter->OnDidNotProduceFrame(skip_reason); return; } } @@ -291,6 +298,20 @@ void CompositorFrameReportingController::DidPresentCompositorFrame( } } +void CompositorFrameReportingController::OnStoppedRequestingBeginFrames() { + // If the client stopped requesting begin-frames, that means the begin-frames + // currently being handled are no longer expected to produce any + // compositor-frames. So terminate the reporters. + auto now = Now(); + for (int i = 0; i < PipelineStage::kNumPipelineStages; ++i) { + if (reporters_[i]) { + reporters_[i]->OnDidNotProduceFrame(FrameSkippedReason::kNoDamage); + reporters_[i]->TerminateFrame(FrameTerminationStatus::kDidNotProduceFrame, + now); + } + } +} + void CompositorFrameReportingController::SetBlinkBreakdown( std::unique_ptr<BeginMainFrameMetrics> details, base::TimeTicks main_thread_start_time) { @@ -301,24 +322,31 @@ void CompositorFrameReportingController::SetBlinkBreakdown( void CompositorFrameReportingController::AddActiveTracker( FrameSequenceTrackerType type) { - active_trackers_.insert(type); + active_trackers_.set(static_cast<size_t>(type)); } void CompositorFrameReportingController::RemoveActiveTracker( FrameSequenceTrackerType type) { - active_trackers_.erase(type); + active_trackers_.reset(static_cast<size_t>(type)); } void CompositorFrameReportingController::AdvanceReporterStage( PipelineStage start, PipelineStage target) { - if (reporters_[target]) { - if (reporters_[target]->did_not_produce_frame()) - reporters_[target]->TerminateFrame( - FrameTerminationStatus::kDidNotProduceFrame, Now()); - else - reporters_[target]->TerminateFrame( - FrameTerminationStatus::kReplacedByNewReporter, Now()); + auto& reporter = reporters_[target]; + if (reporter) { + auto termination_status = FrameTerminationStatus::kReplacedByNewReporter; + base::TimeTicks termination_time; + if (reporter->did_not_produce_frame()) { + termination_time = reporter->did_not_produce_frame_time(); + termination_status = FrameTerminationStatus::kDidNotProduceFrame; + } else if (target == PipelineStage::kBeginMainFrame && + reporter->did_abort_main_frame()) { + termination_time = reporter->main_frame_abort_time(); + } else { + termination_time = Now(); + } + reporter->TerminateFrame(termination_status, termination_time); } reporters_[target] = std::move(reporters_[start]); } diff --git a/chromium/cc/metrics/compositor_frame_reporting_controller.h b/chromium/cc/metrics/compositor_frame_reporting_controller.h index 8eb99e019df..fb6b219ce27 100644 --- a/chromium/cc/metrics/compositor_frame_reporting_controller.h +++ b/chromium/cc/metrics/compositor_frame_reporting_controller.h @@ -8,17 +8,19 @@ #include <memory> #include <vector> +#include "base/time/default_tick_clock.h" #include "base/time/time.h" #include "cc/cc_export.h" #include "cc/metrics/compositor_frame_reporter.h" #include "cc/metrics/event_metrics.h" -#include "cc/metrics/frame_sequence_tracker.h" +#include "cc/metrics/frame_sequence_metrics.h" namespace viz { struct FrameTimingDetails; } namespace cc { +class UkmManager; struct BeginMainFrameMetrics; // This is used for managing simultaneous CompositorFrameReporter instances @@ -60,11 +62,13 @@ class CC_EXPORT CompositorFrameReportingController { const viz::BeginFrameId& current_frame_id, const viz::BeginFrameId& last_activated_frame_id, EventMetricsSet events_metrics); - virtual void DidNotProduceFrame(const viz::BeginFrameId& id); + virtual void DidNotProduceFrame(const viz::BeginFrameId& id, + FrameSkippedReason skip_reason); virtual void OnFinishImplFrame(const viz::BeginFrameId& id); virtual void DidPresentCompositorFrame( uint32_t frame_token, const viz::FrameTimingDetails& details); + void OnStoppedRequestingBeginFrames(); void SetBlinkBreakdown(std::unique_ptr<BeginMainFrameMetrics> details, base::TimeTicks main_thread_start_time); @@ -74,7 +78,12 @@ class CC_EXPORT CompositorFrameReportingController { virtual void AddActiveTracker(FrameSequenceTrackerType type); virtual void RemoveActiveTracker(FrameSequenceTrackerType type); - base::flat_set<FrameSequenceTrackerType> active_trackers_; + void set_tick_clock(const base::TickClock* tick_clock) { + DCHECK(tick_clock); + tick_clock_ = tick_clock; + } + + std::unique_ptr<CompositorFrameReporter>* reporters() { return reporters_; } protected: struct SubmittedCompositorFrame { @@ -87,8 +96,8 @@ class CC_EXPORT CompositorFrameReportingController { ~SubmittedCompositorFrame(); }; base::TimeTicks Now() const; - std::unique_ptr<CompositorFrameReporter> - reporters_[PipelineStage::kNumPipelineStages]; + + bool HasReporterAt(PipelineStage stage) const; private: void AdvanceReporterStage(PipelineStage start, PipelineStage target); @@ -101,6 +110,7 @@ class CC_EXPORT CompositorFrameReportingController { viz::BeginFrameId last_submitted_frame_id_; bool next_activate_has_invalidation_ = false; + CompositorFrameReporter::ActiveTrackers active_trackers_; // The latency reporter passed to each CompositorFrameReporter. Owned here // because it must be common among all reporters. @@ -108,11 +118,16 @@ class CC_EXPORT CompositorFrameReportingController { // outlive the objects in |submitted_compositor_frames_|. std::unique_ptr<LatencyUkmReporter> latency_ukm_reporter_; + std::unique_ptr<CompositorFrameReporter> + reporters_[PipelineStage::kNumPipelineStages]; + // Mapping of frame token to pipeline reporter for submitted compositor // frames. // DO NOT reorder this line and the one above. The latency_ukm_reporter_ must // outlive the objects in |submitted_compositor_frames_|. base::circular_deque<SubmittedCompositorFrame> submitted_compositor_frames_; + + const base::TickClock* tick_clock_ = base::DefaultTickClock::GetInstance(); }; } // namespace cc diff --git a/chromium/cc/metrics/compositor_frame_reporting_controller_unittest.cc b/chromium/cc/metrics/compositor_frame_reporting_controller_unittest.cc index 64da64b3a3d..ca70577d732 100644 --- a/chromium/cc/metrics/compositor_frame_reporting_controller_unittest.cc +++ b/chromium/cc/metrics/compositor_frame_reporting_controller_unittest.cc @@ -10,20 +10,20 @@ #include "base/macros.h" #include "base/strings/strcat.h" #include "base/test/metrics/histogram_tester.h" -#include "cc/input/scroll_input_type.h" +#include "base/test/simple_test_tick_clock.h" #include "cc/metrics/event_metrics.h" #include "components/viz/common/frame_timing_details.h" #include "components/viz/common/quads/compositor_frame_metadata.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/events/types/scroll_input_type.h" namespace cc { namespace { -MATCHER(IsWhitelisted, - base::StrCat({negation ? "isn't" : "is", " whitelisted"})) { - return arg.IsWhitelisted(); -} +using ::testing::Each; +using ::testing::IsEmpty; +using ::testing::NotNull; class TestCompositorFrameReportingController : public CompositorFrameReportingController { @@ -37,27 +37,35 @@ class TestCompositorFrameReportingController TestCompositorFrameReportingController& operator=( const TestCompositorFrameReportingController& controller) = delete; - std::unique_ptr<CompositorFrameReporter>* reporters() { return reporters_; } - int ActiveReporters() { int count = 0; for (int i = 0; i < PipelineStage::kNumPipelineStages; ++i) { - if (reporters_[i]) + if (reporters()[i]) ++count; } return count; } + + void ResetReporters() { + for (int i = 0; i < PipelineStage::kNumPipelineStages; ++i) { + reporters()[i] = nullptr; + } + } }; class CompositorFrameReportingControllerTest : public testing::Test { public: CompositorFrameReportingControllerTest() : current_id_(1, 1) { + test_tick_clock_.SetNowTicks(base::TimeTicks::Now()); + reporting_controller_.set_tick_clock(&test_tick_clock_); args_ = SimulateBeginFrameArgs(current_id_); } // The following functions simulate the actions that would // occur for each phase of the reporting controller. void SimulateBeginImplFrame() { + IncrementCurrentId(); + begin_impl_time_ = AdvanceNowByMs(10); reporting_controller_.WillBeginImplFrame(args_); } @@ -68,22 +76,25 @@ class CompositorFrameReportingControllerTest : public testing::Test { CHECK( reporting_controller_.reporters()[CompositorFrameReportingController:: PipelineStage::kBeginImplFrame]); + begin_main_time_ = AdvanceNowByMs(10); reporting_controller_.WillBeginMainFrame(args_); + begin_main_start_time_ = AdvanceNowByMs(10); } void SimulateCommit(std::unique_ptr<BeginMainFrameMetrics> blink_breakdown) { if (!reporting_controller_ .reporters()[CompositorFrameReportingController::PipelineStage:: kBeginMainFrame]) { - begin_main_start_ = base::TimeTicks::Now(); SimulateBeginMainFrame(); } CHECK( reporting_controller_.reporters()[CompositorFrameReportingController:: PipelineStage::kBeginMainFrame]); reporting_controller_.SetBlinkBreakdown(std::move(blink_breakdown), - begin_main_start_); + begin_main_start_time_); + begin_commit_time_ = AdvanceNowByMs(10); reporting_controller_.WillCommit(); + end_commit_time_ = AdvanceNowByMs(10); reporting_controller_.DidCommit(); } @@ -93,7 +104,9 @@ class CompositorFrameReportingControllerTest : public testing::Test { SimulateCommit(nullptr); CHECK(reporting_controller_.reporters() [CompositorFrameReportingController::PipelineStage::kCommit]); + begin_activation_time_ = AdvanceNowByMs(10); reporting_controller_.WillActivate(); + end_activation_time_ = AdvanceNowByMs(10); reporting_controller_.DidActivate(); last_activated_id_ = current_id_; } @@ -105,6 +118,7 @@ class CompositorFrameReportingControllerTest : public testing::Test { SimulateActivate(); CHECK(reporting_controller_.reporters() [CompositorFrameReportingController::PipelineStage::kActivate]); + submit_time_ = AdvanceNowByMs(10); reporting_controller_.DidSubmitCompositorFrame(frame_token, current_id_, last_activated_id_, std::move(events_metrics)); @@ -114,32 +128,74 @@ class CompositorFrameReportingControllerTest : public testing::Test { ++next_token_; SimulateSubmitCompositorFrame(*next_token_, {}); viz::FrameTimingDetails details = {}; - details.presentation_feedback.timestamp = base::TimeTicks::Now(); + details.presentation_feedback.timestamp = AdvanceNowByMs(10); reporting_controller_.DidPresentCompositorFrame(*next_token_, details); } - viz::BeginFrameArgs SimulateBeginFrameArgs( - viz::BeginFrameId frame_id, - base::TimeTicks frame_time = base::TimeTicks::Now(), - base::TimeDelta interval = base::TimeDelta::FromMilliseconds(16)) { + viz::BeginFrameArgs SimulateBeginFrameArgs(viz::BeginFrameId frame_id) { args_ = viz::BeginFrameArgs(); args_.frame_id = frame_id; - args_.frame_time = frame_time; - args_.interval = interval; + args_.frame_time = AdvanceNowByMs(10); + args_.interval = base::TimeDelta::FromMilliseconds(16); return args_; } + std::unique_ptr<BeginMainFrameMetrics> BuildBlinkBreakdown() { + auto breakdown = std::make_unique<BeginMainFrameMetrics>(); + breakdown->handle_input_events = base::TimeDelta::FromMicroseconds(10); + breakdown->animate = base::TimeDelta::FromMicroseconds(9); + breakdown->style_update = base::TimeDelta::FromMicroseconds(8); + breakdown->layout_update = base::TimeDelta::FromMicroseconds(7); + breakdown->prepaint = base::TimeDelta::FromMicroseconds(6); + breakdown->composite = base::TimeDelta::FromMicroseconds(5); + breakdown->paint = base::TimeDelta::FromMicroseconds(4); + breakdown->scrolling_coordinator = base::TimeDelta::FromMicroseconds(3); + breakdown->composite_commit = base::TimeDelta::FromMicroseconds(2); + breakdown->update_layers = base::TimeDelta::FromMicroseconds(1); + + // Advance now by the sum of the breakdowns. + AdvanceNowByMs(10 + 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1); + + return breakdown; + } + + viz::FrameTimingDetails BuildVizBreakdown() { + viz::FrameTimingDetails 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); + return viz_breakdown; + } + void IncrementCurrentId() { current_id_.sequence_number++; args_.frame_id = current_id_; } + base::TimeTicks AdvanceNowByMs(int64_t advance_ms) { + test_tick_clock_.Advance(base::TimeDelta::FromMicroseconds(advance_ms)); + return test_tick_clock_.NowTicks(); + } + protected: + // This should be defined before |reporting_controller_| so it is created + // before and destroyed after that. + base::SimpleTestTickClock test_tick_clock_; + TestCompositorFrameReportingController reporting_controller_; viz::BeginFrameArgs args_; viz::BeginFrameId current_id_; viz::BeginFrameId last_activated_id_; - base::TimeTicks begin_main_start_; + base::TimeTicks begin_impl_time_; + base::TimeTicks begin_main_time_; + base::TimeTicks begin_main_start_time_; + base::TimeTicks begin_commit_time_; + base::TimeTicks end_commit_time_; + base::TimeTicks begin_activation_time_; + base::TimeTicks end_activation_time_; + base::TimeTicks submit_time_; viz::FrameTokenGenerator next_token_; }; @@ -166,11 +222,17 @@ TEST_F(CompositorFrameReportingControllerTest, ActiveReporterCounts) { // BF reporting_controller_.WillBeginImplFrame(args_1); EXPECT_EQ(1, reporting_controller_.ActiveReporters()); + reporting_controller_.OnFinishImplFrame(args_1.frame_id); + reporting_controller_.DidNotProduceFrame(args_1.frame_id, + FrameSkippedReason::kNoDamage); // BF -> BF // Should replace previous reporter. reporting_controller_.WillBeginImplFrame(args_2); EXPECT_EQ(1, reporting_controller_.ActiveReporters()); + reporting_controller_.OnFinishImplFrame(args_2.frame_id); + reporting_controller_.DidNotProduceFrame(args_2.frame_id, + FrameSkippedReason::kNoDamage); // BF -> BMF -> BF // Should add new reporter. @@ -207,22 +269,47 @@ TEST_F(CompositorFrameReportingControllerTest, ActiveReporterCounts) { last_activated_id_, {}); EXPECT_EQ(0, reporting_controller_.ActiveReporters()); - // 4 simultaneous reporters active. + // Start a frame and take it all the way to the activate stage. SimulateActivate(); + EXPECT_EQ(1, reporting_controller_.ActiveReporters()); + // Start another frame and let it progress up to the commit stage. SimulateCommit(nullptr); + EXPECT_EQ(2, reporting_controller_.ActiveReporters()); + // Start the next frame, and let it progress up to the main-frame. SimulateBeginMainFrame(); + EXPECT_EQ(3, reporting_controller_.ActiveReporters()); + // Start the next frame. SimulateBeginImplFrame(); EXPECT_EQ(4, reporting_controller_.ActiveReporters()); + reporting_controller_.OnFinishImplFrame(args_.frame_id); + reporting_controller_.DidNotProduceFrame(args_.frame_id, + FrameSkippedReason::kNoDamage); + // Any additional BeginImplFrame's would be ignored. SimulateBeginImplFrame(); EXPECT_EQ(4, reporting_controller_.ActiveReporters()); } TEST_F(CompositorFrameReportingControllerTest, + StopRequestingFramesCancelsInFlightFrames) { + base::HistogramTester histogram_tester; + + // 2 reporters active. + SimulateActivate(); + SimulateCommit(nullptr); + + reporting_controller_.OnStoppedRequestingBeginFrames(); + reporting_controller_.ResetReporters(); + histogram_tester.ExpectBucketCount( + "CompositorLatency.Type", + CompositorFrameReporter::FrameReportType::kDroppedFrame, 0); +} + +TEST_F(CompositorFrameReportingControllerTest, SubmittedFrameHistogramReporting) { base::HistogramTester histogram_tester; @@ -288,9 +375,25 @@ TEST_F(CompositorFrameReportingControllerTest, ImplFrameCausedNoDamage) { base::HistogramTester histogram_tester; SimulateBeginImplFrame(); + reporting_controller_.OnFinishImplFrame(args_.frame_id); + reporting_controller_.DidNotProduceFrame(args_.frame_id, + FrameSkippedReason::kNoDamage); SimulateBeginImplFrame(); histogram_tester.ExpectTotalCount( "CompositorLatency.DroppedFrame.BeginImplFrameToSendBeginMainFrame", 0); + histogram_tester.ExpectBucketCount( + "CompositorLatency.Type", + CompositorFrameReporter::FrameReportType::kDroppedFrame, 0); + + reporting_controller_.OnFinishImplFrame(args_.frame_id); + reporting_controller_.DidNotProduceFrame(args_.frame_id, + FrameSkippedReason::kWaitingOnMain); + SimulateBeginImplFrame(); + histogram_tester.ExpectTotalCount( + "CompositorLatency.DroppedFrame.BeginImplFrameToSendBeginMainFrame", 1); + histogram_tester.ExpectBucketCount( + "CompositorLatency.Type", + CompositorFrameReporter::FrameReportType::kDroppedFrame, 1); } TEST_F(CompositorFrameReportingControllerTest, MainFrameCausedNoDamage) { @@ -308,13 +411,15 @@ TEST_F(CompositorFrameReportingControllerTest, MainFrameCausedNoDamage) { reporting_controller_.WillBeginMainFrame(args_1); reporting_controller_.BeginMainFrameAborted(current_id_1); reporting_controller_.OnFinishImplFrame(current_id_1); - reporting_controller_.DidNotProduceFrame(current_id_1); + reporting_controller_.DidNotProduceFrame(current_id_1, + FrameSkippedReason::kNoDamage); reporting_controller_.WillBeginImplFrame(args_2); reporting_controller_.WillBeginMainFrame(args_2); reporting_controller_.OnFinishImplFrame(current_id_2); reporting_controller_.BeginMainFrameAborted(current_id_2); - reporting_controller_.DidNotProduceFrame(current_id_2); + reporting_controller_.DidNotProduceFrame(current_id_2, + FrameSkippedReason::kNoDamage); reporting_controller_.WillBeginImplFrame(args_3); reporting_controller_.WillBeginMainFrame(args_3); @@ -337,7 +442,8 @@ TEST_F(CompositorFrameReportingControllerTest, DidNotProduceFrame) { reporting_controller_.WillBeginImplFrame(args_1); reporting_controller_.WillBeginMainFrame(args_1); reporting_controller_.OnFinishImplFrame(current_id_1); - reporting_controller_.DidNotProduceFrame(current_id_1); + reporting_controller_.DidNotProduceFrame(current_id_1, + FrameSkippedReason::kNoDamage); reporting_controller_.WillBeginImplFrame(args_2); reporting_controller_.OnFinishImplFrame(current_id_2); @@ -367,6 +473,73 @@ TEST_F(CompositorFrameReportingControllerTest, DidNotProduceFrame) { histogram_tester.ExpectTotalCount( "CompositorLatency.SubmitCompositorFrameToPresentationCompositorFrame", 2); + histogram_tester.ExpectTotalCount( + "CompositorLatency.CompositorOnlyFrame.BeginImplFrameToFinishImpl", 1); + histogram_tester.ExpectTotalCount( + "CompositorLatency.CompositorOnlyFrame." + "ImplFrameDoneToSubmitCompositorFrame", + 1); + histogram_tester.ExpectTotalCount( + "CompositorLatency.CompositorOnlyFrame." + "SubmitCompositorFrameToPresentationCompositorFrame", + 1); +} + +TEST_F(CompositorFrameReportingControllerTest, + DidNotProduceFrameDueToWaitingOnMain) { + base::HistogramTester histogram_tester; + + viz::BeginFrameId current_id_1(1, 1); + viz::BeginFrameArgs args_1 = SimulateBeginFrameArgs(current_id_1); + + viz::BeginFrameId current_id_2(1, 2); + viz::BeginFrameArgs args_2 = SimulateBeginFrameArgs(current_id_2); + args_2.frame_time = args_1.frame_time + args_1.interval; + + viz::BeginFrameId current_id_3(1, 3); + viz::BeginFrameArgs args_3 = SimulateBeginFrameArgs(current_id_3); + args_3.frame_time = args_2.frame_time + args_2.interval; + + reporting_controller_.WillBeginImplFrame(args_1); + reporting_controller_.WillBeginMainFrame(args_1); + reporting_controller_.OnFinishImplFrame(current_id_1); + reporting_controller_.DidNotProduceFrame(current_id_1, + FrameSkippedReason::kWaitingOnMain); + + reporting_controller_.WillBeginImplFrame(args_2); + reporting_controller_.OnFinishImplFrame(current_id_2); + reporting_controller_.DidNotProduceFrame(current_id_2, + FrameSkippedReason::kWaitingOnMain); + + reporting_controller_.WillBeginImplFrame(args_3); + reporting_controller_.WillCommit(); + reporting_controller_.DidCommit(); + reporting_controller_.WillActivate(); + reporting_controller_.DidActivate(); + reporting_controller_.OnFinishImplFrame(current_id_3); + reporting_controller_.DidSubmitCompositorFrame(1, current_id_3, current_id_1, + {}); + viz::FrameTimingDetails details; + details.presentation_feedback = {args_3.frame_time + args_3.interval, + args_3.interval, 0}; + reporting_controller_.DidPresentCompositorFrame(1, details); + + // Frame for |args_2| was dropped waiting on the main-thread. + histogram_tester.ExpectBucketCount( + "CompositorLatency.Type", + CompositorFrameReporter::FrameReportType::kDroppedFrame, 1); + + // Frames for |args_1| and |args_3| were presented, although |args_1| missed + // its deadline. + histogram_tester.ExpectBucketCount( + "CompositorLatency.Type", + CompositorFrameReporter::FrameReportType::kNonDroppedFrame, 2); + histogram_tester.ExpectBucketCount( + "CompositorLatency.Type", + CompositorFrameReporter::FrameReportType::kMissedDeadlineFrame, 1); + histogram_tester.ExpectBucketCount( + "CompositorLatency.Type", + CompositorFrameReporter::FrameReportType::kCompositorOnlyFrame, 1); } TEST_F(CompositorFrameReportingControllerTest, MainFrameAborted) { @@ -384,6 +557,8 @@ TEST_F(CompositorFrameReportingControllerTest, MainFrameAborted) { histogram_tester.ExpectTotalCount( "CompositorLatency.BeginImplFrameToSendBeginMainFrame", 1); + histogram_tester.ExpectTotalCount( + "CompositorLatency.SendBeginMainFrameToCommit", 1); histogram_tester.ExpectTotalCount("CompositorLatency.Commit", 0); histogram_tester.ExpectTotalCount("CompositorLatency.Activation", 0); histogram_tester.ExpectTotalCount( @@ -391,6 +566,20 @@ TEST_F(CompositorFrameReportingControllerTest, MainFrameAborted) { histogram_tester.ExpectTotalCount( "CompositorLatency.SubmitCompositorFrameToPresentationCompositorFrame", 1); + histogram_tester.ExpectTotalCount( + "CompositorLatency.CompositorOnlyFrame.BeginImplFrameToFinishImpl", 1); + histogram_tester.ExpectTotalCount( + "CompositorLatency.CompositorOnlyFrame." + "SendBeginMainFrameToBeginMainAbort", + 1); + histogram_tester.ExpectTotalCount( + "CompositorLatency.CompositorOnlyFrame." + "ImplFrameDoneToSubmitCompositorFrame", + 1); + histogram_tester.ExpectTotalCount( + "CompositorLatency.CompositorOnlyFrame." + "SubmitCompositorFrameToPresentationCompositorFrame", + 1); } TEST_F(CompositorFrameReportingControllerTest, MainFrameAborted2) { @@ -533,6 +722,20 @@ TEST_F(CompositorFrameReportingControllerTest, LongMainFrame) { histogram_tester.ExpectTotalCount( "CompositorLatency.SubmitCompositorFrameToPresentationCompositorFrame", 2); + histogram_tester.ExpectTotalCount( + "CompositorLatency.CompositorOnlyFrame.BeginImplFrameToFinishImpl", 1); + histogram_tester.ExpectTotalCount( + "CompositorLatency.CompositorOnlyFrame." + "SendBeginMainFrameToBeginMainAbort", + 0); + histogram_tester.ExpectTotalCount( + "CompositorLatency.CompositorOnlyFrame." + "ImplFrameDoneToSubmitCompositorFrame", + 1); + histogram_tester.ExpectTotalCount( + "CompositorLatency.CompositorOnlyFrame." + "SubmitCompositorFrameToPresentationCompositorFrame", + 1); reporting_controller_.WillBeginImplFrame(args_3); reporting_controller_.OnFinishImplFrame(current_id_3); @@ -557,6 +760,20 @@ TEST_F(CompositorFrameReportingControllerTest, LongMainFrame) { histogram_tester.ExpectTotalCount( "CompositorLatency.SubmitCompositorFrameToPresentationCompositorFrame", 4); + histogram_tester.ExpectTotalCount( + "CompositorLatency.CompositorOnlyFrame.BeginImplFrameToFinishImpl", 2); + histogram_tester.ExpectTotalCount( + "CompositorLatency.CompositorOnlyFrame." + "SendBeginMainFrameToBeginMainAbort", + 0); + histogram_tester.ExpectTotalCount( + "CompositorLatency.CompositorOnlyFrame." + "ImplFrameDoneToSubmitCompositorFrame", + 2); + histogram_tester.ExpectTotalCount( + "CompositorLatency.CompositorOnlyFrame." + "SubmitCompositorFrameToPresentationCompositorFrame", + 2); } TEST_F(CompositorFrameReportingControllerTest, LongMainFrame2) { @@ -617,24 +834,27 @@ TEST_F(CompositorFrameReportingControllerTest, LongMainFrame2) { histogram_tester.ExpectTotalCount( "CompositorLatency.SubmitCompositorFrameToPresentationCompositorFrame", 2); + histogram_tester.ExpectTotalCount( + "CompositorLatency.CompositorOnlyFrame.BeginImplFrameToFinishImpl", 1); + histogram_tester.ExpectTotalCount( + "CompositorLatency.CompositorOnlyFrame." + "SendBeginMainFrameToBeginMainAbort", + 0); + histogram_tester.ExpectTotalCount( + "CompositorLatency.CompositorOnlyFrame." + "ImplFrameDoneToSubmitCompositorFrame", + 1); + histogram_tester.ExpectTotalCount( + "CompositorLatency.CompositorOnlyFrame." + "SubmitCompositorFrameToPresentationCompositorFrame", + 1); } TEST_F(CompositorFrameReportingControllerTest, BlinkBreakdown) { base::HistogramTester histogram_tester; std::unique_ptr<BeginMainFrameMetrics> blink_breakdown = - std::make_unique<BeginMainFrameMetrics>(); - blink_breakdown->handle_input_events = base::TimeDelta::FromMicroseconds(10); - blink_breakdown->animate = base::TimeDelta::FromMicroseconds(9); - blink_breakdown->style_update = base::TimeDelta::FromMicroseconds(8); - blink_breakdown->layout_update = base::TimeDelta::FromMicroseconds(7); - blink_breakdown->prepaint = base::TimeDelta::FromMicroseconds(6); - blink_breakdown->composite = base::TimeDelta::FromMicroseconds(5); - blink_breakdown->paint = base::TimeDelta::FromMicroseconds(4); - blink_breakdown->scrolling_coordinator = base::TimeDelta::FromMicroseconds(3); - blink_breakdown->composite_commit = base::TimeDelta::FromMicroseconds(2); - blink_breakdown->update_layers = base::TimeDelta::FromMicroseconds(1); - + BuildBlinkBreakdown(); SimulateActivate(); SimulateCommit(std::move(blink_breakdown)); SimulatePresentCompositorFrame(); @@ -710,6 +930,8 @@ TEST_F(CompositorFrameReportingControllerTest, ReportingMissedDeadlineFrame1) { histogram_tester.ExpectBucketCount("CompositorLatency.Type", 1, 0); // Dropped cases. histogram_tester.ExpectBucketCount("CompositorLatency.Type", 2, 0); + // Impl only cases. + histogram_tester.ExpectBucketCount("CompositorLatency.Type", 3, 0); } // If the presentation of the frame happens after deadline. @@ -749,19 +971,21 @@ TEST_F(CompositorFrameReportingControllerTest, ReportingMissedDeadlineFrame2) { histogram_tester.ExpectBucketCount("CompositorLatency.Type", 2, 0); } -// Tests that EventLatency histograms are reported properly when a frame is -// presented to the user. +// Tests that EventLatency total latency histograms are reported properly when a +// frame is presented to the user. TEST_F(CompositorFrameReportingControllerTest, - EventLatencyForPresentedFrameReported) { + EventLatencyTotalForPresentedFrameReported) { base::HistogramTester histogram_tester; - const base::TimeTicks event_time = base::TimeTicks::Now(); - std::vector<EventMetrics> events_metrics = { - {ui::ET_TOUCH_PRESSED, event_time, base::nullopt}, - {ui::ET_TOUCH_MOVED, event_time, base::nullopt}, - {ui::ET_TOUCH_MOVED, event_time, base::nullopt}, + const base::TimeTicks event_time = AdvanceNowByMs(10); + std::unique_ptr<EventMetrics> event_metrics_ptrs[] = { + EventMetrics::Create(ui::ET_TOUCH_PRESSED, event_time, base::nullopt), + EventMetrics::Create(ui::ET_TOUCH_MOVED, event_time, base::nullopt), + EventMetrics::Create(ui::ET_TOUCH_MOVED, event_time, base::nullopt), }; - EXPECT_THAT(events_metrics, ::testing::Each(IsWhitelisted())); + EXPECT_THAT(event_metrics_ptrs, Each(NotNull())); + std::vector<EventMetrics> events_metrics = { + *event_metrics_ptrs[0], *event_metrics_ptrs[1], *event_metrics_ptrs[2]}; // Submit a compositor frame and notify CompositorFrameReporter of the events // affecting the frame. @@ -769,13 +993,13 @@ TEST_F(CompositorFrameReportingControllerTest, SimulateSubmitCompositorFrame(*next_token_, {std::move(events_metrics), {}}); // Present the submitted compositor frame to the user. - const base::TimeTicks presentation_time = base::TimeTicks::Now(); + const base::TimeTicks presentation_time = AdvanceNowByMs(10); viz::FrameTimingDetails details; details.presentation_feedback.timestamp = presentation_time; reporting_controller_.DidPresentCompositorFrame(*next_token_, details); // Verify that EventLatency histograms are recorded. - const int latency_ms = (presentation_time - event_time).InMicroseconds(); + const int64_t latency_ms = (presentation_time - event_time).InMicroseconds(); histogram_tester.ExpectTotalCount("EventLatency.TouchPressed.TotalLatency", 1); histogram_tester.ExpectTotalCount("EventLatency.TouchMoved.TotalLatency", 2); @@ -785,19 +1009,134 @@ TEST_F(CompositorFrameReportingControllerTest, latency_ms, 2); } -// Tests that EventLatency histograms are reported properly for scroll events -// when a frame is presented to the user. +// Tests that EventLatency breakdown histograms are reported properly when a +// frame is presented to the user. TEST_F(CompositorFrameReportingControllerTest, - EventLatencyScrollForPresentedFrameReported) { + EventLatencyBreakdownsForPresentedFrameReported) { base::HistogramTester histogram_tester; - const base::TimeTicks event_time = base::TimeTicks::Now(); - std::vector<EventMetrics> events_metrics = { - {ui::ET_GESTURE_SCROLL_BEGIN, event_time, ScrollInputType::kWheel}, - {ui::ET_GESTURE_SCROLL_UPDATE, event_time, ScrollInputType::kWheel}, - {ui::ET_GESTURE_SCROLL_UPDATE, event_time, ScrollInputType::kWheel}, + const base::TimeTicks event_time = AdvanceNowByMs(10); + std::unique_ptr<EventMetrics> event_metrics_ptrs[] = { + EventMetrics::Create(ui::ET_TOUCH_PRESSED, event_time, base::nullopt), }; - EXPECT_THAT(events_metrics, ::testing::Each(IsWhitelisted())); + EXPECT_THAT(event_metrics_ptrs, Each(NotNull())); + std::vector<EventMetrics> events_metrics = {*event_metrics_ptrs[0]}; + + // Do a commit with a breakdown of blink stages. + std::unique_ptr<BeginMainFrameMetrics> blink_breakdown = + BuildBlinkBreakdown(); + // Make a copy of the breakdown to use in verifying expectations in the end. + BeginMainFrameMetrics blink_breakdown_copy = *blink_breakdown; + SimulateCommit(std::move(blink_breakdown)); + + // Submit a compositor frame and notify CompositorFrameReporter of the events + // affecting the frame. + ++next_token_; + SimulateSubmitCompositorFrame(*next_token_, {std::move(events_metrics), {}}); + + // Present the submitted compositor frame to the user. + AdvanceNowByMs(10); + viz::FrameTimingDetails viz_breakdown = BuildVizBreakdown(); + reporting_controller_.DidPresentCompositorFrame(*next_token_, viz_breakdown); + + // Verify that EventLatency histograms are recorded. + struct { + const char* name; + const base::TimeDelta latency; + } expected_latencies[] = { + {"EventLatency.TouchPressed.BrowserToRendererCompositor", + begin_impl_time_ - event_time}, + {"EventLatency.TouchPressed.BeginImplFrameToSendBeginMainFrame", + begin_main_time_ - begin_impl_time_}, + {"EventLatency.TouchPressed.SendBeginMainFrameToCommit", + begin_commit_time_ - begin_main_time_}, + {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.HandleInputEvents", + blink_breakdown_copy.handle_input_events}, + {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.Animate", + blink_breakdown_copy.animate}, + {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.StyleUpdate", + blink_breakdown_copy.style_update}, + {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.LayoutUpdate", + blink_breakdown_copy.layout_update}, + {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.Prepaint", + blink_breakdown_copy.prepaint}, + {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.Composite", + blink_breakdown_copy.composite}, + {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.Paint", + blink_breakdown_copy.paint}, + {"EventLatency.TouchPressed.SendBeginMainFrameToCommit." + "ScrollingCoordinator", + blink_breakdown_copy.scrolling_coordinator}, + {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.CompositeCommit", + blink_breakdown_copy.composite_commit}, + {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.UpdateLayers", + blink_breakdown_copy.update_layers}, + {"EventLatency.TouchPressed.SendBeginMainFrameToCommit." + "BeginMainSentToStarted", + begin_main_start_time_ - begin_main_time_}, + {"EventLatency.TouchPressed.Commit", + end_commit_time_ - begin_commit_time_}, + {"EventLatency.TouchPressed.EndCommitToActivation", + begin_activation_time_ - end_commit_time_}, + {"EventLatency.TouchPressed.Activation", + end_activation_time_ - begin_activation_time_}, + {"EventLatency.TouchPressed.EndActivateToSubmitCompositorFrame", + submit_time_ - end_activation_time_}, + {"EventLatency.TouchPressed." + "SubmitCompositorFrameToPresentationCompositorFrame", + viz_breakdown.presentation_feedback.timestamp - submit_time_}, + {"EventLatency.TouchPressed." + "SubmitCompositorFrameToPresentationCompositorFrame." + "SubmitToReceiveCompositorFrame", + viz_breakdown.received_compositor_frame_timestamp - submit_time_}, + {"EventLatency.TouchPressed." + "SubmitCompositorFrameToPresentationCompositorFrame." + "ReceivedCompositorFrameToStartDraw", + viz_breakdown.draw_start_timestamp - + viz_breakdown.received_compositor_frame_timestamp}, + {"EventLatency.TouchPressed." + "SubmitCompositorFrameToPresentationCompositorFrame." + "StartDrawToSwapStart", + viz_breakdown.swap_timings.swap_start - + viz_breakdown.draw_start_timestamp}, + {"EventLatency.TouchPressed." + "SubmitCompositorFrameToPresentationCompositorFrame.SwapStartToSwapEnd", + viz_breakdown.swap_timings.swap_end - + viz_breakdown.swap_timings.swap_start}, + {"EventLatency.TouchPressed." + "SubmitCompositorFrameToPresentationCompositorFrame." + "SwapEndToPresentationCompositorFrame", + viz_breakdown.presentation_feedback.timestamp - + viz_breakdown.swap_timings.swap_end}, + {"EventLatency.TouchPressed.TotalLatency", + viz_breakdown.presentation_feedback.timestamp - event_time}, + }; + + for (const auto& expected_latency : expected_latencies) { + histogram_tester.ExpectTotalCount(expected_latency.name, 1); + histogram_tester.ExpectBucketCount( + expected_latency.name, expected_latency.latency.InMicroseconds(), 1); + } +} + +// Tests that EventLatency total latency histograms are reported properly for +// scroll events when a frame is presented to the user. +TEST_F(CompositorFrameReportingControllerTest, + EventLatencyScrollTotalForPresentedFrameReported) { + base::HistogramTester histogram_tester; + + const base::TimeTicks event_time = AdvanceNowByMs(10); + std::unique_ptr<EventMetrics> event_metrics_ptrs[] = { + EventMetrics::Create(ui::ET_GESTURE_SCROLL_BEGIN, event_time, + ui::ScrollInputType::kWheel), + EventMetrics::Create(ui::ET_GESTURE_SCROLL_UPDATE, event_time, + ui::ScrollInputType::kWheel), + EventMetrics::Create(ui::ET_GESTURE_SCROLL_UPDATE, event_time, + ui::ScrollInputType::kWheel), + }; + EXPECT_THAT(event_metrics_ptrs, Each(NotNull())); + std::vector<EventMetrics> events_metrics = { + *event_metrics_ptrs[0], *event_metrics_ptrs[1], *event_metrics_ptrs[2]}; // Submit a compositor frame and notify CompositorFrameReporter of the events // affecting the frame. @@ -805,21 +1144,39 @@ TEST_F(CompositorFrameReportingControllerTest, SimulateSubmitCompositorFrame(*next_token_, {std::move(events_metrics), {}}); // Present the submitted compositor frame to the user. - const base::TimeTicks presentation_time = base::TimeTicks::Now(); viz::FrameTimingDetails details; - details.presentation_feedback.timestamp = presentation_time; + details.received_compositor_frame_timestamp = AdvanceNowByMs(10); + details.draw_start_timestamp = AdvanceNowByMs(10); + details.swap_timings.swap_start = AdvanceNowByMs(10); + details.swap_timings.swap_end = AdvanceNowByMs(10); + details.presentation_feedback.timestamp = AdvanceNowByMs(10); reporting_controller_.DidPresentCompositorFrame(*next_token_, details); // Verify that EventLatency histograms are recorded. - const int latency_ms = (presentation_time - event_time).InMicroseconds(); - histogram_tester.ExpectTotalCount( - "EventLatency.GestureScrollBegin.Wheel.TotalLatency", 1); - histogram_tester.ExpectTotalCount( - "EventLatency.GestureScrollUpdate.Wheel.TotalLatency", 2); - histogram_tester.ExpectBucketCount( - "EventLatency.GestureScrollBegin.Wheel.TotalLatency", latency_ms, 1); - histogram_tester.ExpectBucketCount( - "EventLatency.GestureScrollUpdate.Wheel.TotalLatency", latency_ms, 2); + const int64_t total_latency_ms = + (details.presentation_feedback.timestamp - event_time).InMicroseconds(); + const int64_t swap_end_latency_ms = + (details.swap_timings.swap_end - event_time).InMicroseconds(); + struct { + const char* name; + const int64_t latency_ms; + const int count; + } expected_counts[] = { + {"EventLatency.GestureScrollBegin.Wheel.TotalLatency", total_latency_ms, + 1}, + {"EventLatency.GestureScrollBegin.Wheel.TotalLatencyToSwapEnd", + swap_end_latency_ms, 1}, + {"EventLatency.GestureScrollUpdate.Wheel.TotalLatency", total_latency_ms, + 2}, + {"EventLatency.GestureScrollUpdate.Wheel.TotalLatencyToSwapEnd", + swap_end_latency_ms, 2}, + }; + for (const auto& expected_count : expected_counts) { + histogram_tester.ExpectTotalCount(expected_count.name, + expected_count.count); + histogram_tester.ExpectBucketCount( + expected_count.name, expected_count.latency_ms, expected_count.count); + } } // Tests that EventLatency histograms are not reported when the frame is dropped @@ -828,13 +1185,15 @@ TEST_F(CompositorFrameReportingControllerTest, EventLatencyForDidNotPresentFrameNotReported) { base::HistogramTester histogram_tester; - const base::TimeTicks event_time = base::TimeTicks::Now(); - std::vector<EventMetrics> events_metrics = { - {ui::ET_TOUCH_PRESSED, event_time, base::nullopt}, - {ui::ET_TOUCH_MOVED, event_time, base::nullopt}, - {ui::ET_TOUCH_MOVED, event_time, base::nullopt}, + const base::TimeTicks event_time = AdvanceNowByMs(10); + std::unique_ptr<EventMetrics> event_metrics_ptrs[] = { + EventMetrics::Create(ui::ET_TOUCH_PRESSED, event_time, base::nullopt), + EventMetrics::Create(ui::ET_TOUCH_MOVED, event_time, base::nullopt), + EventMetrics::Create(ui::ET_TOUCH_MOVED, event_time, base::nullopt), }; - EXPECT_THAT(events_metrics, ::testing::Each(IsWhitelisted())); + EXPECT_THAT(event_metrics_ptrs, Each(NotNull())); + std::vector<EventMetrics> events_metrics = { + *event_metrics_ptrs[0], *event_metrics_ptrs[1], *event_metrics_ptrs[2]}; // Submit a compositor frame and notify CompositorFrameReporter of the events // affecting the frame. @@ -848,13 +1207,12 @@ TEST_F(CompositorFrameReportingControllerTest, // Present the second compositor frame to the uesr, dropping the first one. viz::FrameTimingDetails details; - details.presentation_feedback.timestamp = base::TimeTicks::Now(); + details.presentation_feedback.timestamp = AdvanceNowByMs(10); reporting_controller_.DidPresentCompositorFrame(*next_token_, details); // Verify that no EventLatency histogram is recorded. - histogram_tester.ExpectTotalCount("EventLatency.TouchPressed.TotalLatency", - 0); - histogram_tester.ExpectTotalCount("EventLatency.TouchMoved.TotalLatency", 0); + EXPECT_THAT(histogram_tester.GetTotalCountsForPrefix("EventLatency."), + IsEmpty()); } } // namespace diff --git a/chromium/cc/metrics/compositor_timing_history.cc b/chromium/cc/metrics/compositor_timing_history.cc index db507429503..d0cc7d44262 100644 --- a/chromium/cc/metrics/compositor_timing_history.cc +++ b/chromium/cc/metrics/compositor_timing_history.cc @@ -514,13 +514,10 @@ CompositorTimingHistory::CreateUMAReporter(UMACategory category) { switch (category) { case RENDERER_UMA: return base::WrapUnique(new RendererUMAReporter); - break; case BROWSER_UMA: return base::WrapUnique(new BrowserUMAReporter); - break; case NULL_UMA: return base::WrapUnique(new NullUMAReporter); - break; } NOTREACHED(); return base::WrapUnique<CompositorTimingHistory::UMAReporter>(nullptr); @@ -662,6 +659,7 @@ void CompositorTimingHistory::WillFinishImplFrame(bool needs_redraw, void CompositorTimingHistory::BeginImplFrameNotExpectedSoon() { SetBeginMainFrameNeededContinuously(false); SetCompositorDrawingContinuously(false); + compositor_frame_reporting_controller_->OnStoppedRequestingBeginFrames(); } void CompositorTimingHistory::WillBeginMainFrame( @@ -951,8 +949,10 @@ void CompositorTimingHistory::DidSubmitCompositorFrame( submit_start_time_ = Now(); } -void CompositorTimingHistory::DidNotProduceFrame(const viz::BeginFrameId& id) { - compositor_frame_reporting_controller_->DidNotProduceFrame(id); +void CompositorTimingHistory::DidNotProduceFrame( + const viz::BeginFrameId& id, + FrameSkippedReason skip_reason) { + compositor_frame_reporting_controller_->DidNotProduceFrame(id, skip_reason); } void CompositorTimingHistory::DidReceiveCompositorFrameAck() { diff --git a/chromium/cc/metrics/compositor_timing_history.h b/chromium/cc/metrics/compositor_timing_history.h index 9ec86a2328c..1d265a8419a 100644 --- a/chromium/cc/metrics/compositor_timing_history.h +++ b/chromium/cc/metrics/compositor_timing_history.h @@ -11,6 +11,7 @@ #include "cc/base/rolling_time_delta_history.h" #include "cc/cc_export.h" #include "cc/metrics/event_metrics.h" +#include "cc/scheduler/scheduler.h" #include "cc/tiles/tile_priority.h" #include "components/viz/common/frame_sinks/begin_frame_args.h" @@ -100,7 +101,8 @@ class CC_EXPORT CompositorTimingHistory { const viz::BeginFrameId& current_frame_id, const viz::BeginFrameId& last_activated_frame_id, EventMetricsSet events_metrics); - void DidNotProduceFrame(const viz::BeginFrameId& id); + void DidNotProduceFrame(const viz::BeginFrameId& id, + FrameSkippedReason skip_reason); void DidReceiveCompositorFrameAck(); void DidPresentCompositorFrame(uint32_t frame_token, const viz::FrameTimingDetails& details); diff --git a/chromium/cc/metrics/event_metrics.cc b/chromium/cc/metrics/event_metrics.cc index f7f03b91810..326eaee70a1 100644 --- a/chromium/cc/metrics/event_metrics.cc +++ b/chromium/cc/metrics/event_metrics.cc @@ -4,24 +4,18 @@ #include "cc/metrics/event_metrics.h" -#include <tuple> - +#include "base/check.h" +#include "base/memory/ptr_util.h" +#include "base/notreached.h" #include "base/stl_util.h" namespace cc { -EventMetrics::EventMetrics(ui::EventType type, - base::TimeTicks time_stamp, - base::Optional<ScrollInputType> scroll_input_type) - : type_(type), - time_stamp_(time_stamp), - scroll_input_type_(scroll_input_type) {} - -EventMetrics::EventMetrics(const EventMetrics&) = default; -EventMetrics& EventMetrics::operator=(const EventMetrics&) = default; - -bool EventMetrics::IsWhitelisted() const { - switch (type_) { +std::unique_ptr<EventMetrics> EventMetrics::Create( + ui::EventType type, + base::TimeTicks time_stamp, + base::Optional<ui::ScrollInputType> scroll_input_type) { + switch (type) { case ui::ET_MOUSE_PRESSED: case ui::ET_MOUSE_RELEASED: case ui::ET_MOUSEWHEEL: @@ -33,16 +27,25 @@ bool EventMetrics::IsWhitelisted() const { case ui::ET_GESTURE_SCROLL_BEGIN: case ui::ET_GESTURE_SCROLL_UPDATE: case ui::ET_GESTURE_SCROLL_END: - return true; + return base::WrapUnique( + new EventMetrics(type, time_stamp, scroll_input_type)); default: - return false; + return nullptr; } } -const char* EventMetrics::GetTypeName() const { - DCHECK(IsWhitelisted()) << "Event type is not whitelisted for event metrics: " - << type_; +EventMetrics::EventMetrics( + ui::EventType type, + base::TimeTicks time_stamp, + base::Optional<ui::ScrollInputType> scroll_input_type) + : type_(type), + time_stamp_(time_stamp), + scroll_input_type_(scroll_input_type) {} +EventMetrics::EventMetrics(const EventMetrics&) = default; +EventMetrics& EventMetrics::operator=(const EventMetrics&) = default; + +const char* EventMetrics::GetTypeName() const { switch (type_) { case ui::ET_MOUSE_PRESSED: return "MousePressed"; @@ -51,6 +54,9 @@ const char* EventMetrics::GetTypeName() const { case ui::ET_MOUSEWHEEL: return "MouseWheel"; case ui::ET_KEY_PRESSED: + // TODO(crbug/1071645): Currently, all ET_KEY_PRESSED events are reported + // under EventLatency.KeyPressed histogram. This includes both key-down + // and key-char events. Consider reporting them separately. return "KeyPressed"; case ui::ET_KEY_RELEASED: return "KeyReleased"; @@ -73,18 +79,16 @@ const char* EventMetrics::GetTypeName() const { } const char* EventMetrics::GetScrollTypeName() const { - DCHECK(IsWhitelisted()) << "Event type is not whitelisted for event metrics: " - << type_; DCHECK(scroll_input_type_) << "Event is not a scroll event"; switch (*scroll_input_type_) { - case ScrollInputType::kTouchscreen: + case ui::ScrollInputType::kTouchscreen: return "Touchscreen"; - case ScrollInputType::kWheel: + case ui::ScrollInputType::kWheel: return "Wheel"; - case ScrollInputType::kAutoscroll: + case ui::ScrollInputType::kAutoscroll: return "Autoscroll"; - case ScrollInputType::kScrollbar: + case ui::ScrollInputType::kScrollbar: return "Scrollbar"; } } diff --git a/chromium/cc/metrics/event_metrics.h b/chromium/cc/metrics/event_metrics.h index 3f6b9d3cb20..8de2fc58be9 100644 --- a/chromium/cc/metrics/event_metrics.h +++ b/chromium/cc/metrics/event_metrics.h @@ -5,11 +5,13 @@ #ifndef CC_METRICS_EVENT_METRICS_H_ #define CC_METRICS_EVENT_METRICS_H_ +#include <memory> + #include "base/optional.h" #include "base/time/time.h" #include "cc/cc_export.h" -#include "cc/input/scroll_input_type.h" #include "ui/events/types/event_type.h" +#include "ui/events/types/scroll_input_type.h" namespace cc { @@ -17,17 +19,16 @@ namespace cc { // latency metrics. class CC_EXPORT EventMetrics { public: - EventMetrics(ui::EventType type, - base::TimeTicks time_stamp, - base::Optional<ScrollInputType> scroll_input_type); + // Returns a new instance if |type| is a whitelisted event type. Otherwise, + // returns nullptr. + static std::unique_ptr<EventMetrics> Create( + ui::EventType type, + base::TimeTicks time_stamp, + base::Optional<ui::ScrollInputType> scroll_input_type); EventMetrics(const EventMetrics&); EventMetrics& operator=(const EventMetrics&); - // Determines if the event is whitelisted for event latency reporting. If not, - // this event is ignored in histogram reporting. - bool IsWhitelisted() const; - // Returns a string representing event type. Should only be called for // whitelisted event types. const char* GetTypeName() const; @@ -40,7 +41,7 @@ class CC_EXPORT EventMetrics { base::TimeTicks time_stamp() const { return time_stamp_; } - const base::Optional<ScrollInputType>& scroll_input_type() const { + const base::Optional<ui::ScrollInputType>& scroll_input_type() const { return scroll_input_type_; } @@ -48,12 +49,16 @@ class CC_EXPORT EventMetrics { bool operator==(const EventMetrics& other) const; private: + EventMetrics(ui::EventType type, + base::TimeTicks time_stamp, + base::Optional<ui::ScrollInputType> scroll_input_type); + ui::EventType type_; base::TimeTicks time_stamp_; // Only available for scroll events and represents the type of input device // for the event. - base::Optional<ScrollInputType> scroll_input_type_; + base::Optional<ui::ScrollInputType> scroll_input_type_; }; // Struct storing event metrics from both main and impl threads. diff --git a/chromium/cc/metrics/events_metrics_manager.cc b/chromium/cc/metrics/events_metrics_manager.cc index cd008372af9..b4481d32b20 100644 --- a/chromium/cc/metrics/events_metrics_manager.cc +++ b/chromium/cc/metrics/events_metrics_manager.cc @@ -4,9 +4,11 @@ #include "cc/metrics/events_metrics_manager.h" -#include <memory> #include <utility> +#include "base/bind.h" +#include "base/callback.h" +#include "base/callback_helpers.h" #include "base/stl_util.h" namespace cc { @@ -14,15 +16,11 @@ namespace { class ScopedMonitorImpl : public EventsMetricsManager::ScopedMonitor { public: - explicit ScopedMonitorImpl(base::Optional<EventMetrics>* active_event) - : active_event_(active_event) { - DCHECK(active_event_); - } - - ~ScopedMonitorImpl() override { *active_event_ = base::nullopt; } + explicit ScopedMonitorImpl(base::OnceClosure done_callback) + : closure_runner_(std::move(done_callback)) {} private: - base::Optional<EventMetrics>* active_event_; + base::ScopedClosureRunner closure_runner_; }; } // namespace @@ -33,18 +31,20 @@ EventsMetricsManager::EventsMetricsManager() = default; EventsMetricsManager::~EventsMetricsManager() = default; std::unique_ptr<EventsMetricsManager::ScopedMonitor> -EventsMetricsManager::GetScopedMonitor(const EventMetrics& event_metrics) { +EventsMetricsManager::GetScopedMonitor( + std::unique_ptr<EventMetrics> event_metrics) { DCHECK(!active_event_); - if (!event_metrics.IsWhitelisted()) + if (!event_metrics) return nullptr; - active_event_ = event_metrics; - return std::make_unique<ScopedMonitorImpl>(&active_event_); + active_event_ = std::move(event_metrics); + return std::make_unique<ScopedMonitorImpl>(base::BindOnce( + &EventsMetricsManager::OnScopedMonitorEnded, weak_factory_.GetWeakPtr())); } void EventsMetricsManager::SaveActiveEventMetrics() { if (active_event_) { saved_events_.push_back(*active_event_); - active_event_ = base::nullopt; + active_event_.reset(); } } @@ -54,4 +54,8 @@ std::vector<EventMetrics> EventsMetricsManager::TakeSavedEventsMetrics() { return result; } +void EventsMetricsManager::OnScopedMonitorEnded() { + active_event_.reset(); +} + } // namespace cc diff --git a/chromium/cc/metrics/events_metrics_manager.h b/chromium/cc/metrics/events_metrics_manager.h index 8dd3bbfb9bd..6c44f62d212 100644 --- a/chromium/cc/metrics/events_metrics_manager.h +++ b/chromium/cc/metrics/events_metrics_manager.h @@ -8,7 +8,7 @@ #include <memory> #include <vector> -#include "base/optional.h" +#include "base/memory/weak_ptr.h" #include "base/time/time.h" #include "cc/cc_export.h" #include "cc/metrics/event_metrics.h" @@ -40,9 +40,10 @@ class CC_EXPORT EventsMetricsManager { EventsMetricsManager& operator=(const EventsMetricsManager&) = delete; // Called by clients when they start handling an event. Destruction of the - // scoped monitor indicates the end of event handling. + // scoped monitor indicates the end of event handling. |event_metrics| is + // allowed to be nullptr in which case the return value would also be nullptr. std::unique_ptr<ScopedMonitor> GetScopedMonitor( - const EventMetrics& event_metrics); + std::unique_ptr<EventMetrics> event_metrics); // Called by clients when a frame needs to be produced. If any scoped monitor // is active at this time, its corresponding event metrics would be saved. @@ -53,11 +54,15 @@ class CC_EXPORT EventsMetricsManager { std::vector<EventMetrics> TakeSavedEventsMetrics(); private: + void OnScopedMonitorEnded(); + // Current active EventMetrics, if any. - base::Optional<EventMetrics> active_event_; + std::unique_ptr<EventMetrics> active_event_; // List of saved event metrics. std::vector<EventMetrics> saved_events_; + + base::WeakPtrFactory<EventsMetricsManager> weak_factory_{this}; }; } // namespace cc diff --git a/chromium/cc/metrics/events_metrics_manager_unittest.cc b/chromium/cc/metrics/events_metrics_manager_unittest.cc index d2f38aa3abf..ba162cc3283 100644 --- a/chromium/cc/metrics/events_metrics_manager_unittest.cc +++ b/chromium/cc/metrics/events_metrics_manager_unittest.cc @@ -7,11 +7,11 @@ #include <utility> #include <vector> -#include "cc/input/scroll_input_type.h" #include "cc/metrics/event_metrics.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/events/types/event_type.h" +#include "ui/events/types/scroll_input_type.h" namespace cc { namespace { @@ -39,42 +39,43 @@ TEST_F(EventsMetricsManagerTest, EventsMetricsSaved) { kSaveOutsideScope, }; - std::vector<std::pair<EventMetrics, Behavior>> events = { + std::pair<std::unique_ptr<EventMetrics>, Behavior> events[] = { // A whitelisted event type for which SaveActiveEventMetrics() is not // called. - {{ui::ET_MOUSE_PRESSED, TimeAtMs(0), base::nullopt}, + {EventMetrics::Create(ui::ET_MOUSE_PRESSED, TimeAtMs(0), base::nullopt), Behavior::kDoNotSave}, // A whitelisted event type for which SaveActiveEventMetrics() is called // inside its monitor scope. - {{ui::ET_MOUSE_PRESSED, TimeAtMs(1), base::nullopt}, + {EventMetrics::Create(ui::ET_MOUSE_PRESSED, TimeAtMs(1), base::nullopt), Behavior::kSaveInsideScope}, // A whitelisted event type for which SaveActiveEventMetrics() is called - // after its monitor scope is finished. - {{ui::ET_MOUSE_PRESSED, TimeAtMs(2), base::nullopt}, + // after + // its monitor scope is finished. + {EventMetrics::Create(ui::ET_MOUSE_PRESSED, TimeAtMs(2), base::nullopt), Behavior::kSaveOutsideScope}, // A non-whitelisted event type for which SaveActiveEventMetrics() is // called inside its monitor scope. - {{ui::ET_MOUSE_MOVED, TimeAtMs(3), base::nullopt}, + {EventMetrics::Create(ui::ET_MOUSE_MOVED, TimeAtMs(3), base::nullopt), Behavior::kSaveInsideScope}, }; - EXPECT_TRUE(events[0].first.IsWhitelisted()); - EXPECT_TRUE(events[1].first.IsWhitelisted()); - EXPECT_TRUE(events[2].first.IsWhitelisted()); - EXPECT_FALSE(events[3].first.IsWhitelisted()); + EXPECT_NE(events[0].first, nullptr); + EXPECT_NE(events[1].first, nullptr); + EXPECT_NE(events[2].first, nullptr); + EXPECT_EQ(events[3].first, nullptr); // Out of the above events, only those with a whitelisted event type, for // which SaveActiveEventMetrics() is called inside its monitor scope, are // expected to be saved. std::vector<EventMetrics> expected_saved_events = { - events[1].first, + *events[1].first, }; for (auto& event : events) { { - auto monitor = manager_.GetScopedMonitor(event.first); + auto monitor = manager_.GetScopedMonitor(std::move(event.first)); if (event.second == Behavior::kSaveInsideScope) manager_.SaveActiveEventMetrics(); // Ending the scope destroys the |monitor|. diff --git a/chromium/cc/metrics/frame_sequence_metrics.cc b/chromium/cc/metrics/frame_sequence_metrics.cc new file mode 100644 index 00000000000..0cefe55fd51 --- /dev/null +++ b/chromium/cc/metrics/frame_sequence_metrics.cc @@ -0,0 +1,371 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/metrics/frame_sequence_metrics.h" + +#include "base/metrics/histogram.h" +#include "base/metrics/histogram_macros.h" +#include "base/strings/strcat.h" +#include "base/trace_event/trace_event.h" +#include "base/trace_event/traced_value.h" +#include "cc/metrics/frame_sequence_tracker.h" +#include "cc/metrics/throughput_ukm_reporter.h" + +namespace cc { + +namespace { + +// Avoid reporting any throughput metric for sequences that do not have a +// sufficient number of frames. +constexpr int kMinFramesForThroughputMetric = 100; + +constexpr int kBuiltinSequenceNum = + static_cast<int>(FrameSequenceTrackerType::kMaxType) + 1; +constexpr int kMaximumHistogramIndex = 3 * kBuiltinSequenceNum; + +int GetIndexForMetric(FrameSequenceMetrics::ThreadType thread_type, + FrameSequenceTrackerType type) { + if (thread_type == FrameSequenceMetrics::ThreadType::kMain) + return static_cast<int>(type); + if (thread_type == FrameSequenceMetrics::ThreadType::kCompositor) + return static_cast<int>(type) + kBuiltinSequenceNum; + return static_cast<int>(type) + 2 * kBuiltinSequenceNum; +} + +std::string GetCheckerboardingHistogramName(FrameSequenceTrackerType type) { + return base::StrCat( + {"Graphics.Smoothness.Checkerboarding.", + FrameSequenceTracker::GetFrameSequenceTrackerTypeName(type)}); +} + +std::string GetThroughputHistogramName(FrameSequenceTrackerType type, + const char* thread_name) { + return base::StrCat( + {"Graphics.Smoothness.PercentDroppedFrames.", thread_name, ".", + FrameSequenceTracker::GetFrameSequenceTrackerTypeName(type)}); +} + +std::string GetFrameSequenceLengthHistogramName(FrameSequenceTrackerType type) { + return base::StrCat( + {"Graphics.Smoothness.FrameSequenceLength.", + FrameSequenceTracker::GetFrameSequenceTrackerTypeName(type)}); +} + +bool ShouldReportForAnimation(FrameSequenceTrackerType sequence_type, + FrameSequenceMetrics::ThreadType thread_type) { + if (sequence_type == FrameSequenceTrackerType::kCompositorAnimation) + return thread_type == FrameSequenceMetrics::ThreadType::kCompositor; + + if (sequence_type == FrameSequenceTrackerType::kMainThreadAnimation || + sequence_type == FrameSequenceTrackerType::kRAF) + return thread_type == FrameSequenceMetrics::ThreadType::kMain; + + return false; +} + +bool ShouldReportForInteraction(FrameSequenceMetrics* metrics, + FrameSequenceMetrics::ThreadType thread_type) { + const auto sequence_type = metrics->type(); + + // For touch/wheel scroll, the slower thread is the one we want to report. For + // pinch-zoom, it's the compositor-thread. + if (sequence_type == FrameSequenceTrackerType::kTouchScroll || + sequence_type == FrameSequenceTrackerType::kWheelScroll) + return thread_type == metrics->GetEffectiveThread(); + + if (sequence_type == FrameSequenceTrackerType::kPinchZoom) + return thread_type == FrameSequenceMetrics::ThreadType::kCompositor; + + return false; +} + +bool IsInteractionType(FrameSequenceTrackerType sequence_type) { + return sequence_type == FrameSequenceTrackerType::kTouchScroll || + sequence_type == FrameSequenceTrackerType::kWheelScroll || + sequence_type == FrameSequenceTrackerType::kPinchZoom; +} + +} // namespace + +FrameSequenceMetrics::FrameSequenceMetrics(FrameSequenceTrackerType type, + ThroughputUkmReporter* ukm_reporter) + : type_(type), throughput_ukm_reporter_(ukm_reporter) { + TRACE_EVENT_NESTABLE_ASYNC_BEGIN1( + "cc,benchmark", "FrameSequenceTracker", TRACE_ID_LOCAL(this), "name", + FrameSequenceTracker::GetFrameSequenceTrackerTypeName(type_)); +} + +FrameSequenceMetrics::~FrameSequenceMetrics() { + if (HasDataLeftForReporting()) { + ReportMetrics(); + } +} + +void FrameSequenceMetrics::SetScrollingThread(ThreadType scrolling_thread) { + DCHECK(type_ == FrameSequenceTrackerType::kTouchScroll || + type_ == FrameSequenceTrackerType::kWheelScroll || + type_ == FrameSequenceTrackerType::kScrollbarScroll); + DCHECK_EQ(scrolling_thread_, ThreadType::kUnknown); + scrolling_thread_ = scrolling_thread; +} + +void FrameSequenceMetrics::SetCustomReporter(CustomReporter custom_reporter) { + DCHECK_EQ(FrameSequenceTrackerType::kCustom, type_); + custom_reporter_ = std::move(custom_reporter); +} + +FrameSequenceMetrics::ThreadType FrameSequenceMetrics::GetEffectiveThread() + const { + switch (type_) { + case FrameSequenceTrackerType::kCompositorAnimation: + case FrameSequenceTrackerType::kPinchZoom: + return ThreadType::kCompositor; + + case FrameSequenceTrackerType::kMainThreadAnimation: + case FrameSequenceTrackerType::kRAF: + case FrameSequenceTrackerType::kVideo: + return ThreadType::kMain; + + case FrameSequenceTrackerType::kTouchScroll: + case FrameSequenceTrackerType::kScrollbarScroll: + case FrameSequenceTrackerType::kWheelScroll: + return scrolling_thread_; + + case FrameSequenceTrackerType::kUniversal: + return ThreadType::kSlower; + + case FrameSequenceTrackerType::kCustom: + case FrameSequenceTrackerType::kMaxType: + NOTREACHED(); + } + return ThreadType::kUnknown; +} + +void FrameSequenceMetrics::Merge( + std::unique_ptr<FrameSequenceMetrics> metrics) { + DCHECK_EQ(type_, metrics->type_); + DCHECK_EQ(GetEffectiveThread(), metrics->GetEffectiveThread()); + impl_throughput_.Merge(metrics->impl_throughput_); + main_throughput_.Merge(metrics->main_throughput_); + aggregated_throughput_.Merge(metrics->aggregated_throughput_); + frames_checkerboarded_ += metrics->frames_checkerboarded_; + + // Reset the state of |metrics| before destroying it, so that it doesn't end + // up reporting the metrics. + metrics->impl_throughput_ = {}; + metrics->main_throughput_ = {}; + metrics->aggregated_throughput_ = {}; + metrics->frames_checkerboarded_ = 0; +} + +bool FrameSequenceMetrics::HasEnoughDataForReporting() const { + return impl_throughput_.frames_expected >= kMinFramesForThroughputMetric || + main_throughput_.frames_expected >= kMinFramesForThroughputMetric; +} + +bool FrameSequenceMetrics::HasDataLeftForReporting() const { + return impl_throughput_.frames_expected > 0 || + main_throughput_.frames_expected > 0; +} + +void FrameSequenceMetrics::ComputeAggregatedThroughput() { + // Whenever we are expecting and producing main frames, we are expecting and + // producing impl frames as well. As an example, if we expect one main frame + // to be produced, and when that main frame is presented, we are expecting 3 + // impl frames, then the number of expected frames is 3 for the aggregated + // throughput. + aggregated_throughput_.frames_expected = impl_throughput_.frames_expected; + DCHECK_LE(aggregated_throughput_.frames_produced, + aggregated_throughput_.frames_expected); +} + +void FrameSequenceMetrics::ReportMetrics() { + DCHECK_LE(impl_throughput_.frames_produced, impl_throughput_.frames_expected); + DCHECK_LE(main_throughput_.frames_produced, main_throughput_.frames_expected); + TRACE_EVENT_NESTABLE_ASYNC_END2( + "cc,benchmark", "FrameSequenceTracker", TRACE_ID_LOCAL(this), "args", + ThroughputData::ToTracedValue(impl_throughput_, main_throughput_), + "checkerboard", frames_checkerboarded_); + + if (type_ == FrameSequenceTrackerType::kCustom) { + DCHECK(!custom_reporter_.is_null()); + std::move(custom_reporter_).Run(std::move(main_throughput_)); + + main_throughput_ = {}; + impl_throughput_ = {}; + aggregated_throughput_ = {}; + frames_checkerboarded_ = 0; + return; + } + + ComputeAggregatedThroughput(); + + // Report the throughput metrics. + base::Optional<int> impl_throughput_percent = ThroughputData::ReportHistogram( + this, ThreadType::kCompositor, + GetIndexForMetric(FrameSequenceMetrics::ThreadType::kCompositor, type_), + impl_throughput_); + base::Optional<int> main_throughput_percent = ThroughputData::ReportHistogram( + this, ThreadType::kMain, + GetIndexForMetric(FrameSequenceMetrics::ThreadType::kMain, type_), + main_throughput_); + + // Report for the 'slower thread' for the metrics where it makes sense. + bool should_report_slower_thread = + IsInteractionType(type_) || type_ == FrameSequenceTrackerType::kUniversal; + base::Optional<int> aggregated_throughput_percent; + if (should_report_slower_thread) { + aggregated_throughput_percent = ThroughputData::ReportHistogram( + this, ThreadType::kSlower, + GetIndexForMetric(FrameSequenceMetrics::ThreadType::kSlower, type_), + aggregated_throughput_); + if (aggregated_throughput_percent.has_value() && throughput_ukm_reporter_) { + throughput_ukm_reporter_->ReportThroughputUkm( + aggregated_throughput_percent, impl_throughput_percent, + main_throughput_percent, type_); + } + } + + // Report for the 'scrolling thread' for the scrolling interactions. + if (scrolling_thread_ != ThreadType::kUnknown) { + base::Optional<int> scrolling_thread_throughput; + switch (scrolling_thread_) { + case ThreadType::kCompositor: + scrolling_thread_throughput = impl_throughput_percent; + break; + case ThreadType::kMain: + scrolling_thread_throughput = main_throughput_percent; + break; + case ThreadType::kSlower: + case ThreadType::kUnknown: + NOTREACHED(); + break; + } + if (scrolling_thread_throughput.has_value()) { + // It's OK to use the UMA histogram in the following code while still + // using |GetThroughputHistogramName()| to get the name of the metric, + // since the input-params to the function never change at runtime. + if (type_ == FrameSequenceTrackerType::kWheelScroll) { + UMA_HISTOGRAM_PERCENTAGE( + GetThroughputHistogramName(FrameSequenceTrackerType::kWheelScroll, + "ScrollingThread"), + scrolling_thread_throughput.value()); + } else if (type_ == FrameSequenceTrackerType::kTouchScroll) { + UMA_HISTOGRAM_PERCENTAGE( + GetThroughputHistogramName(FrameSequenceTrackerType::kTouchScroll, + "ScrollingThread"), + scrolling_thread_throughput.value()); + } else { + DCHECK_EQ(type_, FrameSequenceTrackerType::kScrollbarScroll); + UMA_HISTOGRAM_PERCENTAGE( + GetThroughputHistogramName( + FrameSequenceTrackerType::kScrollbarScroll, "ScrollingThread"), + scrolling_thread_throughput.value()); + } + } + } + + // Report the checkerboarding metrics. + if (impl_throughput_.frames_expected >= kMinFramesForThroughputMetric) { + const int checkerboarding_percent = static_cast<int>( + 100 * frames_checkerboarded_ / impl_throughput_.frames_expected); + STATIC_HISTOGRAM_POINTER_GROUP( + GetCheckerboardingHistogramName(type_), static_cast<int>(type_), + static_cast<int>(FrameSequenceTrackerType::kMaxType), + Add(checkerboarding_percent), + base::LinearHistogram::FactoryGet( + GetCheckerboardingHistogramName(type_), 1, 100, 101, + base::HistogramBase::kUmaTargetedHistogramFlag)); + frames_checkerboarded_ = 0; + } + + // Reset the metrics that reach reporting threshold. + if (impl_throughput_.frames_expected >= kMinFramesForThroughputMetric) { + impl_throughput_ = {}; + aggregated_throughput_ = {}; + } + if (main_throughput_.frames_expected >= kMinFramesForThroughputMetric) + main_throughput_ = {}; +} + +base::Optional<int> FrameSequenceMetrics::ThroughputData::ReportHistogram( + FrameSequenceMetrics* metrics, + ThreadType thread_type, + int metric_index, + const ThroughputData& data) { + const auto sequence_type = metrics->type(); + DCHECK_LT(sequence_type, FrameSequenceTrackerType::kMaxType); + + STATIC_HISTOGRAM_POINTER_GROUP( + GetFrameSequenceLengthHistogramName(sequence_type), + static_cast<int>(sequence_type), + static_cast<int>(FrameSequenceTrackerType::kMaxType), + Add(data.frames_expected), + base::Histogram::FactoryGet( + GetFrameSequenceLengthHistogramName(sequence_type), 1, 1000, 50, + base::HistogramBase::kUmaTargetedHistogramFlag)); + + if (data.frames_expected < kMinFramesForThroughputMetric) + return base::nullopt; + + // Throughput means the percent of frames that was expected to show on the + // screen but didn't. In other words, the lower the throughput is, the + // smoother user experience. + const int percent = + std::ceil(100 * (data.frames_expected - data.frames_produced) / + static_cast<double>(data.frames_expected)); + + const bool is_animation = + ShouldReportForAnimation(sequence_type, thread_type); + const bool is_interaction = ShouldReportForInteraction(metrics, thread_type); + + ThroughputUkmReporter* const ukm_reporter = metrics->ukm_reporter(); + + if (is_animation) { + UMA_HISTOGRAM_PERCENTAGE( + "Graphics.Smoothness.PercentDroppedFrames.AllAnimations", percent); + if (ukm_reporter) { + ukm_reporter->ReportAggregateThroughput(AggregationType::kAllAnimations, + percent); + } + } + + if (is_interaction) { + UMA_HISTOGRAM_PERCENTAGE( + "Graphics.Smoothness.PercentDroppedFrames.AllInteractions", percent); + if (ukm_reporter) { + ukm_reporter->ReportAggregateThroughput(AggregationType::kAllInteractions, + percent); + } + } + + if (is_animation || is_interaction) { + UMA_HISTOGRAM_PERCENTAGE( + "Graphics.Smoothness.PercentDroppedFrames.AllSequences", percent); + if (ukm_reporter) { + ukm_reporter->ReportAggregateThroughput(AggregationType::kAllSequences, + percent); + } + } + + if (!is_animation && !IsInteractionType(sequence_type) && + sequence_type != FrameSequenceTrackerType::kUniversal && + sequence_type != FrameSequenceTrackerType::kVideo) { + return base::nullopt; + } + + const char* thread_name = + thread_type == ThreadType::kCompositor + ? "CompositorThread" + : thread_type == ThreadType::kMain ? "MainThread" : "SlowerThread"; + STATIC_HISTOGRAM_POINTER_GROUP( + GetThroughputHistogramName(sequence_type, thread_name), metric_index, + kMaximumHistogramIndex, Add(percent), + base::LinearHistogram::FactoryGet( + GetThroughputHistogramName(sequence_type, thread_name), 1, 100, 101, + base::HistogramBase::kUmaTargetedHistogramFlag)); + return percent; +} + +} // namespace cc diff --git a/chromium/cc/metrics/frame_sequence_metrics.h b/chromium/cc/metrics/frame_sequence_metrics.h new file mode 100644 index 00000000000..0417899c3a9 --- /dev/null +++ b/chromium/cc/metrics/frame_sequence_metrics.h @@ -0,0 +1,140 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CC_METRICS_FRAME_SEQUENCE_METRICS_H_ +#define CC_METRICS_FRAME_SEQUENCE_METRICS_H_ + +#include "base/callback.h" +#include "base/optional.h" +#include "base/trace_event/traced_value.h" +#include "cc/cc_export.h" + +namespace cc { +class ThroughputUkmReporter; + +enum class FrameSequenceTrackerType { + // Used as an enum for metrics. DO NOT reorder or delete values. Rather, + // add them at the end and increment kMaxType. + kCompositorAnimation = 0, + kMainThreadAnimation = 1, + kPinchZoom = 2, + kRAF = 3, + kTouchScroll = 4, + kUniversal = 5, + kVideo = 6, + kWheelScroll = 7, + kScrollbarScroll = 8, + kCustom = 9, // Note that the metrics for kCustom are not reported on UMA, + // and instead are dispatched back to the LayerTreeHostClient. + kMaxType +}; + +class CC_EXPORT FrameSequenceMetrics { + public: + FrameSequenceMetrics(FrameSequenceTrackerType type, + ThroughputUkmReporter* ukm_reporter); + ~FrameSequenceMetrics(); + + FrameSequenceMetrics(const FrameSequenceMetrics&) = delete; + FrameSequenceMetrics& operator=(const FrameSequenceMetrics&) = delete; + + enum class ThreadType { kMain, kCompositor, kSlower, kUnknown }; + + struct ThroughputData { + static std::unique_ptr<base::trace_event::TracedValue> ToTracedValue( + const ThroughputData& impl, + const ThroughputData& main); + + // Returns the throughput in percent, a return value of base::nullopt + // indicates that no throughput metric is reported. + static base::Optional<int> ReportHistogram(FrameSequenceMetrics* metrics, + ThreadType thread_type, + int metric_index, + const ThroughputData& data); + + void Merge(const ThroughputData& data) { + frames_expected += data.frames_expected; + frames_produced += data.frames_produced; +#if DCHECK_IS_ON() + frames_processed += data.frames_processed; + frames_received += data.frames_received; +#endif + } + + // Tracks the number of frames that were expected to be shown during this + // frame-sequence. + uint32_t frames_expected = 0; + + // Tracks the number of frames that were actually presented to the user + // during this frame-sequence. + uint32_t frames_produced = 0; + +#if DCHECK_IS_ON() + // Tracks the number of frames that is either submitted or reported as no + // damage. + uint32_t frames_processed = 0; + + // Tracks the number of begin-frames that are received. + uint32_t frames_received = 0; +#endif + }; + + void SetScrollingThread(ThreadType thread); + + using CustomReporter = + base::OnceCallback<void(ThroughputData throughput_data)>; + // Sets reporter callback for kCustom typed sequence. + void SetCustomReporter(CustomReporter custom_reporter); + + // Returns the 'effective thread' for the metrics (i.e. the thread most + // relevant for this metric). + ThreadType GetEffectiveThread() const; + + void Merge(std::unique_ptr<FrameSequenceMetrics> metrics); + bool HasEnoughDataForReporting() const; + bool HasDataLeftForReporting() const; + // Report related metrics: throughput, checkboarding... + void ReportMetrics(); + void ComputeAggregatedThroughputForTesting() { + ComputeAggregatedThroughput(); + } + + ThroughputData& impl_throughput() { return impl_throughput_; } + ThroughputData& main_throughput() { return main_throughput_; } + ThroughputData& aggregated_throughput() { return aggregated_throughput_; } + void add_checkerboarded_frames(int64_t frames) { + frames_checkerboarded_ += frames; + } + uint32_t frames_checkerboarded() const { return frames_checkerboarded_; } + + FrameSequenceTrackerType type() const { return type_; } + ThroughputUkmReporter* ukm_reporter() const { + return throughput_ukm_reporter_; + } + + private: + void ComputeAggregatedThroughput(); + const FrameSequenceTrackerType type_; + + // Pointer to the reporter owned by the FrameSequenceTrackerCollection. + ThroughputUkmReporter* const throughput_ukm_reporter_; + + ThroughputData impl_throughput_; + ThroughputData main_throughput_; + // The aggregated throughput for the main/compositor thread. + ThroughputData aggregated_throughput_; + + ThreadType scrolling_thread_ = ThreadType::kUnknown; + + // Tracks the number of produced frames that had some amount of + // checkerboarding, and how many frames showed such checkerboarded frames. + uint32_t frames_checkerboarded_ = 0; + + // Callback invoked to report metrics for kCustom typed sequence. + CustomReporter custom_reporter_; +}; + +} // namespace cc + +#endif // CC_METRICS_FRAME_SEQUENCE_METRICS_H_ diff --git a/chromium/cc/metrics/frame_sequence_metrics_unittest.cc b/chromium/cc/metrics/frame_sequence_metrics_unittest.cc index b52a81020ae..ae43da19a24 100644 --- a/chromium/cc/metrics/frame_sequence_metrics_unittest.cc +++ b/chromium/cc/metrics/frame_sequence_metrics_unittest.cc @@ -23,6 +23,18 @@ TEST(FrameSequenceMetricsTest, AggregatedThroughput) { EXPECT_EQ(first.aggregated_throughput().frames_expected, 200u); } +TEST(FrameSequenceMetricsTest, AggregatedThroughputClearedAfterReport) { + FrameSequenceMetrics first(FrameSequenceTrackerType::kCompositorAnimation, + nullptr); + first.impl_throughput().frames_expected = 200u; + first.impl_throughput().frames_produced = 190u; + first.aggregated_throughput().frames_produced = 150u; + + first.ReportMetrics(); + EXPECT_EQ(first.aggregated_throughput().frames_expected, 0u); + EXPECT_EQ(first.aggregated_throughput().frames_produced, 0u); +} + TEST(FrameSequenceMetricsTest, MergeMetrics) { // Create a metric with only a small number of frames. It shouldn't report any // metrics. diff --git a/chromium/cc/metrics/frame_sequence_tracker.cc b/chromium/cc/metrics/frame_sequence_tracker.cc index 0b1dbc1f38e..f1a1fd9b995 100644 --- a/chromium/cc/metrics/frame_sequence_tracker.cc +++ b/chromium/cc/metrics/frame_sequence_tracker.cc @@ -4,16 +4,11 @@ #include "cc/metrics/frame_sequence_tracker.h" -#include "base/memory/ptr_util.h" +#include "base/bind.h" #include "base/metrics/histogram.h" #include "base/metrics/histogram_macros.h" -#include "base/stl_util.h" -#include "base/strings/strcat.h" #include "base/trace_event/trace_event.h" #include "base/trace_event/traced_value.h" -#include "cc/metrics/compositor_frame_reporting_controller.h" -#include "cc/metrics/throughput_ukm_reporter.h" -#include "cc/trees/ukm_manager.h" #include "components/viz/common/frame_sinks/begin_frame_args.h" #include "components/viz/common/quads/compositor_frame_metadata.h" #include "ui/gfx/presentation_feedback.h" @@ -22,7 +17,7 @@ #if DCHECK_IS_ON() #define TRACKER_TRACE_STREAM frame_sequence_trace_ #define TRACKER_DCHECK_MSG \ - " in " << GetFrameSequenceTrackerTypeName(this->type_) \ + " in " << GetFrameSequenceTrackerTypeName(this->type()) \ << " tracker: " << frame_sequence_trace_.str() << " (" \ << frame_sequence_trace_.str().size() << ")"; #else @@ -32,6 +27,10 @@ namespace cc { +// In the |TRACKER_TRACE_STREAM|, we mod the numbers such as frame sequence +// number, or frame token, such that the debug string is not too long. +constexpr int kDebugStrMod = 1000; + const char* FrameSequenceTracker::GetFrameSequenceTrackerTypeName( FrameSequenceTrackerType type) { switch (type) { @@ -60,574 +59,27 @@ const char* FrameSequenceTracker::GetFrameSequenceTrackerTypeName( } } -namespace { - -// Avoid reporting any throughput metric for sequences that do not have a -// sufficient number of frames. -constexpr int kMinFramesForThroughputMetric = 100; - -constexpr int kBuiltinSequenceNum = - static_cast<int>(FrameSequenceTrackerType::kMaxType) + 1; -constexpr int kMaximumHistogramIndex = 3 * kBuiltinSequenceNum; - -int GetIndexForMetric(FrameSequenceMetrics::ThreadType thread_type, - FrameSequenceTrackerType type) { - if (thread_type == FrameSequenceMetrics::ThreadType::kMain) - return static_cast<int>(type); - if (thread_type == FrameSequenceMetrics::ThreadType::kCompositor) - return static_cast<int>(type) + kBuiltinSequenceNum; - return static_cast<int>(type) + 2 * kBuiltinSequenceNum; -} - -std::string GetCheckerboardingHistogramName(FrameSequenceTrackerType type) { - return base::StrCat( - {"Graphics.Smoothness.Checkerboarding.", - FrameSequenceTracker::GetFrameSequenceTrackerTypeName(type)}); -} - -std::string GetThroughputHistogramName(FrameSequenceTrackerType type, - const char* thread_name) { - return base::StrCat( - {"Graphics.Smoothness.PercentDroppedFrames.", thread_name, ".", - FrameSequenceTracker::GetFrameSequenceTrackerTypeName(type)}); -} - -std::string GetFrameSequenceLengthHistogramName(FrameSequenceTrackerType type) { - return base::StrCat( - {"Graphics.Smoothness.FrameSequenceLength.", - FrameSequenceTracker::GetFrameSequenceTrackerTypeName(type)}); -} - -bool ShouldReportForAnimation(FrameSequenceTrackerType sequence_type, - FrameSequenceMetrics::ThreadType thread_type) { - if (sequence_type == FrameSequenceTrackerType::kCompositorAnimation) - return thread_type == FrameSequenceMetrics::ThreadType::kCompositor; - - if (sequence_type == FrameSequenceTrackerType::kMainThreadAnimation || - sequence_type == FrameSequenceTrackerType::kRAF) - return thread_type == FrameSequenceMetrics::ThreadType::kMain; - - return false; -} - -bool ShouldReportForInteraction(FrameSequenceMetrics* metrics, - FrameSequenceMetrics::ThreadType thread_type) { - const auto sequence_type = metrics->type(); - - // For touch/wheel scroll, the slower thread is the one we want to report. For - // pinch-zoom, it's the compositor-thread. - if (sequence_type == FrameSequenceTrackerType::kTouchScroll || - sequence_type == FrameSequenceTrackerType::kWheelScroll) - return thread_type == metrics->GetEffectiveThread(); - - if (sequence_type == FrameSequenceTrackerType::kPinchZoom) - return thread_type == FrameSequenceMetrics::ThreadType::kCompositor; - - return false; -} - -bool IsInteractionType(FrameSequenceTrackerType sequence_type) { - return sequence_type == FrameSequenceTrackerType::kTouchScroll || - sequence_type == FrameSequenceTrackerType::kWheelScroll || - sequence_type == FrameSequenceTrackerType::kPinchZoom; -} - -} // namespace - -//////////////////////////////////////////////////////////////////////////////// -// FrameSequenceMetrics - -FrameSequenceMetrics::FrameSequenceMetrics(FrameSequenceTrackerType type, - ThroughputUkmReporter* ukm_reporter) - : type_(type), - throughput_ukm_reporter_(ukm_reporter) { - TRACE_EVENT_NESTABLE_ASYNC_BEGIN1( - "cc,benchmark", "FrameSequenceTracker", TRACE_ID_LOCAL(this), "name", - FrameSequenceTracker::GetFrameSequenceTrackerTypeName(type_)); -} - -FrameSequenceMetrics::~FrameSequenceMetrics() { - if (HasDataLeftForReporting()) { - ReportMetrics(); - } -} - -void FrameSequenceMetrics::SetScrollingThread(ThreadType scrolling_thread) { - DCHECK(type_ == FrameSequenceTrackerType::kTouchScroll || - type_ == FrameSequenceTrackerType::kWheelScroll || - type_ == FrameSequenceTrackerType::kScrollbarScroll); - DCHECK_EQ(scrolling_thread_, ThreadType::kUnknown); - scrolling_thread_ = scrolling_thread; -} - -FrameSequenceMetrics::ThreadType FrameSequenceMetrics::GetEffectiveThread() - const { - switch (type_) { - case FrameSequenceTrackerType::kCompositorAnimation: - case FrameSequenceTrackerType::kPinchZoom: - return ThreadType::kCompositor; - - case FrameSequenceTrackerType::kMainThreadAnimation: - case FrameSequenceTrackerType::kRAF: - case FrameSequenceTrackerType::kVideo: - return ThreadType::kMain; - - case FrameSequenceTrackerType::kTouchScroll: - case FrameSequenceTrackerType::kScrollbarScroll: - case FrameSequenceTrackerType::kWheelScroll: - return scrolling_thread_; - - case FrameSequenceTrackerType::kUniversal: - return ThreadType::kSlower; - - case FrameSequenceTrackerType::kCustom: - case FrameSequenceTrackerType::kMaxType: - NOTREACHED(); - } - return ThreadType::kUnknown; -} - -void FrameSequenceMetrics::Merge( - std::unique_ptr<FrameSequenceMetrics> metrics) { - DCHECK_EQ(type_, metrics->type_); - DCHECK_EQ(GetEffectiveThread(), metrics->GetEffectiveThread()); - impl_throughput_.Merge(metrics->impl_throughput_); - main_throughput_.Merge(metrics->main_throughput_); - aggregated_throughput_.Merge(metrics->aggregated_throughput_); - frames_checkerboarded_ += metrics->frames_checkerboarded_; - - // Reset the state of |metrics| before destroying it, so that it doesn't end - // up reporting the metrics. - metrics->impl_throughput_ = {}; - metrics->main_throughput_ = {}; - metrics->aggregated_throughput_ = {}; - metrics->frames_checkerboarded_ = 0; -} - -bool FrameSequenceMetrics::HasEnoughDataForReporting() const { - return impl_throughput_.frames_expected >= kMinFramesForThroughputMetric || - main_throughput_.frames_expected >= kMinFramesForThroughputMetric; -} - -bool FrameSequenceMetrics::HasDataLeftForReporting() const { - return impl_throughput_.frames_expected > 0 || - main_throughput_.frames_expected > 0; -} - -void FrameSequenceMetrics::ComputeAggregatedThroughput() { - // Whenever we are expecting and producing main frames, we are expecting and - // producing impl frames as well. As an example, if we expect one main frame - // to be produced, and when that main frame is presented, we are expecting 3 - // impl frames, then the number of expected frames is 3 for the aggregated - // throughput. - aggregated_throughput_.frames_expected = impl_throughput_.frames_expected; - DCHECK_LE(aggregated_throughput_.frames_produced, - aggregated_throughput_.frames_produced); -} - -void FrameSequenceMetrics::ReportMetrics() { - DCHECK_LE(impl_throughput_.frames_produced, impl_throughput_.frames_expected); - DCHECK_LE(main_throughput_.frames_produced, main_throughput_.frames_expected); - TRACE_EVENT_NESTABLE_ASYNC_END2( - "cc,benchmark", "FrameSequenceTracker", TRACE_ID_LOCAL(this), "args", - ThroughputData::ToTracedValue(impl_throughput_, main_throughput_), - "checkerboard", frames_checkerboarded_); - - // Data for kCustom typed tracker is handled by caller instead being - // reported here. - if (type_ == FrameSequenceTrackerType::kCustom) - return; - - ComputeAggregatedThroughput(); - - // Report the throughput metrics. - base::Optional<int> impl_throughput_percent = ThroughputData::ReportHistogram( - this, ThreadType::kCompositor, - GetIndexForMetric(FrameSequenceMetrics::ThreadType::kCompositor, type_), - impl_throughput_); - base::Optional<int> main_throughput_percent = ThroughputData::ReportHistogram( - this, ThreadType::kMain, - GetIndexForMetric(FrameSequenceMetrics::ThreadType::kMain, type_), - main_throughput_); - - // Report for the 'slower thread' for the metrics where it makes sense. - bool should_report_slower_thread = - IsInteractionType(type_) || type_ == FrameSequenceTrackerType::kUniversal; - base::Optional<int> aggregated_throughput_percent; - if (should_report_slower_thread) { - aggregated_throughput_percent = ThroughputData::ReportHistogram( - this, ThreadType::kSlower, - GetIndexForMetric(FrameSequenceMetrics::ThreadType::kSlower, type_), - aggregated_throughput_); - if (aggregated_throughput_percent.has_value() && throughput_ukm_reporter_) { - throughput_ukm_reporter_->ReportThroughputUkm( - aggregated_throughput_percent, impl_throughput_percent, - main_throughput_percent, type_); - } - } - - // Report for the 'scrolling thread' for the scrolling interactions. - if (scrolling_thread_ != ThreadType::kUnknown) { - base::Optional<int> scrolling_thread_throughput; - switch (scrolling_thread_) { - case ThreadType::kCompositor: - scrolling_thread_throughput = impl_throughput_percent; - break; - case ThreadType::kMain: - scrolling_thread_throughput = main_throughput_percent; - break; - case ThreadType::kSlower: - case ThreadType::kUnknown: - NOTREACHED(); - break; - } - if (scrolling_thread_throughput.has_value()) { - // It's OK to use the UMA histogram in the following code while still - // using |GetThroughputHistogramName()| to get the name of the metric, - // since the input-params to the function never change at runtime. - if (type_ == FrameSequenceTrackerType::kWheelScroll) { - UMA_HISTOGRAM_PERCENTAGE( - GetThroughputHistogramName(FrameSequenceTrackerType::kWheelScroll, - "ScrollingThread"), - scrolling_thread_throughput.value()); - } else if (type_ == FrameSequenceTrackerType::kTouchScroll) { - UMA_HISTOGRAM_PERCENTAGE( - GetThroughputHistogramName(FrameSequenceTrackerType::kTouchScroll, - "ScrollingThread"), - scrolling_thread_throughput.value()); - } else { - DCHECK_EQ(type_, FrameSequenceTrackerType::kScrollbarScroll); - UMA_HISTOGRAM_PERCENTAGE( - GetThroughputHistogramName( - FrameSequenceTrackerType::kScrollbarScroll, "ScrollingThread"), - scrolling_thread_throughput.value()); - } - } - } - - // Report the checkerboarding metrics. - if (impl_throughput_.frames_expected >= kMinFramesForThroughputMetric) { - const int checkerboarding_percent = static_cast<int>( - 100 * frames_checkerboarded_ / impl_throughput_.frames_expected); - STATIC_HISTOGRAM_POINTER_GROUP( - GetCheckerboardingHistogramName(type_), static_cast<int>(type_), - static_cast<int>(FrameSequenceTrackerType::kMaxType), - Add(checkerboarding_percent), - base::LinearHistogram::FactoryGet( - GetCheckerboardingHistogramName(type_), 1, 100, 101, - base::HistogramBase::kUmaTargetedHistogramFlag)); - frames_checkerboarded_ = 0; - } - - // Reset the metrics that reach reporting threshold. - if (impl_throughput_.frames_expected >= kMinFramesForThroughputMetric) - impl_throughput_ = {}; - if (main_throughput_.frames_expected >= kMinFramesForThroughputMetric) - main_throughput_ = {}; - if (aggregated_throughput_percent.has_value()) - aggregated_throughput_ = {}; -} - -//////////////////////////////////////////////////////////////////////////////// -// FrameSequenceTrackerCollection - -FrameSequenceTrackerCollection::FrameSequenceTrackerCollection( - bool is_single_threaded, - CompositorFrameReportingController* compositor_frame_reporting_controller) - : is_single_threaded_(is_single_threaded), - compositor_frame_reporting_controller_( - compositor_frame_reporting_controller) {} - -FrameSequenceTrackerCollection::~FrameSequenceTrackerCollection() { - frame_trackers_.clear(); - removal_trackers_.clear(); -} - -FrameSequenceMetrics* FrameSequenceTrackerCollection::StartSequence( - FrameSequenceTrackerType type) { - DCHECK_NE(FrameSequenceTrackerType::kCustom, type); - - if (is_single_threaded_) - return nullptr; - if (frame_trackers_.contains(type)) - return frame_trackers_[type]->metrics(); - auto tracker = base::WrapUnique( - new FrameSequenceTracker(type, throughput_ukm_reporter_.get())); - frame_trackers_[type] = std::move(tracker); - - if (compositor_frame_reporting_controller_) - compositor_frame_reporting_controller_->AddActiveTracker(type); - return frame_trackers_[type]->metrics(); -} - -void FrameSequenceTrackerCollection::StopSequence( - FrameSequenceTrackerType type) { - DCHECK_NE(FrameSequenceTrackerType::kCustom, type); - - if (!frame_trackers_.contains(type)) - return; - - std::unique_ptr<FrameSequenceTracker> tracker = - std::move(frame_trackers_[type]); - - if (compositor_frame_reporting_controller_) - compositor_frame_reporting_controller_->RemoveActiveTracker(tracker->type_); - - frame_trackers_.erase(type); - tracker->ScheduleTerminate(); - removal_trackers_.push_back(std::move(tracker)); - DestroyTrackers(); -} - -void FrameSequenceTrackerCollection::StartCustomSequence(int sequence_id) { - DCHECK(!base::Contains(custom_frame_trackers_, sequence_id)); - - custom_frame_trackers_[sequence_id] = base::WrapUnique( - new FrameSequenceTracker(FrameSequenceTrackerType::kCustom, - /*throughput_ukm_reporter=*/nullptr, - /*custom_sequence_id=*/sequence_id)); -} - -void FrameSequenceTrackerCollection::StopCustomSequence(int sequence_id) { - auto it = custom_frame_trackers_.find(sequence_id); - // This happens when an animation is aborted before starting. - if (it == custom_frame_trackers_.end()) - return; - - std::unique_ptr<FrameSequenceTracker> tracker = std::move(it->second); - custom_frame_trackers_.erase(it); - tracker->ScheduleTerminate(); - removal_trackers_.push_back(std::move(tracker)); -} - -void FrameSequenceTrackerCollection::ClearAll() { - frame_trackers_.clear(); - custom_frame_trackers_.clear(); - removal_trackers_.clear(); -} - -void FrameSequenceTrackerCollection::NotifyBeginImplFrame( - const viz::BeginFrameArgs& args) { - RecreateTrackers(args); - for (auto& tracker : frame_trackers_) - tracker.second->ReportBeginImplFrame(args); - for (auto& tracker : custom_frame_trackers_) - tracker.second->ReportBeginImplFrame(args); -} - -void FrameSequenceTrackerCollection::NotifyBeginMainFrame( - const viz::BeginFrameArgs& args) { - for (auto& tracker : frame_trackers_) - tracker.second->ReportBeginMainFrame(args); - for (auto& tracker : custom_frame_trackers_) - tracker.second->ReportBeginMainFrame(args); -} - -void FrameSequenceTrackerCollection::NotifyMainFrameProcessed( - const viz::BeginFrameArgs& args) { - for (auto& tracker : frame_trackers_) - tracker.second->ReportMainFrameProcessed(args); - for (auto& tracker : custom_frame_trackers_) - tracker.second->ReportMainFrameProcessed(args); -} - -void FrameSequenceTrackerCollection::NotifyImplFrameCausedNoDamage( - const viz::BeginFrameAck& ack) { - for (auto& tracker : frame_trackers_) - tracker.second->ReportImplFrameCausedNoDamage(ack); - for (auto& tracker : custom_frame_trackers_) - tracker.second->ReportImplFrameCausedNoDamage(ack); - - // Removal trackers continue to process any frames which they started - // observing. - for (auto& tracker : removal_trackers_) - tracker->ReportImplFrameCausedNoDamage(ack); -} - -void FrameSequenceTrackerCollection::NotifyMainFrameCausedNoDamage( - const viz::BeginFrameArgs& args) { - for (auto& tracker : frame_trackers_) - tracker.second->ReportMainFrameCausedNoDamage(args); - for (auto& tracker : custom_frame_trackers_) - tracker.second->ReportMainFrameCausedNoDamage(args); -} - -void FrameSequenceTrackerCollection::NotifyPauseFrameProduction() { - for (auto& tracker : frame_trackers_) - tracker.second->PauseFrameProduction(); - for (auto& tracker : custom_frame_trackers_) - tracker.second->PauseFrameProduction(); -} - -void FrameSequenceTrackerCollection::NotifySubmitFrame( - uint32_t frame_token, - bool has_missing_content, - const viz::BeginFrameAck& ack, - const viz::BeginFrameArgs& origin_args) { - for (auto& tracker : frame_trackers_) { - tracker.second->ReportSubmitFrame(frame_token, has_missing_content, ack, - origin_args); - } - for (auto& tracker : custom_frame_trackers_) { - tracker.second->ReportSubmitFrame(frame_token, has_missing_content, ack, - origin_args); - } - - // Removal trackers continue to process any frames which they started - // observing. - for (auto& tracker : removal_trackers_) { - tracker->ReportSubmitFrame(frame_token, has_missing_content, ack, - origin_args); - } - - // TODO(crbug.com/1072482): find a proper way to terminate a tracker. Please - // refer to details in FrameSequenceTracker::ReportSubmitFrame - DestroyTrackers(); -} - -void FrameSequenceTrackerCollection::NotifyFrameEnd( - const viz::BeginFrameArgs& args, - const viz::BeginFrameArgs& main_args) { - for (auto& tracker : frame_trackers_) - tracker.second->ReportFrameEnd(args, main_args); - for (auto& tracker : custom_frame_trackers_) - tracker.second->ReportFrameEnd(args, main_args); - - // Removal trackers continue to process any frames which they started - // observing. - for (auto& tracker : removal_trackers_) - tracker->ReportFrameEnd(args, main_args); - DestroyTrackers(); -} - -void FrameSequenceTrackerCollection::NotifyFramePresented( - uint32_t frame_token, - const gfx::PresentationFeedback& feedback) { - for (auto& tracker : frame_trackers_) - tracker.second->ReportFramePresented(frame_token, feedback); - for (auto& tracker : custom_frame_trackers_) - tracker.second->ReportFramePresented(frame_token, feedback); - - for (auto& tracker : removal_trackers_) - tracker->ReportFramePresented(frame_token, feedback); - - for (auto& tracker : removal_trackers_) { - if (tracker->termination_status() == - FrameSequenceTracker::TerminationStatus::kReadyForTermination) { - // The tracker is ready to be terminated. - // For non kCustom typed trackers, take the metrics from the tracker. - // merge with any outstanding metrics from previous trackers of the same - // type. If there are enough frames to report the metrics, then report the - // metrics and destroy it. Otherwise, retain it to be merged with - // follow-up sequences. - // For kCustom typed trackers, put its result in |custom_tracker_results_| - // to be picked up by caller. - auto metrics = tracker->TakeMetrics(); - if (tracker->type() == FrameSequenceTrackerType::kCustom) { - custom_tracker_results_[tracker->custom_sequence_id()] = - metrics->main_throughput(); - // |custom_tracker_results_| should be picked up timely. - DCHECK_LT(custom_tracker_results_.size(), 500u); - continue; - } - - auto key = std::make_pair(tracker->type(), metrics->GetEffectiveThread()); - if (accumulated_metrics_.contains(key)) { - metrics->Merge(std::move(accumulated_metrics_[key])); - accumulated_metrics_.erase(key); - } - - if (metrics->HasEnoughDataForReporting()) - metrics->ReportMetrics(); - if (metrics->HasDataLeftForReporting()) - accumulated_metrics_[key] = std::move(metrics); - } - } - - DestroyTrackers(); -} - -void FrameSequenceTrackerCollection::DestroyTrackers() { - base::EraseIf( - removal_trackers_, - [](const std::unique_ptr<FrameSequenceTracker>& tracker) { - return tracker->termination_status() == - FrameSequenceTracker::TerminationStatus::kReadyForTermination; - }); -} - -void FrameSequenceTrackerCollection::RecreateTrackers( - const viz::BeginFrameArgs& args) { - std::vector<FrameSequenceTrackerType> recreate_trackers; - for (const auto& tracker : frame_trackers_) { - if (tracker.second->ShouldReportMetricsNow(args)) - recreate_trackers.push_back(tracker.first); - } - - for (const auto& tracker_type : recreate_trackers) { - // StopSequence put the tracker in the |removal_trackers_|, which will - // report its throughput data when its frame is presented. - StopSequence(tracker_type); - // The frame sequence is still active, so create a new tracker to keep - // tracking this sequence. - StartSequence(tracker_type); - } -} - -ActiveFrameSequenceTrackers -FrameSequenceTrackerCollection::FrameSequenceTrackerActiveTypes() { - ActiveFrameSequenceTrackers encoded_types = 0; - for (const auto& tracker : frame_trackers_) { - encoded_types |= static_cast<ActiveFrameSequenceTrackers>( - 1 << static_cast<unsigned>(tracker.first)); - } - return encoded_types; -} - -CustomTrackerResults -FrameSequenceTrackerCollection::TakeCustomTrackerResults() { - return std::move(custom_tracker_results_); -} - -FrameSequenceTracker* FrameSequenceTrackerCollection::GetTrackerForTesting( - FrameSequenceTrackerType type) { - if (!frame_trackers_.contains(type)) - return nullptr; - return frame_trackers_[type].get(); -} - -FrameSequenceTracker* -FrameSequenceTrackerCollection::GetRemovalTrackerForTesting( - FrameSequenceTrackerType type) { - for (const auto& tracker : removal_trackers_) - if (tracker->type_ == type) - return tracker.get(); - return nullptr; -} - -void FrameSequenceTrackerCollection::SetUkmManager(UkmManager* manager) { - DCHECK(frame_trackers_.empty()); - if (manager) - throughput_ukm_reporter_ = std::make_unique<ThroughputUkmReporter>(manager); - else - throughput_ukm_reporter_ = nullptr; -} - -//////////////////////////////////////////////////////////////////////////////// -// FrameSequenceTracker - FrameSequenceTracker::FrameSequenceTracker( FrameSequenceTrackerType type, - ThroughputUkmReporter* throughput_ukm_reporter, - int custom_sequence_id) - : type_(type), - custom_sequence_id_(custom_sequence_id), + ThroughputUkmReporter* throughput_ukm_reporter) + : custom_sequence_id_(-1), metrics_(std::make_unique<FrameSequenceMetrics>(type, throughput_ukm_reporter)), trace_data_(metrics_.get()) { - DCHECK_LT(type_, FrameSequenceTrackerType::kMaxType); - DCHECK(type_ != FrameSequenceTrackerType::kCustom || - custom_sequence_id_ >= 0); + DCHECK_LT(type, FrameSequenceTrackerType::kMaxType); + DCHECK(type != FrameSequenceTrackerType::kCustom); +} + +FrameSequenceTracker::FrameSequenceTracker( + int custom_sequence_id, + FrameSequenceMetrics::CustomReporter custom_reporter) + : custom_sequence_id_(custom_sequence_id), + metrics_(std::make_unique<FrameSequenceMetrics>( + FrameSequenceTrackerType::kCustom, + /*ukm_reporter=*/nullptr)), + trace_data_(metrics_.get()) { + DCHECK_GT(custom_sequence_id_, 0); + metrics_->SetCustomReporter(std::move(custom_reporter)); } FrameSequenceTracker::~FrameSequenceTracker() = default; @@ -649,7 +101,8 @@ void FrameSequenceTracker::ReportBeginImplFrame( if (ShouldIgnoreBeginFrameSource(args.frame_id.source_id)) return; - TRACKER_TRACE_STREAM << "b(" << args.frame_id.sequence_number << ")"; + TRACKER_TRACE_STREAM << "b(" << args.frame_id.sequence_number % kDebugStrMod + << ")"; DCHECK(!is_inside_frame_) << TRACKER_DCHECK_MSG; is_inside_frame_ = true; @@ -689,8 +142,11 @@ void FrameSequenceTracker::ReportBeginMainFrame( if (ShouldIgnoreBeginFrameSource(args.frame_id.source_id)) return; - TRACKER_TRACE_STREAM << "B(" << begin_main_frame_data_.previous_sequence - << "," << args.frame_id.sequence_number << ")"; + TRACKER_TRACE_STREAM << "B(" + << begin_main_frame_data_.previous_sequence % + kDebugStrMod + << "," << args.frame_id.sequence_number % kDebugStrMod + << ")"; if (first_received_main_sequence_ && first_received_main_sequence_ > args.frame_id.sequence_number) { @@ -732,7 +188,8 @@ void FrameSequenceTracker::ReportMainFrameProcessed( if (ShouldIgnoreBeginFrameSource(args.frame_id.source_id)) return; - TRACKER_TRACE_STREAM << "E(" << args.frame_id.sequence_number << ")"; + TRACKER_TRACE_STREAM << "E(" << args.frame_id.sequence_number % kDebugStrMod + << ")"; const bool previous_main_frame_submitted_or_no_damage = previous_begin_main_sequence_ != 0 && @@ -779,6 +236,7 @@ void FrameSequenceTracker::ReportSubmitFrame( // not going to be presented, and thus terminate this tracker. const uint32_t frames_to_terminate_tracker = 3; if (termination_status_ == TerminationStatus::kScheduledForTermination && + last_submitted_frame_ != 0 && viz::FrameTokenGT(frame_token, last_submitted_frame_ + frames_to_terminate_tracker)) { termination_status_ = TerminationStatus::kReadyForTermination; @@ -805,7 +263,7 @@ void FrameSequenceTracker::ReportSubmitFrame( last_submitted_frame_ = frame_token; compositor_frame_submitted_ = true; - TRACKER_TRACE_STREAM << "s(" << frame_token << ")"; + TRACKER_TRACE_STREAM << "s(" << frame_token % kDebugStrMod << ")"; had_impl_frame_submitted_between_commits_ = true; const bool main_changes_after_sequence_started = @@ -825,7 +283,9 @@ void FrameSequenceTracker::ReportSubmitFrame( if (main_changes_after_sequence_started && main_changes_include_new_changes && !main_change_had_no_damage) { submitted_frame_had_new_main_content_ = true; - TRACKER_TRACE_STREAM << "S(" << origin_args.frame_id.sequence_number + TRACKER_TRACE_STREAM << "S(" + << origin_args.frame_id.sequence_number % + kDebugStrMod << ")"; last_submitted_main_sequence_ = origin_args.frame_id.sequence_number; @@ -856,8 +316,10 @@ void FrameSequenceTracker::ReportFrameEnd( if (ShouldIgnoreBeginFrameSource(args.frame_id.source_id)) return; - TRACKER_TRACE_STREAM << "e(" << args.frame_id.sequence_number << "," - << main_args.frame_id.sequence_number << ")"; + TRACKER_TRACE_STREAM << "e(" << args.frame_id.sequence_number % kDebugStrMod + << "," + << main_args.frame_id.sequence_number % kDebugStrMod + << ")"; bool should_ignore_sequence = ShouldIgnoreSequence(args.frame_id.sequence_number); @@ -923,18 +385,24 @@ void FrameSequenceTracker::ReportFrameEnd( void FrameSequenceTracker::ReportFramePresented( uint32_t frame_token, const gfx::PresentationFeedback& feedback) { + // TODO(xidachen): We should early exit if |last_submitted_frame_| = 0, as it + // means that we are presenting the same frame_token again. + const bool submitted_frame_since_last_presentation = !!last_submitted_frame_; // !viz::FrameTokenGT(a, b) is equivalent to b >= a. const bool frame_token_acks_last_frame = !viz::FrameTokenGT(last_submitted_frame_, frame_token); + // Even if the presentation timestamp is null, we set last_submitted_frame_ to + // 0 such that the tracker can be terminated. + if (last_submitted_frame_ && frame_token_acks_last_frame) + last_submitted_frame_ = 0; // Update termination status if this is scheduled for termination, and it is // not waiting for any frames, or it has received the presentation-feedback // for the latest frame it is tracking. // // We should always wait for an impl frame to end, that is, ReportFrameEnd. if (termination_status_ == TerminationStatus::kScheduledForTermination && - (last_submitted_frame_ == 0 || frame_token_acks_last_frame) && - !is_inside_frame_) { + last_submitted_frame_ == 0 && !is_inside_frame_) { termination_status_ = TerminationStatus::kReadyForTermination; } @@ -945,28 +413,25 @@ void FrameSequenceTracker::ReportFramePresented( return; } - TRACKER_TRACE_STREAM << "P(" << frame_token << ")"; + TRACKER_TRACE_STREAM << "P(" << frame_token % kDebugStrMod << ")"; - if (ignored_frame_tokens_.contains(frame_token)) - return; base::EraseIf(ignored_frame_tokens_, [frame_token](const uint32_t& token) { return viz::FrameTokenGT(frame_token, token); }); + if (ignored_frame_tokens_.contains(frame_token)) + return; uint32_t impl_frames_produced = 0; uint32_t main_frames_produced = 0; trace_data_.Advance(feedback.timestamp); const bool was_presented = !feedback.timestamp.is_null(); - if (was_presented && last_submitted_frame_) { + if (was_presented && submitted_frame_since_last_presentation) { DCHECK_LT(impl_throughput().frames_produced, impl_throughput().frames_expected) << TRACKER_DCHECK_MSG; ++impl_throughput().frames_produced; ++impl_frames_produced; - - if (frame_token_acks_last_frame) - last_submitted_frame_ = 0; } if (was_presented) { @@ -1062,7 +527,8 @@ void FrameSequenceTracker::ReportImplFrameCausedNoDamage( if (ShouldIgnoreBeginFrameSource(ack.frame_id.source_id)) return; - TRACKER_TRACE_STREAM << "n(" << ack.frame_id.sequence_number << ")"; + TRACKER_TRACE_STREAM << "n(" << ack.frame_id.sequence_number % kDebugStrMod + << ")"; // This tracker would be scheduled to terminate, and this frame doesn't belong // to that tracker. @@ -1087,8 +553,11 @@ void FrameSequenceTracker::ReportMainFrameCausedNoDamage( if (ShouldIgnoreBeginFrameSource(args.frame_id.source_id)) return; - TRACKER_TRACE_STREAM << "N(" << begin_main_frame_data_.previous_sequence - << "," << args.frame_id.sequence_number << ")"; + TRACKER_TRACE_STREAM << "N(" + << begin_main_frame_data_.previous_sequence % + kDebugStrMod + << "," << args.frame_id.sequence_number % kDebugStrMod + << ")"; if (!first_received_main_sequence_ || first_received_main_sequence_ > args.frame_id.sequence_number) { @@ -1206,85 +675,6 @@ std::unique_ptr<FrameSequenceMetrics> FrameSequenceTracker::TakeMetrics() { return std::move(metrics_); } -base::Optional<int> FrameSequenceMetrics::ThroughputData::ReportHistogram( - FrameSequenceMetrics* metrics, - ThreadType thread_type, - int metric_index, - const ThroughputData& data) { - const auto sequence_type = metrics->type(); - DCHECK_LT(sequence_type, FrameSequenceTrackerType::kMaxType); - - STATIC_HISTOGRAM_POINTER_GROUP( - GetFrameSequenceLengthHistogramName(sequence_type), - static_cast<int>(sequence_type), - static_cast<int>(FrameSequenceTrackerType::kMaxType), - Add(data.frames_expected), - base::Histogram::FactoryGet( - GetFrameSequenceLengthHistogramName(sequence_type), 1, 1000, 50, - base::HistogramBase::kUmaTargetedHistogramFlag)); - - if (data.frames_expected < kMinFramesForThroughputMetric) - return base::nullopt; - - // Throughput means the percent of frames that was expected to show on the - // screen but didn't. In other words, the lower the throughput is, the - // smoother user experience. - const int percent = - std::ceil(100 * (data.frames_expected - data.frames_produced) / - static_cast<double>(data.frames_expected)); - - const bool is_animation = - ShouldReportForAnimation(sequence_type, thread_type); - const bool is_interaction = ShouldReportForInteraction(metrics, thread_type); - - ThroughputUkmReporter* const ukm_reporter = metrics->ukm_reporter(); - - if (is_animation) { - UMA_HISTOGRAM_PERCENTAGE( - "Graphics.Smoothness.PercentDroppedFrames.AllAnimations", percent); - if (ukm_reporter) { - ukm_reporter->ReportAggregateThroughput(AggregationType::kAllAnimations, - percent); - } - } - - if (is_interaction) { - UMA_HISTOGRAM_PERCENTAGE( - "Graphics.Smoothness.PercentDroppedFrames.AllInteractions", percent); - if (ukm_reporter) { - ukm_reporter->ReportAggregateThroughput(AggregationType::kAllInteractions, - percent); - } - } - - if (is_animation || is_interaction) { - UMA_HISTOGRAM_PERCENTAGE( - "Graphics.Smoothness.PercentDroppedFrames.AllSequences", percent); - if (ukm_reporter) { - ukm_reporter->ReportAggregateThroughput(AggregationType::kAllSequences, - percent); - } - } - - if (!is_animation && !IsInteractionType(sequence_type) && - sequence_type != FrameSequenceTrackerType::kUniversal && - sequence_type != FrameSequenceTrackerType::kVideo) { - return base::nullopt; - } - - const char* thread_name = - thread_type == ThreadType::kCompositor - ? "CompositorThread" - : thread_type == ThreadType::kMain ? "MainThread" : "SlowerThread"; - STATIC_HISTOGRAM_POINTER_GROUP( - GetThroughputHistogramName(sequence_type, thread_name), metric_index, - kMaximumHistogramIndex, Add(percent), - base::LinearHistogram::FactoryGet( - GetThroughputHistogramName(sequence_type, thread_name), 1, 100, 101, - base::HistogramBase::kUmaTargetedHistogramFlag)); - return percent; -} - FrameSequenceTracker::CheckerboardingData::CheckerboardingData() = default; FrameSequenceTracker::CheckerboardingData::~CheckerboardingData() = default; diff --git a/chromium/cc/metrics/frame_sequence_tracker.h b/chromium/cc/metrics/frame_sequence_tracker.h index b45fd72dac5..680b2a85ad9 100644 --- a/chromium/cc/metrics/frame_sequence_tracker.h +++ b/chromium/cc/metrics/frame_sequence_tracker.h @@ -5,20 +5,12 @@ #ifndef CC_METRICS_FRAME_SEQUENCE_TRACKER_H_ #define CC_METRICS_FRAME_SEQUENCE_TRACKER_H_ -#include <stdint.h> -#include <memory> -#include <set> -#include <utility> -#include <vector> - -#include "base/callback_helpers.h" #include "base/containers/circular_deque.h" #include "base/containers/flat_map.h" #include "base/containers/flat_set.h" -#include "base/macros.h" #include "base/optional.h" -#include "base/trace_event/traced_value.h" #include "cc/cc_export.h" +#include "cc/metrics/frame_sequence_metrics.h" namespace gfx { struct PresentationFeedback; @@ -31,237 +23,7 @@ struct BeginFrameId; } // namespace viz namespace cc { -class FrameSequenceTracker; -class CompositorFrameReportingController; class ThroughputUkmReporter; -class UkmManager; - -enum class FrameSequenceTrackerType { - // Used as an enum for metrics. DO NOT reorder or delete values. Rather, - // add them at the end and increment kMaxType. - kCompositorAnimation = 0, - kMainThreadAnimation = 1, - kPinchZoom = 2, - kRAF = 3, - kTouchScroll = 4, - kUniversal = 5, - kVideo = 6, - kWheelScroll = 7, - kScrollbarScroll = 8, - kCustom = 9, // Note that the metrics for kCustom are not reported on UMA, - // and instead are dispatched back to the LayerTreeHostClient. - kMaxType -}; - -typedef uint16_t ActiveFrameSequenceTrackers; - -class CC_EXPORT FrameSequenceMetrics { - public: - FrameSequenceMetrics(FrameSequenceTrackerType type, - ThroughputUkmReporter* ukm_reporter); - ~FrameSequenceMetrics(); - - FrameSequenceMetrics(const FrameSequenceMetrics&) = delete; - FrameSequenceMetrics& operator=(const FrameSequenceMetrics&) = delete; - - enum class ThreadType { kMain, kCompositor, kSlower, kUnknown }; - - struct ThroughputData { - static std::unique_ptr<base::trace_event::TracedValue> ToTracedValue( - const ThroughputData& impl, - const ThroughputData& main); - - // Returns the throughput in percent, a return value of base::nullopt - // indicates that no throughput metric is reported. - static base::Optional<int> ReportHistogram(FrameSequenceMetrics* metrics, - ThreadType thread_type, - int metric_index, - const ThroughputData& data); - - void Merge(const ThroughputData& data) { - frames_expected += data.frames_expected; - frames_produced += data.frames_produced; -#if DCHECK_IS_ON() - frames_processed += data.frames_processed; - frames_received += data.frames_received; -#endif - } - - // Tracks the number of frames that were expected to be shown during this - // frame-sequence. - uint32_t frames_expected = 0; - - // Tracks the number of frames that were actually presented to the user - // during this frame-sequence. - uint32_t frames_produced = 0; - -#if DCHECK_IS_ON() - // Tracks the number of frames that is either submitted or reported as no - // damage. - uint32_t frames_processed = 0; - - // Tracks the number of begin-frames that are received. - uint32_t frames_received = 0; -#endif - }; - - void SetScrollingThread(ThreadType thread); - - // Returns the 'effective thread' for the metrics (i.e. the thread most - // relevant for this metric). - ThreadType GetEffectiveThread() const; - - void Merge(std::unique_ptr<FrameSequenceMetrics> metrics); - bool HasEnoughDataForReporting() const; - bool HasDataLeftForReporting() const; - // Report related metrics: throughput, checkboarding... - void ReportMetrics(); - void ComputeAggregatedThroughputForTesting() { - ComputeAggregatedThroughput(); - } - - ThroughputData& impl_throughput() { return impl_throughput_; } - ThroughputData& main_throughput() { return main_throughput_; } - ThroughputData& aggregated_throughput() { return aggregated_throughput_; } - void add_checkerboarded_frames(int64_t frames) { - frames_checkerboarded_ += frames; - } - uint32_t frames_checkerboarded() const { return frames_checkerboarded_; } - - FrameSequenceTrackerType type() const { return type_; } - ThroughputUkmReporter* ukm_reporter() const { - return throughput_ukm_reporter_; - } - - private: - void ComputeAggregatedThroughput(); - const FrameSequenceTrackerType type_; - - // Pointer to the reporter owned by the FrameSequenceTrackerCollection. - ThroughputUkmReporter* const throughput_ukm_reporter_; - - ThroughputData impl_throughput_; - ThroughputData main_throughput_; - // The aggregated throughput for the main/compositor thread. - ThroughputData aggregated_throughput_; - - ThreadType scrolling_thread_ = ThreadType::kUnknown; - - // Tracks the number of produced frames that had some amount of - // checkerboarding, and how many frames showed such checkerboarded frames. - uint32_t frames_checkerboarded_ = 0; -}; - -// Map of kCustom tracker results keyed by a sequence id. -using CustomTrackerResults = - base::flat_map<int, FrameSequenceMetrics::ThroughputData>; - -// Used for notifying attached FrameSequenceTracker's of begin-frames and -// submitted frames. -class CC_EXPORT FrameSequenceTrackerCollection { - public: - FrameSequenceTrackerCollection( - bool is_single_threaded, - CompositorFrameReportingController* frame_reporting_controller); - ~FrameSequenceTrackerCollection(); - - FrameSequenceTrackerCollection(const FrameSequenceTrackerCollection&) = - delete; - FrameSequenceTrackerCollection& operator=( - const FrameSequenceTrackerCollection&) = delete; - - // Creates a tracker for the specified sequence-type. - FrameSequenceMetrics* StartSequence(FrameSequenceTrackerType type); - - // Schedules |tracker| for destruction. This is preferred instead of outright - // desrtruction of the tracker, since this ensures that the actual tracker - // instance is destroyed *after* the presentation-feedbacks have been received - // for all submitted frames. - void StopSequence(FrameSequenceTrackerType type); - - // Creates a kCustom tracker for the given sequence id. It is an error and - // DCHECKs if there is already a tracker associated with the sequence id. - void StartCustomSequence(int sequence_id); - - // Schedules the kCustom tracker representing |sequence_id| for destruction. - // It is a no-op if there is no tracker associated with the sequence id. - // Similar to StopSequence above, the tracker instance is destroyed *after* - // the presentation feedbacks have been received for all submitted frames. - void StopCustomSequence(int sequence_id); - - // Removes all trackers. This also immediately destroys all trackers that had - // been scheduled for destruction, even if there are pending - // presentation-feedbacks. This is typically used if the client no longer - // expects to receive presentation-feedbacks for the previously submitted - // frames (e.g. when the gpu process dies). - void ClearAll(); - - // Notifies all trackers of various events. - void NotifyBeginImplFrame(const viz::BeginFrameArgs& args); - void NotifyBeginMainFrame(const viz::BeginFrameArgs& args); - void NotifyMainFrameProcessed(const viz::BeginFrameArgs& args); - void NotifyImplFrameCausedNoDamage(const viz::BeginFrameAck& ack); - void NotifyMainFrameCausedNoDamage(const viz::BeginFrameArgs& args); - void NotifyPauseFrameProduction(); - void NotifySubmitFrame(uint32_t frame_token, - bool has_missing_content, - const viz::BeginFrameAck& ack, - const viz::BeginFrameArgs& origin_args); - void NotifyFrameEnd(const viz::BeginFrameArgs& args, - const viz::BeginFrameArgs& main_args); - - // Note that this notifies the trackers of the presentation-feedbacks, and - // destroys any tracker that had been scheduled for destruction (using - // |ScheduleRemoval()|) if it has no more pending frames. Data from non - // kCustom typed trackers are reported to UMA. Data from kCustom typed - // trackers are added to |custom_tracker_results_| for caller to pick up. - void NotifyFramePresented(uint32_t frame_token, - const gfx::PresentationFeedback& feedback); - - // Return the type of each active frame tracker, encoded into a 16 bit - // integer with the bit at each position corresponding to the enum value of - // each type. - ActiveFrameSequenceTrackers FrameSequenceTrackerActiveTypes(); - - // Reports the accumulated kCustom tracker results and clears it. - CustomTrackerResults TakeCustomTrackerResults(); - - FrameSequenceTracker* GetTrackerForTesting(FrameSequenceTrackerType type); - FrameSequenceTracker* GetRemovalTrackerForTesting( - FrameSequenceTrackerType type); - - void SetUkmManager(UkmManager* manager); - - private: - friend class FrameSequenceTrackerTest; - - void RecreateTrackers(const viz::BeginFrameArgs& args); - // Destroy the trackers that are ready to be terminated. - void DestroyTrackers(); - - const bool is_single_threaded_; - // The callsite can use the type to manipulate the tracker. - base::flat_map<FrameSequenceTrackerType, - std::unique_ptr<FrameSequenceTracker>> - frame_trackers_; - - // Custom trackers are keyed by a custom sequence id. - base::flat_map<int, std::unique_ptr<FrameSequenceTracker>> - custom_frame_trackers_; - CustomTrackerResults custom_tracker_results_; - - std::vector<std::unique_ptr<FrameSequenceTracker>> removal_trackers_; - CompositorFrameReportingController* const - compositor_frame_reporting_controller_; - - // The reporter takes throughput data and connect to UkmManager to report it. - std::unique_ptr<ThroughputUkmReporter> throughput_ukm_reporter_; - - base::flat_map< - std::pair<FrameSequenceTrackerType, FrameSequenceMetrics::ThreadType>, - std::unique_ptr<FrameSequenceMetrics>> - accumulated_metrics_; -}; // Tracks a sequence of frames to determine the throughput. It tracks this by // tracking the vsync sequence-numbers (from |BeginFrameArgs::sequence_number|), @@ -334,7 +96,7 @@ class CC_EXPORT FrameSequenceTracker { bool ShouldReportMetricsNow(const viz::BeginFrameArgs& args) const; FrameSequenceMetrics* metrics() { return metrics_.get(); } - FrameSequenceTrackerType type() const { return type_; } + FrameSequenceTrackerType type() const { return metrics_->type(); } int custom_sequence_id() const { return custom_sequence_id_; } std::unique_ptr<FrameSequenceMetrics> TakeMetrics(); @@ -343,9 +105,12 @@ class CC_EXPORT FrameSequenceTracker { friend class FrameSequenceTrackerCollection; friend class FrameSequenceTrackerTest; + // Constructs a tracker for a typed sequence other than kCustom. FrameSequenceTracker(FrameSequenceTrackerType type, - ThroughputUkmReporter* throughput_ukm_reporter, - int custom_sequence_id = -1); + ThroughputUkmReporter* throughput_ukm_reporter); + // Constructs a tracker for a kCustom typed sequence. + FrameSequenceTracker(int custom_sequence_id, + FrameSequenceMetrics::CustomReporter custom_reporter); FrameSequenceMetrics::ThroughputData& impl_throughput() { return metrics_->impl_throughput(); @@ -393,7 +158,6 @@ class CC_EXPORT FrameSequenceTracker { bool ShouldIgnoreSequence(uint64_t sequence_number) const; - const FrameSequenceTrackerType type_; const int custom_sequence_id_; TerminationStatus termination_status_ = TerminationStatus::kActive; diff --git a/chromium/cc/metrics/frame_sequence_tracker_collection.cc b/chromium/cc/metrics/frame_sequence_tracker_collection.cc new file mode 100644 index 00000000000..8dd78ca0ccd --- /dev/null +++ b/chromium/cc/metrics/frame_sequence_tracker_collection.cc @@ -0,0 +1,316 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/metrics/frame_sequence_tracker_collection.h" + +#include "base/memory/ptr_util.h" +#include "cc/metrics/compositor_frame_reporting_controller.h" +#include "cc/metrics/frame_sequence_tracker.h" +#include "cc/metrics/throughput_ukm_reporter.h" + +namespace cc { + +FrameSequenceTrackerCollection::FrameSequenceTrackerCollection( + bool is_single_threaded, + CompositorFrameReportingController* compositor_frame_reporting_controller) + : is_single_threaded_(is_single_threaded), + compositor_frame_reporting_controller_( + compositor_frame_reporting_controller) {} + +FrameSequenceTrackerCollection::~FrameSequenceTrackerCollection() { + frame_trackers_.clear(); + removal_trackers_.clear(); +} + +FrameSequenceMetrics* FrameSequenceTrackerCollection::StartSequence( + FrameSequenceTrackerType type) { + DCHECK_NE(FrameSequenceTrackerType::kCustom, type); + + if (is_single_threaded_) + return nullptr; + if (frame_trackers_.contains(type)) + return frame_trackers_[type]->metrics(); + auto tracker = base::WrapUnique( + new FrameSequenceTracker(type, throughput_ukm_reporter_.get())); + frame_trackers_[type] = std::move(tracker); + + if (compositor_frame_reporting_controller_) + compositor_frame_reporting_controller_->AddActiveTracker(type); + return frame_trackers_[type]->metrics(); +} + +void FrameSequenceTrackerCollection::StopSequence( + FrameSequenceTrackerType type) { + DCHECK_NE(FrameSequenceTrackerType::kCustom, type); + + if (!frame_trackers_.contains(type)) + return; + + std::unique_ptr<FrameSequenceTracker> tracker = + std::move(frame_trackers_[type]); + + if (compositor_frame_reporting_controller_) + compositor_frame_reporting_controller_->RemoveActiveTracker( + tracker->type()); + + frame_trackers_.erase(type); + tracker->ScheduleTerminate(); + removal_trackers_.push_back(std::move(tracker)); + DestroyTrackers(); +} + +void FrameSequenceTrackerCollection::StartCustomSequence(int sequence_id) { + DCHECK(!base::Contains(custom_frame_trackers_, sequence_id)); + + // base::Unretained() is safe here because |this| owns FrameSequenceTracker + // and FrameSequenceMetrics. + custom_frame_trackers_[sequence_id] = + base::WrapUnique(new FrameSequenceTracker( + sequence_id, + base::BindOnce( + &FrameSequenceTrackerCollection::AddCustomTrackerResult, + base::Unretained(this), sequence_id))); +} + +void FrameSequenceTrackerCollection::StopCustomSequence(int sequence_id) { + auto it = custom_frame_trackers_.find(sequence_id); + // This happens when an animation is aborted before starting. + if (it == custom_frame_trackers_.end()) + return; + + std::unique_ptr<FrameSequenceTracker> tracker = std::move(it->second); + custom_frame_trackers_.erase(it); + tracker->ScheduleTerminate(); + removal_trackers_.push_back(std::move(tracker)); + DestroyTrackers(); +} + +void FrameSequenceTrackerCollection::ClearAll() { + frame_trackers_.clear(); + custom_frame_trackers_.clear(); + removal_trackers_.clear(); +} + +void FrameSequenceTrackerCollection::NotifyBeginImplFrame( + const viz::BeginFrameArgs& args) { + RecreateTrackers(args); + for (auto& tracker : frame_trackers_) + tracker.second->ReportBeginImplFrame(args); + for (auto& tracker : custom_frame_trackers_) + tracker.second->ReportBeginImplFrame(args); +} + +void FrameSequenceTrackerCollection::NotifyBeginMainFrame( + const viz::BeginFrameArgs& args) { + for (auto& tracker : frame_trackers_) + tracker.second->ReportBeginMainFrame(args); + for (auto& tracker : custom_frame_trackers_) + tracker.second->ReportBeginMainFrame(args); +} + +void FrameSequenceTrackerCollection::NotifyMainFrameProcessed( + const viz::BeginFrameArgs& args) { + for (auto& tracker : frame_trackers_) + tracker.second->ReportMainFrameProcessed(args); + for (auto& tracker : custom_frame_trackers_) + tracker.second->ReportMainFrameProcessed(args); +} + +void FrameSequenceTrackerCollection::NotifyImplFrameCausedNoDamage( + const viz::BeginFrameAck& ack) { + for (auto& tracker : frame_trackers_) + tracker.second->ReportImplFrameCausedNoDamage(ack); + for (auto& tracker : custom_frame_trackers_) + tracker.second->ReportImplFrameCausedNoDamage(ack); + + // Removal trackers continue to process any frames which they started + // observing. + for (auto& tracker : removal_trackers_) + tracker->ReportImplFrameCausedNoDamage(ack); +} + +void FrameSequenceTrackerCollection::NotifyMainFrameCausedNoDamage( + const viz::BeginFrameArgs& args) { + for (auto& tracker : frame_trackers_) + tracker.second->ReportMainFrameCausedNoDamage(args); + for (auto& tracker : custom_frame_trackers_) + tracker.second->ReportMainFrameCausedNoDamage(args); +} + +void FrameSequenceTrackerCollection::NotifyPauseFrameProduction() { + for (auto& tracker : frame_trackers_) + tracker.second->PauseFrameProduction(); + for (auto& tracker : custom_frame_trackers_) + tracker.second->PauseFrameProduction(); +} + +void FrameSequenceTrackerCollection::NotifySubmitFrame( + uint32_t frame_token, + bool has_missing_content, + const viz::BeginFrameAck& ack, + const viz::BeginFrameArgs& origin_args) { + for (auto& tracker : frame_trackers_) { + tracker.second->ReportSubmitFrame(frame_token, has_missing_content, ack, + origin_args); + } + for (auto& tracker : custom_frame_trackers_) { + tracker.second->ReportSubmitFrame(frame_token, has_missing_content, ack, + origin_args); + } + + // Removal trackers continue to process any frames which they started + // observing. + for (auto& tracker : removal_trackers_) { + tracker->ReportSubmitFrame(frame_token, has_missing_content, ack, + origin_args); + } + + // TODO(crbug.com/1072482): find a proper way to terminate a tracker. Please + // refer to details in FrameSequenceTracker::ReportSubmitFrame + DestroyTrackers(); +} + +void FrameSequenceTrackerCollection::NotifyFrameEnd( + const viz::BeginFrameArgs& args, + const viz::BeginFrameArgs& main_args) { + for (auto& tracker : frame_trackers_) + tracker.second->ReportFrameEnd(args, main_args); + for (auto& tracker : custom_frame_trackers_) + tracker.second->ReportFrameEnd(args, main_args); + + // Removal trackers continue to process any frames which they started + // observing. + for (auto& tracker : removal_trackers_) + tracker->ReportFrameEnd(args, main_args); + DestroyTrackers(); +} + +void FrameSequenceTrackerCollection::NotifyFramePresented( + uint32_t frame_token, + const gfx::PresentationFeedback& feedback) { + for (auto& tracker : frame_trackers_) + tracker.second->ReportFramePresented(frame_token, feedback); + for (auto& tracker : custom_frame_trackers_) + tracker.second->ReportFramePresented(frame_token, feedback); + + for (auto& tracker : removal_trackers_) + tracker->ReportFramePresented(frame_token, feedback); + + for (auto& tracker : removal_trackers_) { + if (tracker->termination_status() == + FrameSequenceTracker::TerminationStatus::kReadyForTermination) { + // The tracker is ready to be terminated. + // For non kCustom typed trackers, take the metrics from the tracker. + // merge with any outstanding metrics from previous trackers of the same + // type. If there are enough frames to report the metrics, then report the + // metrics and destroy it. Otherwise, retain it to be merged with + // follow-up sequences. + // For kCustom typed trackers, |metrics| invokes AddCustomTrackerResult + // on its destruction, which add its data to |custom_tracker_results_| + // to be picked up by caller. + auto metrics = tracker->TakeMetrics(); + if (metrics->type() == FrameSequenceTrackerType::kCustom) + continue; + + auto key = std::make_pair(metrics->type(), metrics->GetEffectiveThread()); + if (accumulated_metrics_.contains(key)) { + metrics->Merge(std::move(accumulated_metrics_[key])); + accumulated_metrics_.erase(key); + } + + if (metrics->HasEnoughDataForReporting()) { + if (metrics->type() == FrameSequenceTrackerType::kUniversal) { + uint32_t frames_expected = metrics->impl_throughput().frames_expected; + uint32_t frames_produced = + metrics->aggregated_throughput().frames_produced; + current_universal_throughput_ = std::floor( + 100 * frames_produced / static_cast<float>(frames_expected)); + } + metrics->ReportMetrics(); + } + if (metrics->HasDataLeftForReporting()) + accumulated_metrics_[key] = std::move(metrics); + } + } + + DestroyTrackers(); +} + +void FrameSequenceTrackerCollection::DestroyTrackers() { + base::EraseIf( + removal_trackers_, + [](const std::unique_ptr<FrameSequenceTracker>& tracker) { + return tracker->termination_status() == + FrameSequenceTracker::TerminationStatus::kReadyForTermination; + }); +} + +void FrameSequenceTrackerCollection::RecreateTrackers( + const viz::BeginFrameArgs& args) { + std::vector<FrameSequenceTrackerType> recreate_trackers; + for (const auto& tracker : frame_trackers_) { + if (tracker.second->ShouldReportMetricsNow(args)) + recreate_trackers.push_back(tracker.first); + } + + for (const auto& tracker_type : recreate_trackers) { + // StopSequence put the tracker in the |removal_trackers_|, which will + // report its throughput data when its frame is presented. + StopSequence(tracker_type); + // The frame sequence is still active, so create a new tracker to keep + // tracking this sequence. + StartSequence(tracker_type); + } +} + +ActiveFrameSequenceTrackers +FrameSequenceTrackerCollection::FrameSequenceTrackerActiveTypes() { + ActiveFrameSequenceTrackers encoded_types = 0; + for (const auto& tracker : frame_trackers_) { + encoded_types |= static_cast<ActiveFrameSequenceTrackers>( + 1 << static_cast<unsigned>(tracker.first)); + } + return encoded_types; +} + +CustomTrackerResults +FrameSequenceTrackerCollection::TakeCustomTrackerResults() { + CustomTrackerResults results; + results.swap(custom_tracker_results_); + return results; +} + +FrameSequenceTracker* FrameSequenceTrackerCollection::GetTrackerForTesting( + FrameSequenceTrackerType type) { + if (!frame_trackers_.contains(type)) + return nullptr; + return frame_trackers_[type].get(); +} + +FrameSequenceTracker* +FrameSequenceTrackerCollection::GetRemovalTrackerForTesting( + FrameSequenceTrackerType type) { + for (const auto& tracker : removal_trackers_) + if (tracker->type() == type) + return tracker.get(); + return nullptr; +} + +void FrameSequenceTrackerCollection::SetUkmManager(UkmManager* manager) { + DCHECK(frame_trackers_.empty()); + if (manager) + throughput_ukm_reporter_ = std::make_unique<ThroughputUkmReporter>(manager); + else + throughput_ukm_reporter_ = nullptr; +} + +void FrameSequenceTrackerCollection::AddCustomTrackerResult( + int custom_sequence_id, + FrameSequenceMetrics::ThroughputData throughput_data) { + // |custom_tracker_results_| should be picked up timely. + DCHECK_LT(custom_tracker_results_.size(), 500u); + custom_tracker_results_[custom_sequence_id] = std::move(throughput_data); +} + +} // namespace cc diff --git a/chromium/cc/metrics/frame_sequence_tracker_collection.h b/chromium/cc/metrics/frame_sequence_tracker_collection.h new file mode 100644 index 00000000000..9eaf1efb6b2 --- /dev/null +++ b/chromium/cc/metrics/frame_sequence_tracker_collection.h @@ -0,0 +1,156 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CC_METRICS_FRAME_SEQUENCE_TRACKER_COLLECTION_H_ +#define CC_METRICS_FRAME_SEQUENCE_TRACKER_COLLECTION_H_ + +#include <memory> + +#include "base/containers/flat_map.h" +#include "base/optional.h" +#include "cc/cc_export.h" +#include "cc/metrics/frame_sequence_metrics.h" + +namespace gfx { +struct PresentationFeedback; +} + +namespace viz { +struct BeginFrameAck; +struct BeginFrameArgs; +} // namespace viz + +namespace cc { +class FrameSequenceTracker; +class CompositorFrameReportingController; +class ThroughputUkmReporter; +class UkmManager; + +// Map of kCustom tracker results keyed by a sequence id. +using CustomTrackerResults = + base::flat_map<int, FrameSequenceMetrics::ThroughputData>; + +typedef uint16_t ActiveFrameSequenceTrackers; + +// Used for notifying attached FrameSequenceTracker's of begin-frames and +// submitted frames. +class CC_EXPORT FrameSequenceTrackerCollection { + public: + FrameSequenceTrackerCollection( + bool is_single_threaded, + CompositorFrameReportingController* frame_reporting_controller); + ~FrameSequenceTrackerCollection(); + + FrameSequenceTrackerCollection(const FrameSequenceTrackerCollection&) = + delete; + FrameSequenceTrackerCollection& operator=( + const FrameSequenceTrackerCollection&) = delete; + + // Creates a tracker for the specified sequence-type. + FrameSequenceMetrics* StartSequence(FrameSequenceTrackerType type); + + // Schedules |tracker| for destruction. This is preferred instead of outright + // desrtruction of the tracker, since this ensures that the actual tracker + // instance is destroyed *after* the presentation-feedbacks have been received + // for all submitted frames. + void StopSequence(FrameSequenceTrackerType type); + + // Creates a kCustom tracker for the given sequence id. It is an error and + // DCHECKs if there is already a tracker associated with the sequence id. + void StartCustomSequence(int sequence_id); + + // Schedules the kCustom tracker representing |sequence_id| for destruction. + // It is a no-op if there is no tracker associated with the sequence id. + // Similar to StopSequence above, the tracker instance is destroyed *after* + // the presentation feedbacks have been received for all submitted frames. + void StopCustomSequence(int sequence_id); + + // Removes all trackers. This also immediately destroys all trackers that had + // been scheduled for destruction, even if there are pending + // presentation-feedbacks. This is typically used if the client no longer + // expects to receive presentation-feedbacks for the previously submitted + // frames (e.g. when the gpu process dies). + void ClearAll(); + + // Notifies all trackers of various events. + void NotifyBeginImplFrame(const viz::BeginFrameArgs& args); + void NotifyBeginMainFrame(const viz::BeginFrameArgs& args); + void NotifyMainFrameProcessed(const viz::BeginFrameArgs& args); + void NotifyImplFrameCausedNoDamage(const viz::BeginFrameAck& ack); + void NotifyMainFrameCausedNoDamage(const viz::BeginFrameArgs& args); + void NotifyPauseFrameProduction(); + void NotifySubmitFrame(uint32_t frame_token, + bool has_missing_content, + const viz::BeginFrameAck& ack, + const viz::BeginFrameArgs& origin_args); + void NotifyFrameEnd(const viz::BeginFrameArgs& args, + const viz::BeginFrameArgs& main_args); + + // Note that this notifies the trackers of the presentation-feedbacks, and + // destroys any tracker that had been scheduled for destruction (using + // |ScheduleRemoval()|) if it has no more pending frames. Data from non + // kCustom typed trackers are reported to UMA. Data from kCustom typed + // trackers are added to |custom_tracker_results_| for caller to pick up. + void NotifyFramePresented(uint32_t frame_token, + const gfx::PresentationFeedback& feedback); + + // Return the type of each active frame tracker, encoded into a 16 bit + // integer with the bit at each position corresponding to the enum value of + // each type. + ActiveFrameSequenceTrackers FrameSequenceTrackerActiveTypes(); + + // Reports the accumulated kCustom tracker results and clears it. + CustomTrackerResults TakeCustomTrackerResults(); + + FrameSequenceTracker* GetTrackerForTesting(FrameSequenceTrackerType type); + FrameSequenceTracker* GetRemovalTrackerForTesting( + FrameSequenceTrackerType type); + + void SetUkmManager(UkmManager* manager); + + base::Optional<int> current_universal_throughput() { + return current_universal_throughput_; + } + + private: + friend class FrameSequenceTrackerTest; + + void RecreateTrackers(const viz::BeginFrameArgs& args); + // Destroy the trackers that are ready to be terminated. + void DestroyTrackers(); + + // Adds collected metrics data for |custom_sequence_id| to be picked up via + // TakeCustomTrackerResults() below. + void AddCustomTrackerResult( + int custom_sequence_id, + FrameSequenceMetrics::ThroughputData throughput_data); + + const bool is_single_threaded_; + // The callsite can use the type to manipulate the tracker. + base::flat_map<FrameSequenceTrackerType, + std::unique_ptr<FrameSequenceTracker>> + frame_trackers_; + + // Custom trackers are keyed by a custom sequence id. + base::flat_map<int, std::unique_ptr<FrameSequenceTracker>> + custom_frame_trackers_; + CustomTrackerResults custom_tracker_results_; + + std::vector<std::unique_ptr<FrameSequenceTracker>> removal_trackers_; + CompositorFrameReportingController* const + compositor_frame_reporting_controller_; + + // The reporter takes throughput data and connect to UkmManager to report it. + std::unique_ptr<ThroughputUkmReporter> throughput_ukm_reporter_; + + base::flat_map< + std::pair<FrameSequenceTrackerType, FrameSequenceMetrics::ThreadType>, + std::unique_ptr<FrameSequenceMetrics>> + accumulated_metrics_; + base::Optional<int> current_universal_throughput_; +}; + +} // namespace cc + +#endif // CC_METRICS_FRAME_SEQUENCE_TRACKER_COLLECTION_H_ diff --git a/chromium/cc/metrics/frame_sequence_tracker_unittest.cc b/chromium/cc/metrics/frame_sequence_tracker_unittest.cc index 8e6e977aec0..2a6b8cd2b82 100644 --- a/chromium/cc/metrics/frame_sequence_tracker_unittest.cc +++ b/chromium/cc/metrics/frame_sequence_tracker_unittest.cc @@ -4,11 +4,10 @@ #include "cc/metrics/frame_sequence_tracker.h" -#include <vector> - #include "base/macros.h" #include "base/test/metrics/histogram_tester.h" #include "cc/metrics/compositor_frame_reporting_controller.h" +#include "cc/metrics/frame_sequence_tracker_collection.h" #include "components/viz/common/frame_sinks/begin_frame_args.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -107,7 +106,7 @@ class FrameSequenceTrackerTest : public testing::Test { bool RemovalTrackerExists(unsigned index, FrameSequenceTrackerType type) const { DCHECK_GT(collection_.removal_trackers_.size(), index); - return collection_.removal_trackers_[index]->type_ == type; + return collection_.removal_trackers_[index]->type() == type; } void GenerateSequence(const char* str) { @@ -229,7 +228,7 @@ class FrameSequenceTrackerTest : public testing::Test { void ReportMetrics() { tracker_->metrics_->ReportMetrics(); } - base::TimeDelta TimeDeltaToReort() const { + base::TimeDelta TimeDeltaToReport() const { return tracker_->time_delta_to_report_; } @@ -246,6 +245,7 @@ class FrameSequenceTrackerTest : public testing::Test { uint64_t BeginImplFrameDataPreviousSequence() const { return tracker_->begin_impl_frame_data_.previous_sequence; } + uint64_t BeginMainFrameDataPreviousSequence() const { return tracker_->begin_main_frame_data_.previous_sequence; } @@ -257,31 +257,28 @@ class FrameSequenceTrackerTest : public testing::Test { FrameSequenceMetrics::ThroughputData& ImplThroughput() const { return tracker_->impl_throughput(); } + FrameSequenceMetrics::ThroughputData& MainThroughput() const { return tracker_->main_throughput(); } + FrameSequenceMetrics::ThroughputData& AggregatedThroughput() const { return tracker_->aggregated_throughput(); } - void SetTerminationStatus(FrameSequenceTracker::TerminationStatus status) { - tracker_->termination_status_ = status; + FrameSequenceTracker::TerminationStatus GetTerminationStatus( + FrameSequenceTracker* tracker) { + return tracker->termination_status_; } - FrameSequenceTracker::TerminationStatus GetTerminationStatus() { return tracker_->termination_status_; } - FrameSequenceTracker::TerminationStatus GetTerminationStatusForTracker( - FrameSequenceTracker* tracker) { - return tracker->termination_status_; - } - - protected: - uint32_t number_of_frames_checkerboarded() const { + uint32_t NumberOfFramesCheckerboarded() const { return tracker_->metrics_->frames_checkerboarded(); } + protected: std::unique_ptr<CompositorFrameReportingController> compositor_frame_reporting_controller_; FrameSequenceTrackerCollection collection_; @@ -383,7 +380,7 @@ TEST_F(FrameSequenceTrackerTest, CheckerboardingSimple) { gfx::PresentationFeedback(base::TimeTicks::Now() + interval, interval, 0); collection_.NotifyFramePresented(frame_token, feedback); - EXPECT_EQ(1u, number_of_frames_checkerboarded()); + EXPECT_EQ(1u, NumberOfFramesCheckerboarded()); } // Present a single frame with checkerboarding, followed by a non-checkerboard @@ -414,7 +411,7 @@ TEST_F(FrameSequenceTrackerTest, CheckerboardingMultipleFrames) { interval, 0); collection_.NotifyFramePresented(frame_token, feedback); - EXPECT_EQ(3u, number_of_frames_checkerboarded()); + EXPECT_EQ(3u, NumberOfFramesCheckerboarded()); } // Present multiple checkerboarded frames, followed by a non-checkerboard @@ -452,7 +449,7 @@ TEST_F(FrameSequenceTrackerTest, MultipleCheckerboardingFrames) { gfx::PresentationFeedback feedback(present_now, interval, 0); collection_.NotifyFramePresented(frame_token, feedback); - EXPECT_EQ(kFrames, number_of_frames_checkerboarded()); + EXPECT_EQ(kFrames, NumberOfFramesCheckerboarded()); } TEST_F(FrameSequenceTrackerTest, ReportMetrics) { @@ -533,7 +530,7 @@ TEST_F(FrameSequenceTrackerTest, ReportMetricsAtFixedInterval) { // Now args.frame_time is 5s since the tracker creation time, so this tracker // should be scheduled to report its throughput. args = CreateBeginFrameArgs(source, ++sequence, - args.frame_time + TimeDeltaToReort()); + args.frame_time + TimeDeltaToReport()); collection_.NotifyBeginImplFrame(args); collection_.NotifyImplFrameCausedNoDamage(viz::BeginFrameAck(args, false)); collection_.NotifyFrameEnd(args, args); @@ -1076,13 +1073,13 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame1) { FrameSequenceTracker* removal_tracker = collection_.GetRemovalTrackerForTesting( FrameSequenceTrackerType::kTouchScroll); - EXPECT_EQ(GetTerminationStatusForTracker(removal_tracker), + EXPECT_EQ(GetTerminationStatus(removal_tracker), FrameSequenceTracker::TerminationStatus::kScheduledForTermination); GenerateSequence("P(1)"); // There is still one impl-frame not processed not, so the tracker is not yet // ready for termination. EXPECT_EQ(NumberOfRemovalTrackers(), 1u); - EXPECT_EQ(GetTerminationStatusForTracker(removal_tracker), + EXPECT_EQ(GetTerminationStatus(removal_tracker), FrameSequenceTracker::TerminationStatus::kScheduledForTermination); } @@ -1100,7 +1097,7 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame2) { FrameSequenceTracker* removal_tracker = collection_.GetRemovalTrackerForTesting( FrameSequenceTrackerType::kTouchScroll); - EXPECT_EQ(GetTerminationStatusForTracker(removal_tracker), + EXPECT_EQ(GetTerminationStatus(removal_tracker), FrameSequenceTracker::TerminationStatus::kScheduledForTermination); GenerateSequence("s(1)e(1,0)P(1)"); // Now the |removal_tracker| should have been destroyed. @@ -1128,7 +1125,7 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame3) { FrameSequenceTracker* removal_tracker = collection_.GetRemovalTrackerForTesting( FrameSequenceTrackerType::kTouchScroll); - EXPECT_EQ(GetTerminationStatusForTracker(removal_tracker), + EXPECT_EQ(GetTerminationStatus(removal_tracker), FrameSequenceTracker::TerminationStatus::kScheduledForTermination); GenerateSequence("e(1,0)P(1)"); // Now the |removal_tracker| should have been destroyed. @@ -1156,7 +1153,7 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame4) { FrameSequenceTracker* removal_tracker = collection_.GetRemovalTrackerForTesting( FrameSequenceTrackerType::kTouchScroll); - EXPECT_EQ(GetTerminationStatusForTracker(removal_tracker), + EXPECT_EQ(GetTerminationStatus(removal_tracker), FrameSequenceTracker::TerminationStatus::kScheduledForTermination); GenerateSequence("P(1)"); // Now the |removal_tracker| should have been destroyed. @@ -1182,11 +1179,11 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame5) { FrameSequenceTracker* removal_tracker = collection_.GetRemovalTrackerForTesting( FrameSequenceTrackerType::kTouchScroll); - EXPECT_EQ(GetTerminationStatusForTracker(removal_tracker), + EXPECT_EQ(GetTerminationStatus(removal_tracker), FrameSequenceTracker::TerminationStatus::kScheduledForTermination); GenerateSequence("s(1)P(1)"); EXPECT_EQ(NumberOfRemovalTrackers(), 1u); - EXPECT_EQ(GetTerminationStatusForTracker(removal_tracker), + EXPECT_EQ(GetTerminationStatus(removal_tracker), FrameSequenceTracker::TerminationStatus::kScheduledForTermination); } @@ -1197,11 +1194,11 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame6) { FrameSequenceTracker* removal_tracker = collection_.GetRemovalTrackerForTesting( FrameSequenceTrackerType::kTouchScroll); - EXPECT_EQ(GetTerminationStatusForTracker(removal_tracker), + EXPECT_EQ(GetTerminationStatus(removal_tracker), FrameSequenceTracker::TerminationStatus::kScheduledForTermination); GenerateSequence("P(1)"); EXPECT_EQ(NumberOfRemovalTrackers(), 1u); - EXPECT_EQ(GetTerminationStatusForTracker(removal_tracker), + EXPECT_EQ(GetTerminationStatus(removal_tracker), FrameSequenceTracker::TerminationStatus::kScheduledForTermination); } @@ -1225,7 +1222,7 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame7) { FrameSequenceTracker* removal_tracker = collection_.GetRemovalTrackerForTesting( FrameSequenceTrackerType::kTouchScroll); - EXPECT_EQ(GetTerminationStatusForTracker(removal_tracker), + EXPECT_EQ(GetTerminationStatus(removal_tracker), FrameSequenceTracker::TerminationStatus::kScheduledForTermination); GenerateSequence("n(2)e(2,0)P(1)"); // Now the |removal_tracker| should have been destroyed. @@ -1253,7 +1250,7 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame8) { FrameSequenceTracker* removal_tracker = collection_.GetRemovalTrackerForTesting( FrameSequenceTrackerType::kTouchScroll); - EXPECT_EQ(GetTerminationStatusForTracker(removal_tracker), + EXPECT_EQ(GetTerminationStatus(removal_tracker), FrameSequenceTracker::TerminationStatus::kScheduledForTermination); GenerateSequence("e(2,0)P(1)"); // Now the |removal_tracker| should have been destroyed. @@ -1281,7 +1278,7 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame9) { FrameSequenceTracker* removal_tracker = collection_.GetRemovalTrackerForTesting( FrameSequenceTrackerType::kTouchScroll); - EXPECT_EQ(GetTerminationStatusForTracker(removal_tracker), + EXPECT_EQ(GetTerminationStatus(removal_tracker), FrameSequenceTracker::TerminationStatus::kScheduledForTermination); GenerateSequence("P(1)"); // Now the |removal_tracker| should have been destroyed. @@ -1304,11 +1301,11 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame10) { FrameSequenceTracker* removal_tracker = collection_.GetRemovalTrackerForTesting( FrameSequenceTrackerType::kTouchScroll); - EXPECT_EQ(GetTerminationStatusForTracker(removal_tracker), + EXPECT_EQ(GetTerminationStatus(removal_tracker), FrameSequenceTracker::TerminationStatus::kScheduledForTermination); GenerateSequence("n(2)P(1)"); EXPECT_EQ(NumberOfRemovalTrackers(), 1u); - EXPECT_EQ(GetTerminationStatusForTracker(removal_tracker), + EXPECT_EQ(GetTerminationStatus(removal_tracker), FrameSequenceTracker::TerminationStatus::kScheduledForTermination); } @@ -1319,11 +1316,11 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame11) { FrameSequenceTracker* removal_tracker = collection_.GetRemovalTrackerForTesting( FrameSequenceTrackerType::kTouchScroll); - EXPECT_EQ(GetTerminationStatusForTracker(removal_tracker), + EXPECT_EQ(GetTerminationStatus(removal_tracker), FrameSequenceTracker::TerminationStatus::kScheduledForTermination); GenerateSequence("P(1)"); EXPECT_EQ(NumberOfRemovalTrackers(), 1u); - EXPECT_EQ(GetTerminationStatusForTracker(removal_tracker), + EXPECT_EQ(GetTerminationStatus(removal_tracker), FrameSequenceTracker::TerminationStatus::kScheduledForTermination); } @@ -1342,7 +1339,7 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame12) { FrameSequenceTracker* removal_tracker = collection_.GetRemovalTrackerForTesting( FrameSequenceTrackerType::kTouchScroll); - EXPECT_EQ(GetTerminationStatusForTracker(removal_tracker), + EXPECT_EQ(GetTerminationStatus(removal_tracker), FrameSequenceTracker::TerminationStatus::kScheduledForTermination); GenerateSequence("n(2)e(2,0)"); // Now the |removal_tracker| should have been destroyed. @@ -1370,7 +1367,7 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame13) { FrameSequenceTracker* removal_tracker = collection_.GetRemovalTrackerForTesting( FrameSequenceTrackerType::kTouchScroll); - EXPECT_EQ(GetTerminationStatusForTracker(removal_tracker), + EXPECT_EQ(GetTerminationStatus(removal_tracker), FrameSequenceTracker::TerminationStatus::kScheduledForTermination); GenerateSequence("e(2,0)"); // Now the |removal_tracker| should have been destroyed. @@ -1426,7 +1423,7 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame15) { FrameSequenceTracker* removal_tracker = collection_.GetRemovalTrackerForTesting( FrameSequenceTrackerType::kTouchScroll); - EXPECT_EQ(GetTerminationStatusForTracker(removal_tracker), + EXPECT_EQ(GetTerminationStatus(removal_tracker), FrameSequenceTracker::TerminationStatus::kScheduledForTermination); GenerateSequence("s(1)e(1,0)b(2)s(2)e(2,0)P(1)"); // Now the |removal_tracker| should have been destroyed. @@ -1454,7 +1451,7 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame16) { FrameSequenceTracker* removal_tracker = collection_.GetRemovalTrackerForTesting( FrameSequenceTrackerType::kTouchScroll); - EXPECT_EQ(GetTerminationStatusForTracker(removal_tracker), + EXPECT_EQ(GetTerminationStatus(removal_tracker), FrameSequenceTracker::TerminationStatus::kScheduledForTermination); GenerateSequence("e(1,0)b(2)s(2)e(2,0)P(1)"); // Now the |removal_tracker| should have been destroyed. @@ -1482,7 +1479,7 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame17) { FrameSequenceTracker* removal_tracker = collection_.GetRemovalTrackerForTesting( FrameSequenceTrackerType::kTouchScroll); - EXPECT_EQ(GetTerminationStatusForTracker(removal_tracker), + EXPECT_EQ(GetTerminationStatus(removal_tracker), FrameSequenceTracker::TerminationStatus::kScheduledForTermination); GenerateSequence("b(2)s(2)e(2,0)P(1)"); // Now the |removal_tracker| should have been destroyed. @@ -1511,7 +1508,7 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame18) { FrameSequenceTracker* removal_tracker = collection_.GetRemovalTrackerForTesting( FrameSequenceTrackerType::kTouchScroll); - EXPECT_EQ(GetTerminationStatusForTracker(removal_tracker), + EXPECT_EQ(GetTerminationStatus(removal_tracker), FrameSequenceTracker::TerminationStatus::kScheduledForTermination); GenerateSequence("s(1)e(1,0)b(2)n(2)e(2,0)P(1)"); // Now the |removal_tracker| should have been destroyed. @@ -1539,7 +1536,7 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame19) { FrameSequenceTracker* removal_tracker = collection_.GetRemovalTrackerForTesting( FrameSequenceTrackerType::kTouchScroll); - EXPECT_EQ(GetTerminationStatusForTracker(removal_tracker), + EXPECT_EQ(GetTerminationStatus(removal_tracker), FrameSequenceTracker::TerminationStatus::kScheduledForTermination); GenerateSequence("e(1,0)b(2)n(2)e(2,0)P(1)"); // Now the |removal_tracker| should have been destroyed. @@ -1567,7 +1564,7 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame20) { FrameSequenceTracker* removal_tracker = collection_.GetRemovalTrackerForTesting( FrameSequenceTrackerType::kTouchScroll); - EXPECT_EQ(GetTerminationStatusForTracker(removal_tracker), + EXPECT_EQ(GetTerminationStatus(removal_tracker), FrameSequenceTracker::TerminationStatus::kScheduledForTermination); GenerateSequence("b(2)n(2)e(2,0)P(1)"); // Now the |removal_tracker| should have been destroyed. @@ -1597,7 +1594,7 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame21) { FrameSequenceTracker* removal_tracker = collection_.GetRemovalTrackerForTesting( FrameSequenceTrackerType::kTouchScroll); - EXPECT_EQ(GetTerminationStatusForTracker(removal_tracker), + EXPECT_EQ(GetTerminationStatus(removal_tracker), FrameSequenceTracker::TerminationStatus::kScheduledForTermination); GenerateSequence("n(1)e(1,0)"); // Ensure that this tracker is actually removed from the |removal_trackers_| @@ -1622,7 +1619,7 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame22) { FrameSequenceTracker* removal_tracker = collection_.GetRemovalTrackerForTesting( FrameSequenceTrackerType::kTouchScroll); - EXPECT_EQ(GetTerminationStatusForTracker(removal_tracker), + EXPECT_EQ(GetTerminationStatus(removal_tracker), FrameSequenceTracker::TerminationStatus::kScheduledForTermination); GenerateSequence("e(1,0)"); // Ensure that this tracker is actually removed from the |removal_trackers_| @@ -1663,27 +1660,15 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame24) { } TEST_F(FrameSequenceTrackerTest, IgnoredFrameTokensRemovedAtPresentation1) { - GenerateSequence("b(5)"); + GenerateSequence("b(5)s(1)e(5,0)P(1)"); auto args = CreateBeginFrameArgs(/*source_id=*/1u, 1u); // Ack to an impl frame that doesn't exist in this tracker. - collection_.NotifySubmitFrame(1, /*has_missing_content=*/false, + collection_.NotifySubmitFrame(2, /*has_missing_content=*/false, viz::BeginFrameAck(args, true), args); EXPECT_EQ(IgnoredFrameTokens().size(), 1u); - args = CreateBeginFrameArgs(1u, 5u); - collection_.NotifySubmitFrame(2, false, viz::BeginFrameAck(args, true), args); - GenerateSequence("e(5,0)b(6)"); - // Ack to an impl frame that doesn't exist in this tracker. - args = CreateBeginFrameArgs(1u, 1u); - collection_.NotifySubmitFrame(3, false, viz::BeginFrameAck(args, true), args); - args = CreateBeginFrameArgs(1u, 6u); - collection_.NotifySubmitFrame(4, false, viz::BeginFrameAck(args, true), args); - EXPECT_EQ(IgnoredFrameTokens().size(), 2u); - GenerateSequence("P(2)"); - // frame_token = 2 is presented, frame_token = 3 remains in the list. - EXPECT_EQ(IgnoredFrameTokens().size(), 1u); - GenerateSequence("P(4)"); - // Now frame_token = 4 is presented, frame_token = 3 should be removed. - EXPECT_TRUE(IgnoredFrameTokens().empty()); + GenerateSequence("P(3)"); + // Any token that is < 3 should have been removed. + EXPECT_EQ(IgnoredFrameTokens().size(), 0u); } // Test the case where the frame tokens wraps around the 32-bit max value. @@ -1701,6 +1686,19 @@ TEST_F(FrameSequenceTrackerTest, IgnoredFrameTokensRemovedAtPresentation2) { EXPECT_TRUE(IgnoredFrameTokens().empty()); } +TEST_F(FrameSequenceTrackerTest, TerminationWithNullPresentationTimeStamp) { + GenerateSequence("b(1)s(1)"); + collection_.StopSequence(FrameSequenceTrackerType::kTouchScroll); + EXPECT_EQ(NumberOfRemovalTrackers(), 1u); + // Even if the presentation timestamp is null, as long as this presentation + // is acking the last impl frame, we consider that impl frame completed and + // so the tracker is ready for termination. + collection_.NotifyFramePresented( + 1, {base::TimeTicks(), viz::BeginFrameArgs::DefaultInterval(), 0}); + GenerateSequence("e(1,0)"); + EXPECT_EQ(NumberOfRemovalTrackers(), 0u); +} + // Test that a tracker is terminated after 3 submitted frames, remove this // once crbug.com/1072482 is fixed. TEST_F(FrameSequenceTrackerTest, TerminationAfterThreeSubmissions1) { @@ -1724,6 +1722,15 @@ TEST_F(FrameSequenceTrackerTest, TerminationAfterThreeSubmissions2) { EXPECT_EQ(NumberOfRemovalTrackers(), 0u); } +TEST_F(FrameSequenceTrackerTest, TerminationAfterThreeSubmissions3) { + GenerateSequence( + "b(1)s(1)e(1,0)P(1)b(2)s(2)e(2,0)P(2)b(3)s(3)e(3,0)P(3)b(4)"); + collection_.StopSequence(FrameSequenceTrackerType::kTouchScroll); + EXPECT_EQ(NumberOfRemovalTrackers(), 1u); + GenerateSequence("s(4)"); + EXPECT_EQ(NumberOfRemovalTrackers(), 1u); +} + TEST_F(FrameSequenceTrackerTest, OffScreenMainDamage1) { const char sequence[] = "b(1)B(0,1)n(1)e(1,0)b(2)E(1)B(1,2)n(2)e(2,1)b(3)E(2)B(2,3)n(3)e(3,2)"; @@ -1887,11 +1894,10 @@ TEST_F(FrameSequenceTrackerTest, CustomTrackers) { collection_.StopCustomSequence(2); EXPECT_EQ(2u, NumberOfCustomTrackers()); - // Tracker 2 should report with no data. + // Tracker 2 has no data to report. collection_.NotifyFramePresented(frame_token, {}); results = collection_.TakeCustomTrackerResults(); - EXPECT_EQ(1u, results.size()); - EXPECT_EQ(0u, results[2].frames_expected); + EXPECT_EQ(0u, results.size()); // Simple sequence of one frame. const char sequence[] = "b(1)B(0,1)s(1)S(1)e(1,0)P(1)"; diff --git a/chromium/cc/metrics/latency_ukm_reporter.cc b/chromium/cc/metrics/latency_ukm_reporter.cc index 9678edee540..072ccb31daf 100644 --- a/chromium/cc/metrics/latency_ukm_reporter.cc +++ b/chromium/cc/metrics/latency_ukm_reporter.cc @@ -12,7 +12,7 @@ namespace cc { void LatencyUkmReporter::ReportLatencyUkm( CompositorFrameReporter::FrameReportType report_type, const std::vector<CompositorFrameReporter::StageData>& stage_history, - const base::flat_set<FrameSequenceTrackerType>* active_trackers, + const CompositorFrameReporter::ActiveTrackers& active_trackers, const viz::FrameTimingDetails& viz_breakdown) { if (!ukm_manager_) return; diff --git a/chromium/cc/metrics/latency_ukm_reporter.h b/chromium/cc/metrics/latency_ukm_reporter.h index 9bbc810cec4..3ee27823a4b 100644 --- a/chromium/cc/metrics/latency_ukm_reporter.h +++ b/chromium/cc/metrics/latency_ukm_reporter.h @@ -23,7 +23,7 @@ class CC_EXPORT LatencyUkmReporter { void ReportLatencyUkm( CompositorFrameReporter::FrameReportType report_type, const std::vector<CompositorFrameReporter::StageData>& stage_history, - const base::flat_set<FrameSequenceTrackerType>* active_trackers, + const CompositorFrameReporter::ActiveTrackers& active_trackers, const viz::FrameTimingDetails& viz_breakdown); void SetUkmManager(UkmManager* manager) { ukm_manager_ = manager; } diff --git a/chromium/cc/metrics/lcd_text_metrics_reporter.cc b/chromium/cc/metrics/lcd_text_metrics_reporter.cc new file mode 100644 index 00000000000..023651ce2c7 --- /dev/null +++ b/chromium/cc/metrics/lcd_text_metrics_reporter.cc @@ -0,0 +1,102 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/metrics/lcd_text_metrics_reporter.h" + +#include "base/lazy_instance.h" +#include "base/metrics/histogram_macros.h" +#include "cc/base/histograms.h" +#include "cc/layers/picture_layer_impl.h" +#include "cc/paint/display_item_list.h" +#include "cc/trees/layer_tree_host_impl.h" +#include "cc/trees/layer_tree_impl.h" + +namespace cc { + +namespace { + +constexpr auto kMinimumTimeInterval = base::TimeDelta::FromMinutes(1); +constexpr unsigned kMinimumFrameInterval = 500; + +// This must be the same as that used in DeviceScaleEnsuresTextQuality() in +// content/renderer/render_widget.cc. +constexpr float kHighDPIDeviceScaleFactorThreshold = 1.5f; +constexpr char kMetricNameLCDTextKPixelsHighDPI[] = + "Compositing.Renderer.LCDTextDisallowedReasonKPixels.HighDPI"; +constexpr char kMetricNameLCDTextKPixelsLowDPI[] = + "Compositing.Renderer.LCDTextDisallowedReasonKPixels.LowDPI"; +constexpr char kMetricNameLCDTextLayersHighDPI[] = + "Compositing.Renderer.LCDTextDisallowedReasonLayers.HighDPI"; +constexpr char kMetricNameLCDTextLayersLowDPI[] = + "Compositing.Renderer.LCDTextDisallowedReasonLayers.LowDPI"; + +} // anonymous namespace + +std::unique_ptr<LCDTextMetricsReporter> LCDTextMetricsReporter::CreateIfNeeded( + const LayerTreeHostImpl* layer_tree_host_impl) { + const char* client_name = GetClientNameForMetrics(); + // The metrics are for the renderer only. + if (!client_name || strcmp(client_name, "Renderer") != 0) + return nullptr; + return base::WrapUnique(new LCDTextMetricsReporter(layer_tree_host_impl)); +} + +LCDTextMetricsReporter::LCDTextMetricsReporter( + const LayerTreeHostImpl* layer_tree_host_impl) + : layer_tree_host_impl_(layer_tree_host_impl) {} + +LCDTextMetricsReporter::~LCDTextMetricsReporter() = default; + +void LCDTextMetricsReporter::NotifySubmitFrame( + const viz::BeginFrameArgs& args) { + current_frame_time_ = args.frame_time; + frame_count_since_last_report_++; + if (last_report_frame_time_.is_null()) + last_report_frame_time_ = current_frame_time_; +} + +void LCDTextMetricsReporter::NotifyPauseFrameProduction() { + if (current_frame_time_.is_null() || + current_frame_time_ - last_report_frame_time_ < kMinimumTimeInterval || + frame_count_since_last_report_ < kMinimumFrameInterval) { + return; + } + + last_report_frame_time_ = current_frame_time_; + frame_count_since_last_report_ = 0; + + float device_scale_factor = + layer_tree_host_impl_->settings().use_painted_device_scale_factor + ? layer_tree_host_impl_->active_tree()->painted_device_scale_factor() + : layer_tree_host_impl_->active_tree()->device_scale_factor(); + bool is_high_dpi = device_scale_factor >= kHighDPIDeviceScaleFactorThreshold; + + for (const auto* layer : + layer_tree_host_impl_->active_tree()->picture_layers()) { + if (!layer->DrawsContent() || !layer->GetRasterSource()) + continue; + const scoped_refptr<DisplayItemList>& display_item_list = + layer->GetRasterSource()->GetDisplayItemList(); + if (!display_item_list) + continue; + + int text_pixels = static_cast<int>( + display_item_list->AreaOfDrawText(layer->visible_layer_rect())); + if (!text_pixels) + continue; + + auto reason = layer->lcd_text_disallowed_reason(); + if (is_high_dpi) { + UMA_HISTOGRAM_SCALED_ENUMERATION(kMetricNameLCDTextKPixelsHighDPI, reason, + text_pixels, 1000); + UMA_HISTOGRAM_ENUMERATION(kMetricNameLCDTextLayersHighDPI, reason); + } else { + UMA_HISTOGRAM_SCALED_ENUMERATION(kMetricNameLCDTextKPixelsLowDPI, reason, + text_pixels, 1000); + UMA_HISTOGRAM_ENUMERATION(kMetricNameLCDTextLayersLowDPI, reason); + } + } +} + +} // namespace cc diff --git a/chromium/cc/metrics/lcd_text_metrics_reporter.h b/chromium/cc/metrics/lcd_text_metrics_reporter.h new file mode 100644 index 00000000000..b2aede915a5 --- /dev/null +++ b/chromium/cc/metrics/lcd_text_metrics_reporter.h @@ -0,0 +1,47 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CC_METRICS_LCD_TEXT_METRICS_REPORTER_H_ +#define CC_METRICS_LCD_TEXT_METRICS_REPORTER_H_ + +#include <cstdint> +#include <memory> + +#include "base/time/time.h" +#include "cc/cc_export.h" + +namespace viz { +struct BeginFrameArgs; +} + +namespace cc { + +class LayerTreeHostImpl; + +// See tools/metrics/histograms/histograms.xml for description of the metrics +// (names listed in .cc). +class CC_EXPORT LCDTextMetricsReporter { + public: + static std::unique_ptr<LCDTextMetricsReporter> CreateIfNeeded( + const LayerTreeHostImpl*); + ~LCDTextMetricsReporter(); + + LCDTextMetricsReporter(const LCDTextMetricsReporter&) = delete; + LCDTextMetricsReporter& operator=(const LCDTextMetricsReporter&) = delete; + + void NotifySubmitFrame(const viz::BeginFrameArgs&); + void NotifyPauseFrameProduction(); + + private: + explicit LCDTextMetricsReporter(const LayerTreeHostImpl*); + + const LayerTreeHostImpl* layer_tree_host_impl_; + base::TimeTicks last_report_frame_time_; + base::TimeTicks current_frame_time_; + uint64_t frame_count_since_last_report_ = 0; +}; + +} // namespace cc + +#endif // CC_METRICS_LCD_TEXT_METRICS_REPORTER_H_ diff --git a/chromium/cc/metrics/throughput_ukm_reporter.h b/chromium/cc/metrics/throughput_ukm_reporter.h index b544946f941..441be89f88a 100644 --- a/chromium/cc/metrics/throughput_ukm_reporter.h +++ b/chromium/cc/metrics/throughput_ukm_reporter.h @@ -7,7 +7,7 @@ #include "base/optional.h" #include "cc/cc_export.h" -#include "cc/metrics/frame_sequence_tracker.h" +#include "cc/metrics/frame_sequence_metrics.h" namespace cc { class UkmManager; 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 6d3f094e50b..b37945a42f1 100644 --- a/chromium/cc/mojo_embedder/async_layer_tree_frame_sink.cc +++ b/chromium/cc/mojo_embedder/async_layer_tree_frame_sink.cc @@ -131,6 +131,9 @@ bool AsyncLayerTreeFrameSink::BindToClient(LayerTreeFrameSinkClient* client) { if (wants_animate_only_begin_frames_) compositor_frame_sink_->SetWantsAnimateOnlyBeginFrames(); + compositor_frame_sink_ptr_->InitializeCompositorFrameSinkType( + viz::mojom::CompositorFrameSinkType::kLayerTree); + return true; } diff --git a/chromium/cc/mojom/BUILD.gn b/chromium/cc/mojom/BUILD.gn index fdafe611497..12d79257d5d 100644 --- a/chromium/cc/mojom/BUILD.gn +++ b/chromium/cc/mojom/BUILD.gn @@ -6,7 +6,10 @@ import("//mojo/public/tools/bindings/mojom.gni") mojom("mojom") { generate_java = true - sources = [ "touch_action.mojom" ] + sources = [ + "overscroll_behavior.mojom", + "touch_action.mojom", + ] public_deps = [ "//mojo/public/mojom/base" ] @@ -21,6 +24,23 @@ mojom("mojom") { traits_public_deps = [ "//cc/ipc" ] } - cpp_typemaps = [ touch_action_typemap ] - blink_cpp_typemaps = [ touch_action_typemap ] + overscroll_behavior_typemap = { + types = [ + { + mojom = "cc.mojom.OverscrollBehavior" + cpp = "::cc::OverscrollBehavior" + }, + ] + traits_headers = [ "//cc/ipc/cc_param_traits_macros.h" ] + traits_public_deps = [ "//cc/ipc" ] + } + + cpp_typemaps = [ + overscroll_behavior_typemap, + touch_action_typemap, + ] + blink_cpp_typemaps = [ + overscroll_behavior_typemap, + touch_action_typemap, + ] } diff --git a/chromium/cc/mojom/overscroll_behavior.mojom b/chromium/cc/mojom/overscroll_behavior.mojom new file mode 100644 index 00000000000..df6f7827cbc --- /dev/null +++ b/chromium/cc/mojom/overscroll_behavior.mojom @@ -0,0 +1,8 @@ +// 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. + +module cc.mojom; + +[Native] +struct OverscrollBehavior; diff --git a/chromium/cc/paint/display_item_list.cc b/chromium/cc/paint/display_item_list.cc index 9429fef1569..52a3aac9f07 100644 --- a/chromium/cc/paint/display_item_list.cc +++ b/chromium/cc/paint/display_item_list.cc @@ -31,31 +31,39 @@ bool GetCanvasClipBounds(SkCanvas* canvas, gfx::Rect* clip_bounds) { return true; } -void FillTextContent(const PaintOpBuffer* buffer, - std::vector<NodeId>* content) { +template <typename Function> +void IterateTextContent(const PaintOpBuffer* buffer, const Function& yield) { for (auto* op : PaintOpBuffer::Iterator(buffer)) { if (op->GetType() == PaintOpType::DrawTextBlob) { - content->push_back(static_cast<DrawTextBlobOp*>(op)->node_id); + yield(static_cast<DrawTextBlobOp*>(op)); } else if (op->GetType() == PaintOpType::DrawRecord) { - FillTextContent(static_cast<DrawRecordOp*>(op)->record.get(), content); + IterateTextContent(static_cast<DrawRecordOp*>(op)->record.get(), yield); } } } -void FillTextContentByOffsets(const PaintOpBuffer* buffer, - const std::vector<size_t>& offsets, - std::vector<NodeId>* content) { +template <typename Function> +void IterateTextContentByOffsets(const PaintOpBuffer* buffer, + const std::vector<size_t>& offsets, + const Function& yield) { if (!buffer) return; for (auto* op : PaintOpBuffer::OffsetIterator(buffer, &offsets)) { if (op->GetType() == PaintOpType::DrawTextBlob) { - content->push_back(static_cast<DrawTextBlobOp*>(op)->node_id); + yield(static_cast<DrawTextBlobOp*>(op)); } else if (op->GetType() == PaintOpType::DrawRecord) { - FillTextContent(static_cast<DrawRecordOp*>(op)->record.get(), content); + IterateTextContent(static_cast<DrawRecordOp*>(op)->record.get(), yield); } } } +bool RotationEquivalentToAxisFlip(const SkMatrix& matrix) { + float skew_x = matrix.getSkewX(); + float skew_y = matrix.getSkewY(); + return ((skew_x == 1.f || skew_x == -1.f) && + (skew_y == 1.f || skew_y == -1.f)); +} + } // namespace DisplayItemList::DisplayItemList(UsageHint usage_hint) @@ -63,7 +71,7 @@ DisplayItemList::DisplayItemList(UsageHint usage_hint) if (usage_hint_ == kTopLevelDisplayItemList) { visual_rects_.reserve(1024); offsets_.reserve(1024); - begin_paired_indices_.reserve(32); + paired_begin_stack_.reserve(32); } } @@ -85,7 +93,54 @@ void DisplayItemList::CaptureContent(const gfx::Rect& rect, std::vector<NodeId>* content) const { std::vector<size_t> offsets; rtree_.Search(rect, &offsets); - FillTextContentByOffsets(&paint_op_buffer_, offsets, content); + IterateTextContentByOffsets( + &paint_op_buffer_, offsets, + [content](const DrawTextBlobOp* op) { content->push_back(op->node_id); }); +} + +double DisplayItemList::AreaOfDrawText(const gfx::Rect& rect) const { + std::vector<size_t> offsets; + rtree_.Search(rect, &offsets); + double area = 0; + IterateTextContentByOffsets( + &paint_op_buffer_, offsets, [&area](const DrawTextBlobOp* op) { + // This is not fully accurate, e.g. when there is transform operations, + // but is good for statistics purpose. + SkRect bounds = op->blob->bounds(); + area += static_cast<double>(bounds.width()) * bounds.height(); + }); + return area; +} + +void DisplayItemList::EndPaintOfPairedEnd() { +#if DCHECK_IS_ON() + DCHECK(IsPainting()); + DCHECK_LT(current_range_start_, paint_op_buffer_.size()); + current_range_start_ = kNotPainting; +#endif + if (usage_hint_ == kToBeReleasedAsPaintOpBuffer) + return; + + DCHECK(paired_begin_stack_.size()); + size_t last_begin_index = paired_begin_stack_.back().first_index; + size_t last_begin_count = paired_begin_stack_.back().count; + DCHECK_GT(last_begin_count, 0u); + + // Copy the visual rect at |last_begin_index| to all indices that constitute + // the begin item. Note that because we possibly reallocate the + // |visual_rects_| buffer below, we need an actual copy instead of a const + // reference which can become dangling. + auto visual_rect = visual_rects_[last_begin_index]; + for (size_t i = 1; i < last_begin_count; ++i) + visual_rects_[i + last_begin_index] = visual_rect; + paired_begin_stack_.pop_back(); + + // Copy the visual rect of the matching begin item to the end item(s). + visual_rects_.resize(paint_op_buffer_.size(), visual_rect); + + // The block that ended needs to be included in the bounds of the enclosing + // block. + GrowCurrentBeginItemVisualRect(visual_rect); } void DisplayItemList::Finalize() { @@ -96,7 +151,7 @@ void DisplayItemList::Finalize() { DCHECK(!IsPainting()); // If this fails we had more calls to EndPaintOfPairedBegin() than // to EndPaintOfPairedEnd(). - DCHECK(begin_paired_indices_.empty()); + DCHECK(paired_begin_stack_.empty()); DCHECK_EQ(visual_rects_.size(), offsets_.size()); #endif @@ -117,7 +172,7 @@ void DisplayItemList::Finalize() { visual_rects_.shrink_to_fit(); offsets_.clear(); offsets_.shrink_to_fit(); - begin_paired_indices_.shrink_to_fit(); + paired_begin_stack_.shrink_to_fit(); } size_t DisplayItemList::BytesUsed() const { @@ -229,7 +284,7 @@ void DisplayItemList::GenerateDiscardableImagesMetadata() { void DisplayItemList::Reset() { #if DCHECK_IS_ON() DCHECK(!IsPainting()); - DCHECK(begin_paired_indices_.empty()); + DCHECK(paired_begin_stack_.empty()); #endif rtree_.Reset(); @@ -239,8 +294,8 @@ void DisplayItemList::Reset() { visual_rects_.shrink_to_fit(); offsets_.clear(); offsets_.shrink_to_fit(); - begin_paired_indices_.clear(); - begin_paired_indices_.shrink_to_fit(); + paired_begin_stack_.clear(); + paired_begin_stack_.shrink_to_fit(); has_draw_ops_ = false; } @@ -273,4 +328,114 @@ bool DisplayItemList::GetColorIfSolidInRect(const gfx::Rect& rect, return false; } +base::Optional<DisplayItemList::DirectlyCompositedImageResult> +DisplayItemList::GetDirectlyCompositedImageResult( + gfx::Size containing_layer_bounds) const { + const PaintOpBuffer* op_buffer = nullptr; + if (paint_op_buffer_.size() == 1) { + // The actual ops are wrapped in DrawRecord if they were previously + // recorded. + if (paint_op_buffer_.GetFirstOp()->GetType() == PaintOpType::DrawRecord) { + const DrawRecordOp* draw_record = + static_cast<const DrawRecordOp*>(paint_op_buffer_.GetFirstOp()); + op_buffer = draw_record->record.get(); + } else { + op_buffer = &paint_op_buffer_; + } + } else { + return base::nullopt; + } + + const DrawImageRectOp* draw_image_rect_op = nullptr; + bool transpose_image_size = false; + constexpr size_t kNumDrawImageForOrientationOps = 10; + if (op_buffer->size() == 1 && + op_buffer->GetFirstOp()->GetType() == PaintOpType::DrawImageRect) { + draw_image_rect_op = + static_cast<const DrawImageRectOp*>(op_buffer->GetFirstOp()); + } else if (op_buffer->size() < kNumDrawImageForOrientationOps) { + // Images that respect orientation will have 5 paint operations: + // (1) Save + // (2) Translate + // (3) Concat (rotation matrix) + // (4) DrawImageRect + // (5) Restore + // Detect these the paint op buffer and disqualify the layer as a directly + // composited image if any other paint op is detected. + for (auto* op : PaintOpBuffer::Iterator(op_buffer)) { + switch (op->GetType()) { + case PaintOpType::Save: + case PaintOpType::Restore: + break; + case PaintOpType::Translate: { + const TranslateOp* translate = static_cast<const TranslateOp*>(op); + if (translate->dx != 0 || translate->dy != 0) + return base::nullopt; + break; + } + case PaintOpType::Concat: { + // We only expect a single rotation. If we see another one, then this + // image won't be eligible for directly compositing. + if (transpose_image_size) + return base::nullopt; + + const ConcatOp* concat_op = static_cast<const ConcatOp*>(op); + if (concat_op->matrix.hasPerspective() || + !concat_op->matrix.preservesAxisAlignment()) + return base::nullopt; + + // If the rotation is not an axis flip, we'll need to transpose the + // width and height dimensions to account for the same transform + // applying when the layer bounds were calculated. + transpose_image_size = + RotationEquivalentToAxisFlip(concat_op->matrix); + break; + } + case PaintOpType::DrawImageRect: + if (draw_image_rect_op) + return base::nullopt; + draw_image_rect_op = static_cast<const DrawImageRectOp*>(op); + break; + default: + return base::nullopt; + } + } + } + + if (!draw_image_rect_op) + return base::nullopt; + + // The src rect must match the image size exactly, i.e. the entire image + // must be drawn. + const SkRect& src = draw_image_rect_op->src; + if (src.fLeft != 0 || src.fTop != 0 || + src.fRight != draw_image_rect_op->image.width() || + src.fBottom != draw_image_rect_op->image.height()) + return base::nullopt; + + // The DrawImageRect op's destination rect must match the layer bounds + // exactly. Note that the layer bounds have already taken into account image + // orientation so transpose the dst width/height before comparing, if + // appropriate. + const SkRect& dst = draw_image_rect_op->dst; + int dst_width = transpose_image_size ? dst.fBottom : dst.fRight; + int dst_height = transpose_image_size ? dst.fRight : dst.fBottom; + if (dst.fLeft != 0 || dst.fTop != 0 || + dst_width != containing_layer_bounds.width() || + dst_height != containing_layer_bounds.height()) + return base::nullopt; + + int width = transpose_image_size ? draw_image_rect_op->image.height() + : draw_image_rect_op->image.width(); + int height = transpose_image_size ? draw_image_rect_op->image.width() + : draw_image_rect_op->image.height(); + DirectlyCompositedImageResult result; + result.intrinsic_image_size = gfx::Size(width, height); + // Ensure the layer will use nearest neighbor when drawn by the display + // compositor, if required. + result.nearest_neighbor = + draw_image_rect_op->flags.getFilterQuality() == kNone_SkFilterQuality; + return result; +} + } // namespace cc diff --git a/chromium/cc/paint/display_item_list.h b/chromium/cc/paint/display_item_list.h index 3054d27c09b..b518d67715a 100644 --- a/chromium/cc/paint/display_item_list.h +++ b/chromium/cc/paint/display_item_list.h @@ -62,11 +62,15 @@ class CC_PAINT_EXPORT DisplayItemList void Raster(SkCanvas* canvas, ImageProvider* image_provider = nullptr) const; - // Captures the DrawTextBlobOp within |rect| and returns the associated - // NodeId in |content|. + // Captures |DrawTextBlobOp|s intersecting |rect| and returns the associated + // |NodeId|s in |content|. void CaptureContent(const gfx::Rect& rect, std::vector<NodeId>* content) const; + // Returns the approximate total area covered by |DrawTextBlobOp|s + // intersecting |rect|, used for statistics purpose. + double AreaOfDrawText(const gfx::Rect& rect) const; + void StartPaint() { #if DCHECK_IS_ON() DCHECK(!IsPainting()); @@ -124,48 +128,27 @@ class CC_PAINT_EXPORT DisplayItemList DCHECK_LT(visual_rects_.size(), paint_op_buffer_.size()); size_t count = paint_op_buffer_.size() - visual_rects_.size(); + paired_begin_stack_.push_back({visual_rects_.size(), count}); visual_rects_.resize(paint_op_buffer_.size()); - begin_paired_indices_.push_back( - std::make_pair(visual_rects_.size() - 1, count)); } - void EndPaintOfPairedEnd() { -#if DCHECK_IS_ON() - DCHECK(IsPainting()); - DCHECK_LT(current_range_start_, paint_op_buffer_.size()); - current_range_start_ = kNotPainting; -#endif - if (usage_hint_ == kToBeReleasedAsPaintOpBuffer) - return; - - DCHECK(begin_paired_indices_.size()); - size_t last_begin_index = begin_paired_indices_.back().first; - size_t last_begin_count = begin_paired_indices_.back().second; - DCHECK_GT(last_begin_count, 0u); - DCHECK_GE(last_begin_index, last_begin_count - 1); - - // Copy the visual rect at |last_begin_index| to all indices that constitute - // the begin item. Note that because we possibly reallocate the - // |visual_rects_| buffer below, we need an actual copy instead of a const - // reference which can become dangling. - auto visual_rect = visual_rects_[last_begin_index]; - for (size_t i = last_begin_index - last_begin_count + 1; - i < last_begin_index; ++i) { - visual_rects_[i] = visual_rect; - } - begin_paired_indices_.pop_back(); - - // Copy the visual rect of the matching begin item to the end item(s). - visual_rects_.resize(paint_op_buffer_.size(), visual_rect); - - // The block that ended needs to be included in the bounds of the enclosing - // block. - GrowCurrentBeginItemVisualRect(visual_rect); - } + void EndPaintOfPairedEnd(); // Called after all items are appended, to process the items. void Finalize(); + struct DirectlyCompositedImageResult { + gfx::Size intrinsic_image_size; + bool nearest_neighbor; + }; + + // If this list represents an image that should be directly composited (i.e. + // rasterized at the intrinsic size of the image), return the intrinsic size + // of the image and whether or not to use nearest neighbor filtering when + // scaling the layer. + base::Optional<DirectlyCompositedImageResult> + GetDirectlyCompositedImageResult(gfx::Size containing_layer_bounds) const; + int NumSlowPaths() const { return paint_op_buffer_.numSlowPaths(); } bool HasNonAAPaint() const { return paint_op_buffer_.HasNonAAPaint(); } @@ -200,6 +183,9 @@ class CC_PAINT_EXPORT DisplayItemList std::string ToString() const; bool has_draw_ops() const { return has_draw_ops_; } + // Ops with nested paint ops are considered as a single op. + size_t num_paint_ops() const { return paint_op_buffer_.size(); } + private: friend class DisplayItemListTest; friend gpu::raster::RasterImplementation; @@ -217,8 +203,8 @@ class CC_PAINT_EXPORT DisplayItemList // given visual rect with the begin display item's visual rect. void GrowCurrentBeginItemVisualRect(const gfx::Rect& visual_rect) { DCHECK_EQ(usage_hint_, kTopLevelDisplayItemList); - if (!begin_paired_indices_.empty()) - visual_rects_[begin_paired_indices_.back().first].Union(visual_rect); + if (!paired_begin_stack_.empty()) + visual_rects_[paired_begin_stack_.back().first_index].Union(visual_rect); } // RTree stores indices into the paint op buffer. @@ -233,11 +219,15 @@ class CC_PAINT_EXPORT DisplayItemList std::vector<gfx::Rect> visual_rects_; // Byte offsets associated with each of the ops. std::vector<size_t> offsets_; - // A stack of pairs of indices and counts. The indices are into the - // |visual_rects_| for each paired begin range that hasn't been closed. The - // counts refer to the number of visual rects in that begin sequence that end - // with the index. - std::vector<std::pair<size_t, size_t>> begin_paired_indices_; + // A stack of paired begin sequences that haven't been closed. + struct PairedBeginInfo { + // Index (into virual_rects_ and offsets_) of the first operation in the + // paired begin sequence. + size_t first_index; + // Number of operations in the paired begin sequence. + size_t count; + }; + std::vector<PairedBeginInfo> paired_begin_stack_; #if DCHECK_IS_ON() // While recording a range of ops, this is the position in the PaintOpBuffer diff --git a/chromium/cc/paint/display_item_list_unittest.cc b/chromium/cc/paint/display_item_list_unittest.cc index f15b5a63c16..a4dea57bbfb 100644 --- a/chromium/cc/paint/display_item_list_unittest.cc +++ b/chromium/cc/paint/display_item_list_unittest.cc @@ -1132,4 +1132,55 @@ TEST_F(DisplayItemListTest, TotalOpCount) { EXPECT_EQ(8u, list->TotalOpCount()); } +TEST_F(DisplayItemListTest, AreaOfDrawText) { + auto list = base::MakeRefCounted<DisplayItemList>(); + auto sub_list = base::MakeRefCounted<DisplayItemList>(); + + auto text_blob1 = SkTextBlob::MakeFromString("ABCD", SkFont()); + auto text_blob1_bounds = text_blob1->bounds(); + auto text_blob1_area = text_blob1_bounds.width() * text_blob1_bounds.height(); + auto text_blob2 = SkTextBlob::MakeFromString("EFG", SkFont()); + auto text_blob2_bounds = text_blob2->bounds(); + auto text_blob2_area = text_blob2_bounds.width() * text_blob2_bounds.height(); + + sub_list->StartPaint(); + sub_list->push<DrawRectOp>(SkRect::MakeWH(100, 200), PaintFlags()); + sub_list->push<DrawTextBlobOp>(text_blob1, 0, 0, PaintFlags()); + sub_list->EndPaintOfUnpaired(gfx::Rect()); + auto record = sub_list->ReleaseAsRecord(); + + list->StartPaint(); + list->push<SaveOp>(); + list->push<TranslateOp>(100, 100); + list->push<DrawRecordOp>(record); + list->push<RestoreOp>(); + list->EndPaintOfUnpaired(gfx::Rect(100, 100, 100, 200)); + + list->StartPaint(); + list->push<SaveOp>(); + list->push<TranslateOp>(100, 400); + list->push<DrawRecordOp>(record); + list->push<RestoreOp>(); + list->EndPaintOfUnpaired(gfx::Rect(100, 400, 100, 200)); + + list->StartPaint(); + list->push<DrawRectOp>(SkRect::MakeWH(100, 100), PaintFlags()); + list->push<DrawTextBlobOp>(text_blob2, 10, 20, PaintFlags()); + list->EndPaintOfUnpaired(gfx::Rect(0, 0, 100, 100)); + + list->StartPaint(); + list->push<DrawTextBlobOp>(text_blob2, 400, 100, PaintFlags()); + list->push<DrawRectOp>(SkRect::MakeXYWH(400, 100, 100, 100), PaintFlags()); + list->EndPaintOfUnpaired(gfx::Rect(400, 100, 100, 100)); + + list->Finalize(); + // This includes the DrawTextBlobOp in the first DrawRecordOp the the first + // direct DrawTextBlobOp. + EXPECT_EQ(static_cast<int>(text_blob1_area + text_blob2_area), + static_cast<int>(list->AreaOfDrawText(gfx::Rect(0, 0, 200, 200)))); + // This includes all DrawTextBlobOps. + EXPECT_EQ(static_cast<int>(text_blob1_area * 2 + text_blob2_area * 2), + static_cast<int>(list->AreaOfDrawText(gfx::Rect(0, 0, 500, 500)))); +} + } // namespace cc diff --git a/chromium/cc/paint/element_id.h b/chromium/cc/paint/element_id.h index b9e36acd592..a4108a13b37 100644 --- a/chromium/cc/paint/element_id.h +++ b/chromium/cc/paint/element_id.h @@ -12,7 +12,7 @@ #include <iosfwd> #include <memory> -#include "base/hash/hash.h" +#include "base/check_op.h" #include "cc/paint/paint_export.h" namespace base { diff --git a/chromium/cc/paint/image_transfer_cache_entry_unittest.cc b/chromium/cc/paint/image_transfer_cache_entry_unittest.cc index 4c03a544e4f..24a334eedbb 100644 --- a/chromium/cc/paint/image_transfer_cache_entry_unittest.cc +++ b/chromium/cc/paint/image_transfer_cache_entry_unittest.cc @@ -10,8 +10,9 @@ #include <utility> #include <vector> -#include "base/logging.h" +#include "base/check_op.h" #include "base/memory/scoped_refptr.h" +#include "base/notreached.h" #include "build/build_config.h" #include "cc/paint/image_transfer_cache_entry.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/chromium/cc/paint/oop_pixeltest.cc b/chromium/cc/paint/oop_pixeltest.cc index ea32f1f5e7b..6c9f1c1e192 100644 --- a/chromium/cc/paint/oop_pixeltest.cc +++ b/chromium/cc/paint/oop_pixeltest.cc @@ -33,6 +33,7 @@ #include "gpu/ipc/gl_in_process_context.h" #include "gpu/skia_bindings/grcontext_for_gles2_interface.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/khronos/GLES2/gl2ext.h" #include "third_party/skia/include/core/SkFontLCDConfig.h" #include "third_party/skia/include/core/SkGraphics.h" #include "third_party/skia/include/core/SkSurface.h" @@ -162,7 +163,6 @@ class OopPixelTest : public testing::Test, options.color_space, PlaybackImageProvider::Settings()); - gpu::gles2::GLES2Interface* gl = gles2_context_provider_->ContextGL(); int width = options.resource_size.width(); int height = options.resource_size.height(); @@ -209,6 +209,17 @@ class OopPixelTest : public testing::Test, EXPECT_EQ(raster_implementation->GetError(), static_cast<unsigned>(GL_NO_ERROR)); + gpu::gles2::GLES2Interface* gl = gles2_context_provider_->ContextGL(); + SkBitmap result = ReadbackMailbox(gl, mailbox, options); + gpu::SyncToken sync_token; + gl->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData()); + sii->DestroySharedImage(sync_token, mailbox); + return result; + } + + SkBitmap ReadbackMailbox(gpu::gles2::GLES2Interface* gl, + 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->CreateAndConsumeTextureCHROMIUM(mailbox.name); GLuint fbo_id; @@ -218,6 +229,8 @@ class OopPixelTest : public testing::Test, 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()); @@ -225,11 +238,7 @@ class OopPixelTest : public testing::Test, gl->DeleteTextures(1, &gl_texture_id); gl->DeleteFramebuffers(1, &fbo_id); - gpu::SyncToken sync_token; - gl->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData()); - sii->DestroySharedImage(sync_token, mailbox); - - // Swizzle rgba->bgra if needed. + // Swizzle rgba->bgra std::vector<SkPMColor> colors; colors.reserve(width * height); for (int h = 0; h < height; ++h) { @@ -242,14 +251,26 @@ class OopPixelTest : public testing::Test, SkBitmap bitmap; bitmap.allocN32Pixels(width, height); - SkPixmap pixmap(SkImageInfo::MakeN32Premul(options.resource_size.width(), - options.resource_size.height()), - colors.data(), - options.resource_size.width() * sizeof(SkColor)); + SkPixmap pixmap(SkImageInfo::MakeN32Premul(width, height), colors.data(), + width * sizeof(SkColor)); bitmap.writePixels(pixmap); return bitmap; } + gpu::Mailbox CreateMailboxSharedImage(gpu::raster::RasterInterface* ri, + gpu::SharedImageInterface* sii, + const RasterOptions& options, + viz::ResourceFormat image_format) { + 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.color_space, flags); + EXPECT_TRUE(mailbox.Verify()); + ri->WaitSyncTokenCHROMIUM(sii->GenUnverifiedSyncToken().GetConstData()); + + return mailbox; + } + SkBitmap RasterExpectedBitmap( scoped_refptr<DisplayItemList> display_item_list, const gfx::Size& playback_size) { @@ -1612,6 +1633,222 @@ TEST_F(OopPixelTest, DrawTextBlobPersistentShaderCache) { ExpectEquals(actual, expected); } +TEST_F(OopPixelTest, WritePixels) { + gfx::Size dest_size(10, 10); + RasterOptions options(dest_size); + auto* ri = raster_context_provider_->RasterInterface(); + auto* sii = raster_context_provider_->SharedImageInterface(); + gpu::Mailbox dest_mailbox = CreateMailboxSharedImage( + ri, sii, options, viz::ResourceFormat::RGBA_8888); + std::vector<SkPMColor> expected_pixels(dest_size.width() * dest_size.height(), + SkPreMultiplyARGB(255, 0, 0, 255)); + SkBitmap expected; + expected.installPixels( + 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)); + + gpu::gles2::GLES2Interface* gl = gles2_context_provider_->ContextGL(); + SkBitmap actual = ReadbackMailbox(gl, dest_mailbox, options); + gpu::SyncToken sync_token; + gl->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData()); + sii->DestroySharedImage(sync_token, dest_mailbox); + ExpectEquals(actual, expected); +} + +namespace { +void UploadPixels(gpu::gles2::GLES2Interface* gl, + 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); +} + +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); +} +} // namespace + +TEST_F(OopPixelTest, ConvertYUVToRGB) { + 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); + gpu::Mailbox y_mailbox = CreateMailboxSharedImage( + ri, sii, options, viz::ResourceFormat::LUMINANCE_8); + gpu::Mailbox u_mailbox = CreateMailboxSharedImage( + ri, sii, uv_options, viz::ResourceFormat::LUMINANCE_8); + gpu::Mailbox v_mailbox = CreateMailboxSharedImage( + ri, sii, uv_options, viz::ResourceFormat::LUMINANCE_8); + + 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, y_mailbox, options.resource_size, GL_LUMINANCE, + GL_UNSIGNED_BYTE, y_pix.get()); + UploadPixels(gl, u_mailbox, uv_options.resource_size, GL_LUMINANCE, + GL_UNSIGNED_BYTE, u_pix.get()); + UploadPixels(gl, v_mailbox, uv_options.resource_size, GL_LUMINANCE, + GL_UNSIGNED_BYTE, v_pix.get()); + gl->OrderingBarrierCHROMIUM(); + + ri->ConvertYUVMailboxesToRGB(dest_mailbox, kJPEG_SkYUVColorSpace, y_mailbox, + u_mailbox, v_mailbox); + ri->OrderingBarrierCHROMIUM(); + SkBitmap actual_bitmap = ReadbackMailbox(gl, dest_mailbox, options); + + // Create the expected result using SkImage::MakeFromYUVTextures + GrBackendTexture backend_textures[3]; + backend_textures[0] = MakeBackendTexture(gl, y_mailbox, options.resource_size, + GL_LUMINANCE8_EXT); + backend_textures[1] = MakeBackendTexture( + gl, u_mailbox, uv_options.resource_size, GL_LUMINANCE8_EXT); + backend_textures[2] = MakeBackendTexture( + gl, v_mailbox, uv_options.resource_size, GL_LUMINANCE8_EXT); + + SkYUVAIndex yuva_indices[4]; + yuva_indices[SkYUVAIndex::kY_Index] = {0, SkColorChannel::kR}; + yuva_indices[SkYUVAIndex::kU_Index] = {1, SkColorChannel::kR}; + yuva_indices[SkYUVAIndex::kV_Index] = {2, SkColorChannel::kR}; + yuva_indices[SkYUVAIndex::kA_Index] = {-1, SkColorChannel::kA}; + + auto expected_image = SkImage::MakeFromYUVATextures( + gles2_context_provider_->GrContext(), kJPEG_SkYUVColorSpace, + backend_textures, yuva_indices, + {options.resource_size.width(), options.resource_size.height()}, + kTopLeft_GrSurfaceOrigin, nullptr); + + 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); + } + + gpu::SyncToken sync_token; + gl->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData()); + sii->DestroySharedImage(sync_token, dest_mailbox); + sii->DestroySharedImage(sync_token, y_mailbox); + sii->DestroySharedImage(sync_token, u_mailbox); + sii->DestroySharedImage(sync_token, v_mailbox); +} + +// 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. +#ifndef OS_ANDROID +TEST_F(OopPixelTest, ConvertNV12ToRGB) { + 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); + gpu::Mailbox y_mailbox = CreateMailboxSharedImage( + ri, sii, options, viz::ResourceFormat::LUMINANCE_8); + gpu::Mailbox uv_mailbox = + CreateMailboxSharedImage(ri, sii, uv_options, viz::ResourceFormat::RG_88); + + 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) { + uv_pix[i] = 0xff; + uv_pix[i + 1] = 0x6d; + } + + gpu::gles2::GLES2Interface* gl = gles2_context_provider_->ContextGL(); + UploadPixels(gl, y_mailbox, options.resource_size, GL_LUMINANCE, + GL_UNSIGNED_BYTE, y_pix.get()); + UploadPixels(gl, uv_mailbox, uv_options.resource_size, GL_RG, + GL_UNSIGNED_BYTE, uv_pix.get()); + gl->OrderingBarrierCHROMIUM(); + + ri->ConvertNV12MailboxesToRGB(dest_mailbox, kJPEG_SkYUVColorSpace, y_mailbox, + uv_mailbox); + ri->OrderingBarrierCHROMIUM(); + SkBitmap actual_bitmap = ReadbackMailbox(gl, dest_mailbox, options); + + // Create the expected result using SkImage::MakeFromYUVTextures + GrBackendTexture backend_textures[2]; + backend_textures[0] = MakeBackendTexture(gl, y_mailbox, options.resource_size, + GL_LUMINANCE8_EXT); + backend_textures[1] = + MakeBackendTexture(gl, uv_mailbox, uv_options.resource_size, GL_RG8); + + SkYUVAIndex yuva_indices[4]; + yuva_indices[SkYUVAIndex::kY_Index] = {0, SkColorChannel::kR}; + yuva_indices[SkYUVAIndex::kU_Index] = {1, SkColorChannel::kR}; + yuva_indices[SkYUVAIndex::kV_Index] = {1, SkColorChannel::kG}; + yuva_indices[SkYUVAIndex::kA_Index] = {-1, SkColorChannel::kA}; + + auto expected_image = SkImage::MakeFromYUVATextures( + gles2_context_provider_->GrContext(), kJPEG_SkYUVColorSpace, + backend_textures, yuva_indices, + {options.resource_size.width(), options.resource_size.height()}, + kTopLeft_GrSurfaceOrigin, nullptr); + + 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); + } + + gpu::SyncToken sync_token; + gl->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData()); + sii->DestroySharedImage(sync_token, dest_mailbox); + sii->DestroySharedImage(sync_token, y_mailbox); + sii->DestroySharedImage(sync_token, uv_mailbox); +} +#endif // OS_ANDROID + class OopPathPixelTest : public OopPixelTest, public ::testing::WithParamInterface<bool> { public: diff --git a/chromium/cc/paint/paint_cache.cc b/chromium/cc/paint/paint_cache.cc index 35b3882be39..b75a938d19a 100644 --- a/chromium/cc/paint/paint_cache.cc +++ b/chromium/cc/paint/paint_cache.cc @@ -4,8 +4,10 @@ #include "cc/paint/paint_cache.h" +#include "base/check_op.h" #include "base/containers/flat_set.h" #include "base/no_destructor.h" +#include "base/notreached.h" #include "base/synchronization/lock.h" namespace cc { @@ -21,6 +23,8 @@ void EraseFromMap(T* map, size_t n, const volatile PaintCacheId* ids) { } // namespace +constexpr size_t ClientPaintCache::kNoCachingBudget; + ClientPaintCache::ClientPaintCache(size_t max_budget_bytes) : cache_map_(CacheMap::NO_AUTO_EVICT), max_budget_(max_budget_bytes) {} ClientPaintCache::~ClientPaintCache() = default; @@ -32,6 +36,8 @@ bool ClientPaintCache::Get(PaintCacheDataType type, PaintCacheId id) { void ClientPaintCache::Put(PaintCacheDataType type, PaintCacheId id, size_t size) { + if (max_budget_ == kNoCachingBudget) + return; auto key = std::make_pair(type, id); DCHECK(cache_map_.Peek(key) == cache_map_.end()); diff --git a/chromium/cc/paint/paint_cache.h b/chromium/cc/paint/paint_cache.h index 89f9274cf98..728df5a36f3 100644 --- a/chromium/cc/paint/paint_cache.h +++ b/chromium/cc/paint/paint_cache.h @@ -49,6 +49,11 @@ constexpr size_t PaintCacheDataTypeCount = class CC_PAINT_EXPORT ClientPaintCache { public: + // If ClientPaintCache is constructed with a max_budget_bytes of + // kNoCachingBudget, its Put() method becomes a no-op, rendering the instance + // a no-op instance. + static constexpr size_t kNoCachingBudget = 0u; + explicit ClientPaintCache(size_t max_budget_bytes); ClientPaintCache(const ClientPaintCache&) = delete; ~ClientPaintCache(); diff --git a/chromium/cc/paint/paint_filter.cc b/chromium/cc/paint/paint_filter.cc index de15aaa7e26..279258b26b7 100644 --- a/chromium/cc/paint/paint_filter.cc +++ b/chromium/cc/paint/paint_filter.cc @@ -801,8 +801,8 @@ bool MergePaintFilter::operator==(const MergePaintFilter& other) const { } MorphologyPaintFilter::MorphologyPaintFilter(MorphType morph_type, - int radius_x, - int radius_y, + float radius_x, + float radius_y, sk_sp<PaintFilter> input, const CropRect* crop_rect) : PaintFilter(kType, crop_rect, HasDiscardableImages(input)), diff --git a/chromium/cc/paint/paint_filter.h b/chromium/cc/paint/paint_filter.h index bdf724ab1e2..cec36ef7c6a 100644 --- a/chromium/cc/paint/paint_filter.h +++ b/chromium/cc/paint/paint_filter.h @@ -557,15 +557,15 @@ class CC_PAINT_EXPORT MorphologyPaintFilter final : public PaintFilter { enum class MorphType : uint32_t { kDilate, kErode, kMaxMorphType = kErode }; static constexpr Type kType = Type::kMorphology; MorphologyPaintFilter(MorphType morph_type, - int radius_x, - int radius_y, + float radius_x, + float radius_y, sk_sp<PaintFilter> input, const CropRect* crop_rect = nullptr); ~MorphologyPaintFilter() override; MorphType morph_type() const { return morph_type_; } - int radius_x() const { return radius_x_; } - int radius_y() const { return radius_y_; } + float radius_x() const { return radius_x_; } + float radius_y() const { return radius_y_; } const sk_sp<PaintFilter>& input() const { return input_; } size_t SerializedSize() const override; @@ -577,8 +577,8 @@ class CC_PAINT_EXPORT MorphologyPaintFilter final : public PaintFilter { private: MorphType morph_type_; - int radius_x_; - int radius_y_; + float radius_x_; + float radius_y_; sk_sp<PaintFilter> input_; }; diff --git a/chromium/cc/paint/paint_image.cc b/chromium/cc/paint/paint_image.cc index be183635641..86deeddd70f 100644 --- a/chromium/cc/paint/paint_image.cc +++ b/chromium/cc/paint/paint_image.cc @@ -152,6 +152,8 @@ void PaintImage::CreateSkImage() { SkImage::MakeFromGenerator(std::make_unique<SkiaPaintImageGenerator>( paint_image_generator_, kDefaultFrameIndex, kDefaultGeneratorClientId)); + } else if (texture_backing_) { + cached_sk_image_ = texture_backing_->GetAcceleratedSkImage(); } if (!subset_rect_.IsEmpty() && cached_sk_image_) { @@ -278,6 +280,14 @@ SkColorType PaintImage::GetColorType() const { return kUnknown_SkColorType; } +SkAlphaType PaintImage::GetAlphaType() const { + if (paint_image_generator_) + return paint_image_generator_->GetSkImageInfo().alphaType(); + if (GetSkImage()) + return GetSkImage()->alphaType(); + return kUnknown_SkAlphaType; +} + int PaintImage::width() const { return paint_worklet_input_ ? static_cast<int>(paint_worklet_input_->GetSize().width()) diff --git a/chromium/cc/paint/paint_image.h b/chromium/cc/paint/paint_image.h index e7d38dddcb3..95881204e0a 100644 --- a/chromium/cc/paint/paint_image.h +++ b/chromium/cc/paint/paint_image.h @@ -25,9 +25,10 @@ namespace cc { class PaintImageGenerator; class PaintOpBuffer; class PaintWorkletInput; +class TextureBacking; using PaintRecord = PaintOpBuffer; -enum class ImageType { kPNG, kJPEG, kWEBP, kGIF, kICO, kBMP, kInvalid }; +enum class ImageType { kPNG, kJPEG, kWEBP, kGIF, kICO, kBMP, kAVIF, kInvalid }; enum class YUVSubsampling { k410, k411, k420, k422, k440, k444, kUnknown }; @@ -271,8 +272,9 @@ class CC_PAINT_EXPORT PaintImage { SkYUVAIndex* plane_indices = nullptr, SkYUVColorSpace* yuv_color_space = nullptr) const; - // Returns the color type of this image. + // Get metadata associated with this image. SkColorType GetColorType() const; + SkAlphaType GetAlphaType() const; // Returns general information about the underlying image. Returns nullptr if // there is no available |paint_image_generator_|. @@ -333,6 +335,7 @@ class CC_PAINT_EXPORT PaintImage { ContentId content_id_ = kInvalidContentId; sk_sp<PaintImageGenerator> paint_image_generator_; + sk_sp<TextureBacking> texture_backing_; Id id_ = 0; AnimationType animation_type_ = AnimationType::STATIC; diff --git a/chromium/cc/paint/paint_image_builder.h b/chromium/cc/paint/paint_image_builder.h index b7b389962af..25a20bf1a9c 100644 --- a/chromium/cc/paint/paint_image_builder.h +++ b/chromium/cc/paint/paint_image_builder.h @@ -11,6 +11,7 @@ #include "cc/paint/paint_op_buffer.h" #include "cc/paint/paint_worklet_input.h" #include "cc/paint/skia_paint_image_generator.h" +#include "cc/paint/texture_backing.h" #include "third_party/skia/include/core/SkImage.h" namespace cc { @@ -104,6 +105,12 @@ class CC_PAINT_EXPORT PaintImageBuilder { paint_image_.paint_worklet_input_ = std::move(input); return std::move(*this); } + PaintImageBuilder&& set_texture_backing(sk_sp<TextureBacking> texture_backing, + PaintImage::ContentId content_id) { + paint_image_.texture_backing_ = std::move(texture_backing); + paint_image_.content_id_ = content_id; + return std::move(*this); + } PaintImage TakePaintImage(); diff --git a/chromium/cc/paint/paint_image_generator.cc b/chromium/cc/paint/paint_image_generator.cc index 740808e21e1..6ceae174db3 100644 --- a/chromium/cc/paint/paint_image_generator.cc +++ b/chromium/cc/paint/paint_image_generator.cc @@ -7,7 +7,6 @@ #include "cc/paint/paint_image_generator.h" #include "base/atomic_sequence_num.h" -#include "base/logging.h" #include "third_party/skia/include/core/SkImageInfo.h" #include "third_party/skia/include/core/SkSize.h" diff --git a/chromium/cc/paint/paint_op_buffer.cc b/chromium/cc/paint/paint_op_buffer.cc index 6417c049cde..51955bcf32f 100644 --- a/chromium/cc/paint/paint_op_buffer.cc +++ b/chromium/cc/paint/paint_op_buffer.cc @@ -1310,7 +1310,6 @@ void DrawImageRectOp::RasterWithFlags(const DrawImageRectOp* op, const PaintFlags* flags, SkCanvas* canvas, const PlaybackParams& params) { - SkPaint paint = flags ? flags->ToSkPaint() : SkPaint(); // TODO(crbug.com/931704): make sure to support the case where paint worklet // generated images are used in other raster work such as canvas2d. if (op->image.IsPaintWorklet()) { @@ -1322,6 +1321,11 @@ void DrawImageRectOp::RasterWithFlags(const DrawImageRectOp* op, ImageProvider::ScopedResult result = params.image_provider->GetRasterContent(DrawImage(op->image)); + // Check that we are not using loopers with paint worklets, since converting + // PaintFlags to SkPaint drops loopers. + DCHECK(!flags->getLooper()); + SkPaint paint = flags ? flags->ToSkPaint() : SkPaint(); + DCHECK(IsScaleAdjustmentIdentity(op->scale_adjustment)); SkAutoCanvasRestore save_restore(canvas, true); canvas->concat( @@ -1344,8 +1348,11 @@ void DrawImageRectOp::RasterWithFlags(const DrawImageRectOp* op, if (!params.image_provider) { SkRect adjusted_src = AdjustSrcRectForScale(op->src, op->scale_adjustment); - canvas->drawImageRect(op->image.GetSkImage().get(), adjusted_src, op->dst, - &paint, skconstraint); + flags->DrawToSk(canvas, [op, adjusted_src, skconstraint](SkCanvas* c, + const SkPaint& p) { + c->drawImageRect(op->image.GetSkImage().get(), adjusted_src, op->dst, &p, + skconstraint); + }); return; } @@ -1374,9 +1381,13 @@ void DrawImageRectOp::RasterWithFlags(const DrawImageRectOp* op, op->src.makeOffset(decoded_image.src_rect_offset().width(), decoded_image.src_rect_offset().height()); adjusted_src = AdjustSrcRectForScale(adjusted_src, scale_adjustment); - paint.setFilterQuality(decoded_image.filter_quality()); - canvas->drawImageRect(decoded_image.image().get(), adjusted_src, op->dst, - &paint, skconstraint); + flags->DrawToSk(canvas, [op, &decoded_image, adjusted_src, skconstraint]( + SkCanvas* c, const SkPaint& p) { + SkPaint paint_with_filter_quality(p); + paint_with_filter_quality.setFilterQuality(decoded_image.filter_quality()); + c->drawImageRect(decoded_image.image().get(), adjusted_src, op->dst, + &paint_with_filter_quality, skconstraint); + }); } void DrawIRectOp::RasterWithFlags(const DrawIRectOp* op, @@ -1992,7 +2003,6 @@ void PaintOp::Raster(SkCanvas* canvas, const PlaybackParams& params) const { size_t PaintOp::Serialize(void* memory, size_t size, const SerializeOptions& options) const { - DCHECK(options.transfer_cache); DCHECK(options.canvas); // Need at least enough room for a skip/type header. diff --git a/chromium/cc/paint/paint_op_buffer_unittest.cc b/chromium/cc/paint/paint_op_buffer_unittest.cc index f340389e168..7a2861e76a7 100644 --- a/chromium/cc/paint/paint_op_buffer_unittest.cc +++ b/chromium/cc/paint/paint_op_buffer_unittest.cc @@ -3132,6 +3132,56 @@ TEST(PaintOpBufferTest, ReplacesImagesFromProvider) { buffer.Playback(&canvas, PlaybackParams(&image_provider)); } +TEST(PaintOpBufferTest, DrawImageRectOpWithLooperNoImageProvider) { + PaintOpBuffer buffer; + PaintImage image = CreateDiscardablePaintImage(gfx::Size(100, 100)); + SkLayerDrawLooper::Builder sk_draw_looper_builder; + sk_draw_looper_builder.addLayer(20.0, 20.0); + SkLayerDrawLooper::LayerInfo info_unmodified; + sk_draw_looper_builder.addLayerOnTop(info_unmodified); + + PaintFlags paint_flags; + paint_flags.setLooper(sk_draw_looper_builder.detach()); + buffer.push<DrawImageRectOp>( + image, SkRect::MakeWH(100, 100), SkRect::MakeWH(100, 100), &paint_flags, + PaintCanvas::SrcRectConstraint::kFast_SrcRectConstraint); + + testing::StrictMock<MockCanvas> canvas; + EXPECT_CALL(canvas, willSave); + EXPECT_CALL(canvas, didTranslate); + EXPECT_CALL(canvas, willRestore); + EXPECT_CALL(canvas, onDrawImageRect).Times(2); + + buffer.Playback(&canvas, PlaybackParams(nullptr)); +} + +TEST(PaintOpBufferTest, DrawImageRectOpWithLooperWithImageProvider) { + PaintOpBuffer buffer; + PaintImage image = CreateDiscardablePaintImage(gfx::Size(100, 100)); + SkLayerDrawLooper::Builder sk_draw_looper_builder; + sk_draw_looper_builder.addLayer(20.0, 20.0); + SkLayerDrawLooper::LayerInfo info_unmodified; + sk_draw_looper_builder.addLayerOnTop(info_unmodified); + + PaintFlags paint_flags; + paint_flags.setLooper(sk_draw_looper_builder.detach()); + buffer.push<DrawImageRectOp>( + image, SkRect::MakeWH(100, 100), SkRect::MakeWH(100, 100), &paint_flags, + PaintCanvas::SrcRectConstraint::kFast_SrcRectConstraint); + + testing::StrictMock<MockCanvas> canvas; + EXPECT_CALL(canvas, willSave); + EXPECT_CALL(canvas, didTranslate); + EXPECT_CALL(canvas, willRestore); + EXPECT_CALL(canvas, onDrawImageRect).Times(2); + + std::vector<SkSize> src_rect_offset = {SkSize::MakeEmpty()}; + std::vector<SkSize> scale_adjustment = {SkSize::Make(1.0f, 1.0f)}; + std::vector<SkFilterQuality> quality = {kHigh_SkFilterQuality}; + MockImageProvider image_provider(src_rect_offset, scale_adjustment, quality); + buffer.Playback(&canvas, PlaybackParams(&image_provider)); +} + TEST(PaintOpBufferTest, ReplacesImagesFromProviderOOP) { PaintOpBuffer buffer; SkSize expected_scale = SkSize::Make(0.2f, 0.5f); @@ -3221,7 +3271,7 @@ TEST_P(PaintFilterSerializationTest, Basic) { SkMatrixConvolutionImageFilter::kClampToBlack_TileMode, true, nullptr)}, sk_sp<PaintFilter>{new MorphologyPaintFilter( - MorphologyPaintFilter::MorphType::kErode, 15, 30, nullptr)}, + MorphologyPaintFilter::MorphType::kErode, 15.5f, 30.2f, nullptr)}, sk_sp<PaintFilter>{new OffsetPaintFilter(-1.f, -2.f, nullptr)}, sk_sp<PaintFilter>{new TilePaintFilter( SkRect::MakeXYWH(1, 2, 3, 4), SkRect::MakeXYWH(4, 3, 2, 1), nullptr)}, diff --git a/chromium/cc/paint/paint_op_reader.cc b/chromium/cc/paint/paint_op_reader.cc index 078b528e6e1..5118c0f814a 100644 --- a/chromium/cc/paint/paint_op_reader.cc +++ b/chromium/cc/paint/paint_op_reader.cc @@ -1103,8 +1103,8 @@ void PaintOpReader::ReadMorphologyPaintFilter( sk_sp<PaintFilter>* filter, const base::Optional<PaintFilter::CropRect>& crop_rect) { uint32_t morph_type_int = 0; - int radius_x = 0; - int radius_y = 0; + float radius_x = 0; + float radius_y = 0; sk_sp<PaintFilter> input; Read(&morph_type_int); Read(&radius_x); diff --git a/chromium/cc/paint/paint_recorder.cc b/chromium/cc/paint/paint_recorder.cc index 1fb95143f49..cded3e21e40 100644 --- a/chromium/cc/paint/paint_recorder.cc +++ b/chromium/cc/paint/paint_recorder.cc @@ -51,4 +51,8 @@ bool PaintRecorder::ListHasDrawOps() const { return display_item_list_->has_draw_ops(); } +size_t PaintRecorder::num_paint_ops() const { + return display_item_list_->num_paint_ops(); +} + } // namespace cc diff --git a/chromium/cc/paint/paint_recorder.h b/chromium/cc/paint/paint_recorder.h index 6beb024c236..5195835342b 100644 --- a/chromium/cc/paint/paint_recorder.h +++ b/chromium/cc/paint/paint_recorder.h @@ -39,6 +39,9 @@ class CC_PAINT_EXPORT PaintRecorder { bool ListHasDrawOps() const; + // Ops with nested paint ops are considered as a single op. + size_t num_paint_ops() const; + protected: virtual std::unique_ptr<RecordPaintCanvas> CreateCanvas(DisplayItemList* list, const SkRect& bounds); diff --git a/chromium/cc/paint/shader_transfer_cache_entry.cc b/chromium/cc/paint/shader_transfer_cache_entry.cc index dc184f6e84d..fda25bd40ef 100644 --- a/chromium/cc/paint/shader_transfer_cache_entry.cc +++ b/chromium/cc/paint/shader_transfer_cache_entry.cc @@ -4,6 +4,8 @@ #include "cc/paint/shader_transfer_cache_entry.h" +#include "base/notreached.h" + namespace cc { ServiceShaderTransferCacheEntry::ServiceShaderTransferCacheEntry( diff --git a/chromium/cc/paint/texture_backing.h b/chromium/cc/paint/texture_backing.h index feadf9a194e..51055d27b29 100644 --- a/chromium/cc/paint/texture_backing.h +++ b/chromium/cc/paint/texture_backing.h @@ -10,7 +10,7 @@ #include "third_party/skia/include/core/SkImageInfo.h" namespace gpu { -class Mailbox; +struct Mailbox; } // namespace gpu namespace cc { @@ -19,6 +19,7 @@ namespace cc { // This class must be created, used and destroyed on the same thread. class CC_PAINT_EXPORT TextureBacking : public SkRefCnt { public: + TextureBacking() = default; TextureBacking(const TextureBacking&) = delete; ~TextureBacking() override = default; diff --git a/chromium/cc/paint/transfer_cache_entry.cc b/chromium/cc/paint/transfer_cache_entry.cc index 47d4ae2d76b..e192ed1cce1 100644 --- a/chromium/cc/paint/transfer_cache_entry.cc +++ b/chromium/cc/paint/transfer_cache_entry.cc @@ -6,7 +6,7 @@ #include <memory> -#include "base/logging.h" +#include "base/notreached.h" #include "cc/paint/image_transfer_cache_entry.h" #include "cc/paint/raw_memory_transfer_cache_entry.h" #include "cc/paint/shader_transfer_cache_entry.h" diff --git a/chromium/cc/paint/transfer_cache_serialize_helper.cc b/chromium/cc/paint/transfer_cache_serialize_helper.cc index 7a59ca0d197..28ca6675a26 100644 --- a/chromium/cc/paint/transfer_cache_serialize_helper.cc +++ b/chromium/cc/paint/transfer_cache_serialize_helper.cc @@ -6,7 +6,7 @@ #include <utility> -#include "base/logging.h" +#include "base/check_op.h" namespace cc { diff --git a/chromium/cc/raster/lcd_text_disallowed_reason.cc b/chromium/cc/raster/lcd_text_disallowed_reason.cc new file mode 100644 index 00000000000..2bfffcc8b62 --- /dev/null +++ b/chromium/cc/raster/lcd_text_disallowed_reason.cc @@ -0,0 +1,39 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/raster/lcd_text_disallowed_reason.h" + +#include <iostream> +#include "base/notreached.h" + +namespace cc { + +const char* LCDTextDisallowedReasonToString(LCDTextDisallowedReason reason) { + switch (reason) { + case LCDTextDisallowedReason::kNone: + return "none"; + case LCDTextDisallowedReason::kSetting: + return "setting"; + case LCDTextDisallowedReason::kBackgroundColorNotOpaque: + return "background-color-not-opaque"; + case LCDTextDisallowedReason::kContentsNotOpaque: + return "contents-not-opaque"; + case LCDTextDisallowedReason::kNonIntegralTranslation: + return "non-integral-translation"; + case LCDTextDisallowedReason::kNonIntegralXOffset: + return "non-integral-x-offset"; + case LCDTextDisallowedReason::kNonIntegralYOffset: + return "non-integral-y-offset"; + case LCDTextDisallowedReason::kWillChangeTransform: + return "will-change-transform"; + } + NOTREACHED(); + return ""; +} + +std::ostream& operator<<(std::ostream& os, LCDTextDisallowedReason reason) { + return os << LCDTextDisallowedReasonToString(reason); +} + +} // namespace cc diff --git a/chromium/cc/raster/lcd_text_disallowed_reason.h b/chromium/cc/raster/lcd_text_disallowed_reason.h new file mode 100644 index 00000000000..024b974d4ce --- /dev/null +++ b/chromium/cc/raster/lcd_text_disallowed_reason.h @@ -0,0 +1,37 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CC_RASTER_LCD_TEXT_DISALLOWED_REASON_H_ +#define CC_RASTER_LCD_TEXT_DISALLOWED_REASON_H_ + +#include <cstddef> +#include <cstdint> +#include <iosfwd> + +#include "cc/cc_export.h" + +namespace cc { + +// These values are used in UMA and benchmarks. Entries should not be renumbered +// and numeric values should never be reused. +enum class LCDTextDisallowedReason : uint8_t { + kNone = 0, + kSetting = 1, + kBackgroundColorNotOpaque = 2, + kContentsNotOpaque = 3, + kNonIntegralTranslation = 4, + kNonIntegralXOffset = 5, + kNonIntegralYOffset = 6, + kWillChangeTransform = 7, + kMaxValue = kWillChangeTransform, +}; +constexpr size_t kLCDTextDisallowedReasonCount = + static_cast<size_t>(LCDTextDisallowedReason::kMaxValue) + 1; +CC_EXPORT const char* LCDTextDisallowedReasonToString(LCDTextDisallowedReason); + +CC_EXPORT std::ostream& operator<<(std::ostream&, LCDTextDisallowedReason); + +} // namespace cc + +#endif // CC_RASTER_LCD_TEXT_DISALLOWED_REASON_H_ diff --git a/chromium/cc/raster/raster_buffer_provider_unittest.cc b/chromium/cc/raster/raster_buffer_provider_unittest.cc index 28b44791fef..bee4146a7cd 100644 --- a/chromium/cc/raster/raster_buffer_provider_unittest.cc +++ b/chromium/cc/raster/raster_buffer_provider_unittest.cc @@ -17,10 +17,11 @@ #include "base/bind.h" #include "base/bind_helpers.h" #include "base/cancelable_callback.h" +#include "base/check.h" #include "base/location.h" -#include "base/logging.h" #include "base/memory/scoped_refptr.h" #include "base/metrics/histogram_base.h" +#include "base/notreached.h" #include "base/run_loop.h" #include "base/single_thread_task_runner.h" #include "base/test/metrics/histogram_tester.h" diff --git a/chromium/cc/raster/raster_source.cc b/chromium/cc/raster/raster_source.cc index 58d7ea0b03f..99d6d88018a 100644 --- a/chromium/cc/raster/raster_source.cc +++ b/chromium/cc/raster/raster_source.cc @@ -16,11 +16,11 @@ #include "cc/paint/skia_paint_canvas.h" #include "components/viz/common/traced_value.h" #include "third_party/skia/include/core/SkCanvas.h" -#include "third_party/skia/include/core/SkPictureRecorder.h" #include "ui/gfx/geometry/axis_transform2d.h" #include "ui/gfx/geometry/rect_conversions.h" namespace cc { + RasterSource::RasterSource(const RecordingSource* other) : display_list_(other->display_list_), painter_reported_memory_usage_(other->painter_reported_memory_usage_), @@ -33,6 +33,7 @@ RasterSource::RasterSource(const RecordingSource* other) slow_down_raster_scale_factor_for_debug_( other->slow_down_raster_scale_factor_for_debug_), recording_scale_factor_(other->recording_scale_factor_) {} + RasterSource::~RasterSource() = default; void RasterSource::ClearForOpaqueRaster( @@ -146,19 +147,6 @@ void RasterSource::PlaybackToCanvas(SkCanvas* raster_canvas, display_list_->Raster(raster_canvas, image_provider); } -sk_sp<SkPicture> RasterSource::GetFlattenedPicture() { - TRACE_EVENT0("cc", "RasterSource::GetFlattenedPicture"); - - SkPictureRecorder recorder; - SkCanvas* canvas = recorder.beginRecording(size_.width(), size_.height()); - if (!size_.IsEmpty()) { - canvas->clear(SK_ColorTRANSPARENT); - PlaybackToCanvas(canvas, nullptr); - } - - return recorder.finishRecordingAsPicture(); -} - size_t RasterSource::GetMemoryUsage() const { if (!display_list_) return 0; diff --git a/chromium/cc/raster/raster_source.h b/chromium/cc/raster/raster_source.h index c0686dea5d5..7a70742f127 100644 --- a/chromium/cc/raster/raster_source.h +++ b/chromium/cc/raster/raster_source.h @@ -109,7 +109,6 @@ class CC_EXPORT RasterSource : public base::RefCountedThreadSafe<RasterSource> { // Tracing functionality. void DidBeginTracing(); void AsValueInto(base::trace_event::TracedValue* array) const; - sk_sp<SkPicture> GetFlattenedPicture(); size_t GetMemoryUsage() const; const scoped_refptr<DisplayItemList>& GetDisplayItemList() const { diff --git a/chromium/cc/raster/raster_source_unittest.cc b/chromium/cc/raster/raster_source_unittest.cc index d21339b63a2..41ee8c0b387 100644 --- a/chromium/cc/raster/raster_source_unittest.cc +++ b/chromium/cc/raster/raster_source_unittest.cc @@ -254,7 +254,6 @@ TEST(RasterSourceTest, RasterFullContents) { std::unique_ptr<FakeRecordingSource> recording_source = FakeRecordingSource::CreateFilledRecordingSource(layer_bounds); recording_source->SetBackgroundColor(SK_ColorBLACK); - recording_source->SetClearCanvasWithDebugColor(false); // Because the caller sets content opaque, it also promises that it // has at least filled in layer_bounds opaquely. @@ -320,7 +319,6 @@ TEST(RasterSourceTest, RasterPartialContents) { std::unique_ptr<FakeRecordingSource> recording_source = FakeRecordingSource::CreateFilledRecordingSource(layer_bounds); recording_source->SetBackgroundColor(SK_ColorGREEN); - recording_source->SetClearCanvasWithDebugColor(false); // First record everything as white. PaintFlags white_flags; @@ -415,7 +413,6 @@ TEST(RasterSourceTest, RasterPartialClear) { FakeRecordingSource::CreateFilledRecordingSource(layer_bounds); recording_source->SetBackgroundColor(SK_ColorGREEN); recording_source->SetRequiresClear(true); - recording_source->SetClearCanvasWithDebugColor(false); // First record everything as white. const unsigned alpha_dark = 10u; @@ -462,7 +459,6 @@ TEST(RasterSourceTest, RasterPartialClear) { FakeRecordingSource::CreateFilledRecordingSource(layer_bounds); recording_source_light->SetBackgroundColor(SK_ColorGREEN); recording_source_light->SetRequiresClear(true); - recording_source_light->SetClearCanvasWithDebugColor(false); // Record everything as a slightly lighter white. const unsigned alpha_light = 18u; @@ -506,7 +502,6 @@ TEST(RasterSourceTest, RasterContentsTransparent) { FakeRecordingSource::CreateFilledRecordingSource(layer_bounds); recording_source->SetBackgroundColor(SK_ColorTRANSPARENT); recording_source->SetRequiresClear(true); - recording_source->SetClearCanvasWithDebugColor(false); recording_source->Rerecord(); scoped_refptr<RasterSource> raster = recording_source->CreateRasterSource(); diff --git a/chromium/cc/raster/task.cc b/chromium/cc/raster/task.cc index 415b8dbc0f6..5a34ca86ad2 100644 --- a/chromium/cc/raster/task.cc +++ b/chromium/cc/raster/task.cc @@ -4,7 +4,8 @@ #include "cc/raster/task.h" -#include "base/logging.h" +#include "base/check.h" +#include "base/notreached.h" namespace cc { diff --git a/chromium/cc/raster/tile_task.cc b/chromium/cc/raster/tile_task.cc index d731433c2c0..052a6b06b70 100644 --- a/chromium/cc/raster/tile_task.cc +++ b/chromium/cc/raster/tile_task.cc @@ -4,7 +4,7 @@ #include "cc/raster/tile_task.h" -#include "base/logging.h" +#include "base/check.h" namespace cc { diff --git a/chromium/cc/resources/ui_resource_bitmap.cc b/chromium/cc/resources/ui_resource_bitmap.cc index c21048553fa..974706def78 100644 --- a/chromium/cc/resources/ui_resource_bitmap.cc +++ b/chromium/cc/resources/ui_resource_bitmap.cc @@ -8,7 +8,8 @@ #include <memory> -#include "base/logging.h" +#include "base/check_op.h" +#include "base/notreached.h" #include "base/numerics/checked_math.h" #include "third_party/skia/include/core/SkImageInfo.h" #include "third_party/skia/include/core/SkMallocPixelRef.h" diff --git a/chromium/cc/scheduler/begin_frame_tracker.cc b/chromium/cc/scheduler/begin_frame_tracker.cc index fe8f84012ad..0480a2c24b2 100644 --- a/chromium/cc/scheduler/begin_frame_tracker.cc +++ b/chromium/cc/scheduler/begin_frame_tracker.cc @@ -16,7 +16,7 @@ BeginFrameTracker::BeginFrameTracker(const base::Location& location) BeginFrameTracker::~BeginFrameTracker() = default; -void BeginFrameTracker::Start(viz::BeginFrameArgs new_args) { +void BeginFrameTracker::Start(const viz::BeginFrameArgs& new_args) { // Trace the frame time being passed between BeginFrameTrackers. TRACE_EVENT_FLOW_STEP0( TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler.frames"), "BeginFrameArgs", diff --git a/chromium/cc/scheduler/begin_frame_tracker.h b/chromium/cc/scheduler/begin_frame_tracker.h index 2f5f3b1c27e..fc041094891 100644 --- a/chromium/cc/scheduler/begin_frame_tracker.h +++ b/chromium/cc/scheduler/begin_frame_tracker.h @@ -49,7 +49,7 @@ class CC_EXPORT BeginFrameTracker { // Start using a new BFA value and check invariant properties. // **Must** only be called after finishing with any previous BFA. - void Start(viz::BeginFrameArgs new_args); + void Start(const viz::BeginFrameArgs& new_args); // Finish using the current BFA. // **Must** only be called while still using a BFA. void Finish(); diff --git a/chromium/cc/scheduler/scheduler.cc b/chromium/cc/scheduler/scheduler.cc index 8ec008c1220..445e0696639 100644 --- a/chromium/cc/scheduler/scheduler.cc +++ b/chromium/cc/scheduler/scheduler.cc @@ -9,8 +9,8 @@ #include "base/auto_reset.h" #include "base/bind.h" +#include "base/check_op.h" #include "base/location.h" -#include "base/logging.h" #include "base/single_thread_task_runner.h" #include "base/trace_event/trace_event.h" #include "base/trace_event/traced_value.h" @@ -583,7 +583,9 @@ void Scheduler::FinishImplFrame() { // ensures that the acks are sent in order. if (!state_machine_.did_submit_in_last_frame()) { SendDidNotProduceFrame(begin_impl_frame_tracker_.Current(), - FrameSkippedReason::kWaitingOnMain); + state_machine_.draw_succeeded_in_last_frame() + ? FrameSkippedReason::kNoDamage + : FrameSkippedReason::kWaitingOnMain); } begin_impl_frame_tracker_.Finish(); @@ -605,8 +607,7 @@ void Scheduler::SendDidNotProduceFrame(const viz::BeginFrameArgs& args, return; last_begin_frame_ack_ = viz::BeginFrameAck(args, false /* has_damage */); client_->DidNotProduceFrame(last_begin_frame_ack_, reason); - if (reason == FrameSkippedReason::kNoDamage) - compositor_timing_history_->DidNotProduceFrame(args.frame_id); + compositor_timing_history_->DidNotProduceFrame(args.frame_id, reason); } // BeginImplFrame starts a compositor frame that will wait up until a deadline diff --git a/chromium/cc/scheduler/scheduler_state_machine.cc b/chromium/cc/scheduler/scheduler_state_machine.cc index 908c5957027..e69c54ce465 100644 --- a/chromium/cc/scheduler/scheduler_state_machine.cc +++ b/chromium/cc/scheduler/scheduler_state_machine.cc @@ -4,8 +4,9 @@ #include "cc/scheduler/scheduler_state_machine.h" +#include "base/check_op.h" #include "base/format_macros.h" -#include "base/logging.h" +#include "base/notreached.h" #include "base/trace_event/trace_event.h" #include "base/trace_event/traced_value.h" #include "base/values.h" @@ -266,7 +267,7 @@ void SchedulerStateMachine::AsProtozeroInto( minor_state->set_video_needs_begin_frames(video_needs_begin_frames_); minor_state->set_defer_begin_main_frame(defer_begin_main_frame_); minor_state->set_last_commit_had_no_updates(last_commit_had_no_updates_); - minor_state->set_did_draw_in_last_frame(did_draw_in_last_frame_); + minor_state->set_did_draw_in_last_frame(did_attempt_draw_in_last_frame_); minor_state->set_did_submit_in_last_frame(did_submit_in_last_frame_); minor_state->set_needs_impl_side_invalidation(needs_impl_side_invalidation_); minor_state->set_current_pending_tree_is_impl_side( @@ -981,10 +982,11 @@ void SchedulerStateMachine::WillDraw() { // Set this to true to proactively request a new BeginFrame. We can't set this // in WillDrawInternal because AbortDraw calls WillDrawInternal but shouldn't // request another frame. - did_draw_in_last_frame_ = true; + did_attempt_draw_in_last_frame_ = true; } void SchedulerStateMachine::DidDraw(DrawResult draw_result) { + draw_succeeded_in_last_frame_ = draw_result == DRAW_SUCCESS; DidDrawInternal(draw_result); } @@ -1126,7 +1128,7 @@ bool SchedulerStateMachine::ProactiveBeginFrameWanted() const { // frame soon. This helps avoid negative glitches in our SetNeedsBeginFrame // requests, which may propagate to the BeginImplFrame provider and get // sampled at an inopportune time, delaying the next BeginImplFrame. - if (did_draw_in_last_frame_) + if (did_attempt_draw_in_last_frame_) return true; // If the last commit was aborted because of early out (no updates), we should @@ -1154,7 +1156,8 @@ void SchedulerStateMachine::OnBeginImplFrame(const viz::BeginFrameId& frame_id, last_frame_events_.did_commit_during_frame = did_commit_during_frame_; last_commit_had_no_updates_ = false; - did_draw_in_last_frame_ = false; + did_attempt_draw_in_last_frame_ = false; + draw_succeeded_in_last_frame_ = false; did_submit_in_last_frame_ = false; needs_one_begin_impl_frame_ = false; diff --git a/chromium/cc/scheduler/scheduler_state_machine.h b/chromium/cc/scheduler/scheduler_state_machine.h index 25b6b5f8ed6..f6a8d0a37f1 100644 --- a/chromium/cc/scheduler/scheduler_state_machine.h +++ b/chromium/cc/scheduler/scheduler_state_machine.h @@ -323,6 +323,9 @@ class CC_EXPORT SchedulerStateMachine { bool video_needs_begin_frames() const { return video_needs_begin_frames_; } bool did_submit_in_last_frame() const { return did_submit_in_last_frame_; } + bool draw_succeeded_in_last_frame() const { + return draw_succeeded_in_last_frame_; + } bool needs_impl_side_invalidation() const { return needs_impl_side_invalidation_; @@ -450,7 +453,8 @@ class CC_EXPORT SchedulerStateMachine { bool video_needs_begin_frames_ = false; bool last_commit_had_no_updates_ = false; bool active_tree_is_ready_to_draw_ = true; - bool did_draw_in_last_frame_ = false; + bool did_attempt_draw_in_last_frame_ = false; + bool draw_succeeded_in_last_frame_ = false; bool did_submit_in_last_frame_ = false; bool needs_impl_side_invalidation_ = false; bool next_invalidation_needs_first_draw_on_activation_ = false; diff --git a/chromium/cc/scheduler/scheduler_unittest.cc b/chromium/cc/scheduler/scheduler_unittest.cc index 6f324622056..4ce0028b0f3 100644 --- a/chromium/cc/scheduler/scheduler_unittest.cc +++ b/chromium/cc/scheduler/scheduler_unittest.cc @@ -11,8 +11,9 @@ #include "base/auto_reset.h" #include "base/bind.h" -#include "base/logging.h" +#include "base/check_op.h" #include "base/memory/ptr_util.h" +#include "base/notreached.h" #include "base/numerics/safe_conversions.h" #include "base/run_loop.h" #include "base/test/test_mock_time_task_runner.h" @@ -60,6 +61,7 @@ class FakeSchedulerClient : public SchedulerClient, num_draws_ = 0; last_begin_main_frame_args_ = viz::BeginFrameArgs(); last_begin_frame_ack_ = viz::BeginFrameAck(); + last_frame_skipped_reason_.reset(); } void set_scheduler(TestScheduler* scheduler) { scheduler_ = scheduler; } @@ -130,6 +132,7 @@ class FakeSchedulerClient : public SchedulerClient, EXPECT_FALSE(inside_action_); base::AutoReset<bool> mark_inside(&inside_action_, true); last_begin_frame_ack_ = ack; + last_frame_skipped_reason_ = reason; } void WillNotReceiveBeginFrame() override {} @@ -153,23 +156,26 @@ class FakeSchedulerClient : public SchedulerClient, return last_begin_frame_ack_; } + FrameSkippedReason last_frame_skipped_reason() const { + return last_frame_skipped_reason_.value(); + } + DrawResult ScheduledActionDrawIfPossible() override { EXPECT_FALSE(inside_action_); base::AutoReset<bool> mark_inside(&inside_action_, true); PushAction("ScheduledActionDrawIfPossible"); num_draws_++; - DrawResult result = - draw_will_happen_ ? DRAW_SUCCESS : DRAW_ABORTED_CHECKERBOARD_ANIMATIONS; - bool swap_will_happen = - draw_will_happen_ && swap_will_happen_if_draw_happens_; - if (swap_will_happen) { + if (!draw_will_happen_) + return DRAW_ABORTED_CHECKERBOARD_ANIMATIONS; + + if (swap_will_happen_if_draw_happens_) { last_begin_frame_ack_ = scheduler_->CurrentBeginFrameAckForActiveTree(); scheduler_->DidSubmitCompositorFrame(0, EventMetricsSet()); if (automatic_ack_) scheduler_->DidReceiveCompositorFrameAck(); } - return result; + return DRAW_SUCCESS; } DrawResult ScheduledActionDrawForced() override { EXPECT_FALSE(inside_action_); @@ -283,6 +289,7 @@ class FakeSchedulerClient : public SchedulerClient, std::vector<const char*> actions_; TestScheduler* scheduler_ = nullptr; base::TimeDelta frame_interval_; + base::Optional<FrameSkippedReason> last_frame_skipped_reason_; }; enum BeginFrameSourceType { @@ -1813,6 +1820,8 @@ void SchedulerTest::ImplFrameSkippedAfterLateAck( EXPECT_NO_ACTION(); EXPECT_FALSE(client_->IsInsideBeginImplFrame()); EXPECT_FALSE(scheduler_->MainThreadMissedLastDeadline()); + EXPECT_EQ(FrameSkippedReason::kRecoverLatency, + client_->last_frame_skipped_reason()); // Verify that we do not perform any actions after we are no longer // swap throttled. @@ -1939,6 +1948,8 @@ TEST_F(SchedulerTest, EXPECT_NO_ACTION(); EXPECT_FALSE(client_->IsInsideBeginImplFrame()); EXPECT_FALSE(scheduler_->MainThreadMissedLastDeadline()); + EXPECT_EQ(FrameSkippedReason::kRecoverLatency, + client_->last_frame_skipped_reason()); // Verify that we do not perform any actions after we are no longer // swap throttled. @@ -3850,7 +3861,26 @@ TEST_F(SchedulerTest, BeginFrameAckForFinishedImplFrame) { has_damage = false; EXPECT_EQ(viz::BeginFrameAck(args, has_damage), client_->last_begin_frame_ack()); + EXPECT_EQ(FrameSkippedReason::kWaitingOnMain, + client_->last_frame_skipped_reason()); client_->Reset(); + + // Draw succeeds, but 'swap' does not happen (i.e. no frame is submitted). + args = SendNextBeginFrame(); + EXPECT_ACTIONS("WillBeginImplFrame"); + EXPECT_TRUE(client_->IsInsideBeginImplFrame()); + EXPECT_TRUE(scheduler_->begin_frames_expected()); + client_->Reset(); + client_->SetDrawWillHappen(true); + client_->SetSwapWillHappenIfDrawHappens(false); + task_runner_->RunPendingTasks(); // Run posted deadline. + + // Draw with no damage. + has_damage = false; + EXPECT_EQ(viz::BeginFrameAck(args, has_damage), + client_->last_begin_frame_ack()); + EXPECT_EQ(FrameSkippedReason::kNoDamage, + client_->last_frame_skipped_reason()); } TEST_F(SchedulerTest, BeginFrameAckForSkippedImplFrame) { diff --git a/chromium/cc/tiles/image_decode_cache_utils.cc b/chromium/cc/tiles/image_decode_cache_utils.cc index a50f2464110..fb0e1bc04ca 100644 --- a/chromium/cc/tiles/image_decode_cache_utils.cc +++ b/chromium/cc/tiles/image_decode_cache_utils.cc @@ -7,7 +7,7 @@ #include "cc/tiles/image_decode_cache_utils.h" -#include "base/logging.h" +#include "base/check.h" #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkSurface.h" diff --git a/chromium/cc/tiles/picture_layer_tiling.cc b/chromium/cc/tiles/picture_layer_tiling.cc index 116f3c4db58..aefe88d22b8 100644 --- a/chromium/cc/tiles/picture_layer_tiling.cc +++ b/chromium/cc/tiles/picture_layer_tiling.cc @@ -11,8 +11,8 @@ #include <limits> #include <set> +#include "base/check_op.h" #include "base/containers/flat_map.h" -#include "base/logging.h" #include "base/numerics/safe_conversions.h" #include "base/stl_util.h" #include "base/trace_event/trace_event.h" diff --git a/chromium/cc/tiles/software_image_decode_cache_utils.h b/chromium/cc/tiles/software_image_decode_cache_utils.h index 0d2b8cc560a..071f7d216a7 100644 --- a/chromium/cc/tiles/software_image_decode_cache_utils.h +++ b/chromium/cc/tiles/software_image_decode_cache_utils.h @@ -9,6 +9,7 @@ #include <memory> #include <string> +#include "base/callback.h" #include "base/memory/discardable_memory.h" #include "base/memory/scoped_refptr.h" #include "cc/cc_export.h" diff --git a/chromium/cc/tiles/tile.cc b/chromium/cc/tiles/tile.cc index 96962c83b25..f0cf47dcfea 100644 --- a/chromium/cc/tiles/tile.cc +++ b/chromium/cc/tiles/tile.cc @@ -40,7 +40,10 @@ Tile::Tile(TileManager* tile_manager, can_use_lcd_text_(can_use_lcd_text), id_(tile_manager->GetUniqueTileId()), invalidated_id_(0), - scheduled_priority_(0) {} + scheduled_priority_(0) { + raster_rects_.push_back( + std::make_pair(info.content_rect, info.raster_transform)); +} Tile::~Tile() { TRACE_EVENT_OBJECT_DELETED_WITH_ID( diff --git a/chromium/cc/tiles/tile.h b/chromium/cc/tiles/tile.h index 84f68cb8c14..34726608547 100644 --- a/chromium/cc/tiles/tile.h +++ b/chromium/cc/tiles/tile.h @@ -170,6 +170,11 @@ class CC_EXPORT Tile { Id id_; + // List of Rect-Transform pairs, representing unoccluded parts of the + // tile, to support raster culling. See Bug: 1071932 + std::vector<std::pair<const gfx::Rect, const gfx::AxisTransform2d>> + raster_rects_; + // The rect bounding the changes in this Tile vs the previous tile it // replaced. gfx::Rect invalidated_content_rect_; diff --git a/chromium/cc/tiles/tile_manager_unittest.cc b/chromium/cc/tiles/tile_manager_unittest.cc index 0d032b7627a..5aa183e3009 100644 --- a/chromium/cc/tiles/tile_manager_unittest.cc +++ b/chromium/cc/tiles/tile_manager_unittest.cc @@ -1752,7 +1752,6 @@ TEST_F(PixelInspectTileManagerTest, LowResHasNoImage) { FakeRecordingSource::CreateFilledRecordingSource(size); recording_source->SetBackgroundColor(SK_ColorTRANSPARENT); recording_source->SetRequiresClear(true); - recording_source->SetClearCanvasWithDebugColor(false); PaintFlags flags; flags.setColor(SK_ColorGREEN); recording_source->add_draw_rect_with_flags(gfx::Rect(size), flags); diff --git a/chromium/cc/trees/draw_properties_unittest.cc b/chromium/cc/trees/draw_properties_unittest.cc index 538b33043fe..6abe4863df5 100644 --- a/chromium/cc/trees/draw_properties_unittest.cc +++ b/chromium/cc/trees/draw_properties_unittest.cc @@ -3571,8 +3571,8 @@ class LCDTextTest : public DrawPropertiesTestBase, void SetUp() override { root_ = root_layer(); - child_ = AddLayer<LayerImpl>(); - grand_child_ = AddLayer<LayerImpl>(); + child_ = AddLayer<PictureLayerImpl>(); + grand_child_ = AddLayer<PictureLayerImpl>(); SetElementIdsForTesting(); root_->SetContentsOpaque(true); @@ -3593,23 +3593,36 @@ class LCDTextTest : public DrawPropertiesTestBase, CopyProperties(child_, grand_child_); } + void CheckCanUseLCDText(LCDTextDisallowedReason expected_disallowed_reason, + PictureLayerImpl* layer = nullptr) { + if (layers_always_allowed_lcd_text_) + expected_disallowed_reason = LCDTextDisallowedReason::kNone; + else if (!can_use_lcd_text_) + expected_disallowed_reason = LCDTextDisallowedReason::kSetting; + + if (layer) { + EXPECT_EQ(expected_disallowed_reason, + layer->ComputeLCDTextDisallowedReasonForTesting()); + } else { + EXPECT_EQ(expected_disallowed_reason, + child_->ComputeLCDTextDisallowedReasonForTesting()); + EXPECT_EQ(expected_disallowed_reason, + grand_child_->ComputeLCDTextDisallowedReasonForTesting()); + } + } + bool can_use_lcd_text_; bool layers_always_allowed_lcd_text_; LayerImpl* root_ = nullptr; - LayerImpl* child_ = nullptr; - LayerImpl* grand_child_ = nullptr; + PictureLayerImpl* child_ = nullptr; + PictureLayerImpl* grand_child_ = nullptr; }; TEST_P(LCDTextTest, CanUseLCDText) { - bool expect_lcd_text = can_use_lcd_text_ || layers_always_allowed_lcd_text_; - bool expect_not_lcd_text = layers_always_allowed_lcd_text_; - // Case 1: Identity transform. UpdateActiveTreeDrawProperties(); - EXPECT_EQ(expect_lcd_text, root_->CanUseLCDText()); - EXPECT_EQ(expect_lcd_text, child_->CanUseLCDText()); - EXPECT_EQ(expect_lcd_text, grand_child_->CanUseLCDText()); + CheckCanUseLCDText(LCDTextDisallowedReason::kNone); // Case 2: Integral translation. gfx::Transform integral_translation; @@ -3617,124 +3630,105 @@ TEST_P(LCDTextTest, CanUseLCDText) { SetTransform(child_, integral_translation); UpdateActiveTreeDrawProperties(); - EXPECT_EQ(expect_lcd_text, root_->CanUseLCDText()); - EXPECT_EQ(expect_lcd_text, child_->CanUseLCDText()); - EXPECT_EQ(expect_lcd_text, grand_child_->CanUseLCDText()); + CheckCanUseLCDText(LCDTextDisallowedReason::kNone); // Case 3: Non-integral translation. gfx::Transform non_integral_translation; non_integral_translation.Translate(1.5, 2.5); SetTransform(child_, non_integral_translation); UpdateActiveTreeDrawProperties(); - EXPECT_EQ(expect_lcd_text, root_->CanUseLCDText()); - EXPECT_EQ(expect_not_lcd_text, child_->CanUseLCDText()); - EXPECT_EQ(expect_not_lcd_text, grand_child_->CanUseLCDText()); + CheckCanUseLCDText(LCDTextDisallowedReason::kNonIntegralTranslation); // Case 4: Rotation. gfx::Transform rotation; rotation.Rotate(10.0); SetTransform(child_, rotation); UpdateActiveTreeDrawProperties(); - EXPECT_EQ(expect_lcd_text, root_->CanUseLCDText()); - EXPECT_EQ(expect_not_lcd_text, child_->CanUseLCDText()); - EXPECT_EQ(expect_not_lcd_text, grand_child_->CanUseLCDText()); + CheckCanUseLCDText(LCDTextDisallowedReason::kNonIntegralTranslation); // Case 5: Scale. gfx::Transform scale; scale.Scale(2.0, 2.0); SetTransform(child_, scale); UpdateActiveTreeDrawProperties(); - EXPECT_EQ(expect_lcd_text, root_->CanUseLCDText()); - EXPECT_EQ(expect_not_lcd_text, child_->CanUseLCDText()); - EXPECT_EQ(expect_not_lcd_text, grand_child_->CanUseLCDText()); + CheckCanUseLCDText(LCDTextDisallowedReason::kNonIntegralTranslation); // Case 6: Skew. gfx::Transform skew; skew.Skew(10.0, 0.0); SetTransform(child_, skew); UpdateActiveTreeDrawProperties(); - EXPECT_EQ(expect_lcd_text, root_->CanUseLCDText()); - EXPECT_EQ(expect_not_lcd_text, child_->CanUseLCDText()); - EXPECT_EQ(expect_not_lcd_text, grand_child_->CanUseLCDText()); + CheckCanUseLCDText(LCDTextDisallowedReason::kNonIntegralTranslation); - // Case 7: Translucent. + // Case 7: Translucent: LCD-text is allowed. SetTransform(child_, gfx::Transform()); SetOpacity(child_, 0.5f); UpdateActiveTreeDrawProperties(); - EXPECT_EQ(expect_lcd_text, root_->CanUseLCDText()); - EXPECT_EQ(expect_not_lcd_text, child_->CanUseLCDText()); - EXPECT_EQ(expect_not_lcd_text, grand_child_->CanUseLCDText()); + CheckCanUseLCDText(LCDTextDisallowedReason::kNone); // Case 8: Sanity check: restore transform and opacity. SetTransform(child_, gfx::Transform()); SetOpacity(child_, 1.f); UpdateActiveTreeDrawProperties(); - EXPECT_EQ(expect_lcd_text, root_->CanUseLCDText()); - EXPECT_EQ(expect_lcd_text, child_->CanUseLCDText()); - EXPECT_EQ(expect_lcd_text, grand_child_->CanUseLCDText()); + CheckCanUseLCDText(LCDTextDisallowedReason::kNone); - // Case 9: Non-opaque content. + // Case 9a: Non-opaque content and opaque background. child_->SetContentsOpaque(false); + child_->SetBackgroundColor(SK_ColorGREEN); + UpdateActiveTreeDrawProperties(); + CheckCanUseLCDText(LCDTextDisallowedReason::kContentsNotOpaque, child_); + CheckCanUseLCDText(LCDTextDisallowedReason::kNone, grand_child_); + + // Case 9b: Non-opaque content and non-opaque background. + child_->SetBackgroundColor(SkColorSetARGB(128, 255, 255, 255)); UpdateActiveTreeDrawProperties(); - EXPECT_EQ(expect_lcd_text, root_->CanUseLCDText()); - EXPECT_EQ(expect_not_lcd_text, child_->CanUseLCDText()); - EXPECT_EQ(expect_lcd_text, grand_child_->CanUseLCDText()); + CheckCanUseLCDText(LCDTextDisallowedReason::kBackgroundColorNotOpaque, + child_); + CheckCanUseLCDText(LCDTextDisallowedReason::kNone, grand_child_); // Case 10: Sanity check: restore content opaqueness. child_->SetContentsOpaque(true); UpdateActiveTreeDrawProperties(); - EXPECT_EQ(expect_lcd_text, root_->CanUseLCDText()); - EXPECT_EQ(expect_lcd_text, child_->CanUseLCDText()); - EXPECT_EQ(expect_lcd_text, grand_child_->CanUseLCDText()); + CheckCanUseLCDText(LCDTextDisallowedReason::kNone); // Case 11: will-change: transform child_->SetHasWillChangeTransformHint(true); UpdateActiveTreeDrawProperties(); - EXPECT_EQ(expect_lcd_text, root_->CanUseLCDText()); - EXPECT_EQ(expect_not_lcd_text, child_->CanUseLCDText()); - EXPECT_EQ(expect_lcd_text, grand_child_->CanUseLCDText()); + CheckCanUseLCDText(LCDTextDisallowedReason::kWillChangeTransform, child_); + // TODO(wangxianzhu): Is this correct? + CheckCanUseLCDText(LCDTextDisallowedReason::kNone, grand_child_); } TEST_P(LCDTextTest, CanUseLCDTextWithAnimation) { - bool expect_lcd_text = can_use_lcd_text_ || layers_always_allowed_lcd_text_; - bool expect_not_lcd_text = layers_always_allowed_lcd_text_; - // Sanity check: Make sure can_use_lcd_text_ is set on each node. UpdateActiveTreeDrawProperties(); - EXPECT_EQ(expect_lcd_text, root_->CanUseLCDText()); - EXPECT_EQ(expect_lcd_text, child_->CanUseLCDText()); - EXPECT_EQ(expect_lcd_text, grand_child_->CanUseLCDText()); + CheckCanUseLCDText(LCDTextDisallowedReason::kNone); // Add opacity animation. - SetOpacity(child_, 0.9f); - AddOpacityTransitionToElementWithAnimation(child_->element_id(), timeline(), - 10.0, 0.9f, 0.1f, false); + gfx::Transform non_integral_translation; + non_integral_translation.Translate(1.5, 2.5); + SetTransform(child_, non_integral_translation); + AddAnimatedTransformToElementWithAnimation(child_->element_id(), timeline(), + 10.0, 12, 34); UpdateActiveTreeDrawProperties(); // Text LCD should be adjusted while animation is active. - EXPECT_EQ(expect_lcd_text, root_->CanUseLCDText()); - EXPECT_EQ(expect_not_lcd_text, child_->CanUseLCDText()); - EXPECT_EQ(expect_not_lcd_text, grand_child_->CanUseLCDText()); + CheckCanUseLCDText(LCDTextDisallowedReason::kNonIntegralTranslation); } TEST_P(LCDTextTest, CanUseLCDTextWithAnimationContentsOpaque) { - bool expect_lcd_text = can_use_lcd_text_ || layers_always_allowed_lcd_text_; - bool expect_not_lcd_text = layers_always_allowed_lcd_text_; - // Sanity check: Make sure can_use_lcd_text_ is set on each node. UpdateActiveTreeDrawProperties(); - EXPECT_EQ(expect_lcd_text, root_->CanUseLCDText()); - EXPECT_EQ(expect_lcd_text, child_->CanUseLCDText()); - EXPECT_EQ(expect_lcd_text, grand_child_->CanUseLCDText()); + CheckCanUseLCDText(LCDTextDisallowedReason::kNone); // Mark contents non-opaque within the first animation frame. child_->SetContentsOpaque(false); + child_->SetBackgroundColor(SK_ColorWHITE); AddOpacityTransitionToElementWithAnimation(child_->element_id(), timeline(), 10.0, 0.9f, 0.1f, false); UpdateActiveTreeDrawProperties(); // LCD text should be disabled for non-opaque layers even during animations. - EXPECT_EQ(expect_lcd_text, root_->CanUseLCDText()); - EXPECT_EQ(expect_not_lcd_text, child_->CanUseLCDText()); - EXPECT_EQ(expect_lcd_text, grand_child_->CanUseLCDText()); + CheckCanUseLCDText(LCDTextDisallowedReason::kContentsNotOpaque, child_); + CheckCanUseLCDText(LCDTextDisallowedReason::kNone, grand_child_); } INSTANTIATE_TEST_SUITE_P(DrawPropertiesTest, @@ -4481,10 +4475,13 @@ TEST_F(DrawPropertiesTest, TEST_F(DrawPropertiesTest, TransformAnimationUpdatesBackfaceVisibility) { LayerImpl* root = root_layer(); + root->SetDrawsContent(true); LayerImpl* back_facing = AddLayer<LayerImpl>(); + back_facing->SetDrawsContent(true); LayerImpl* render_surface1 = AddLayer<LayerImpl>(); + render_surface1->SetDrawsContent(true); LayerImpl* render_surface2 = AddLayer<LayerImpl>(); - + render_surface2->SetDrawsContent(true); gfx::Transform rotate_about_y; rotate_about_y.RotateAboutYAxis(180.0); @@ -4519,7 +4516,11 @@ TEST_F(DrawPropertiesTest, TransformAnimationUpdatesBackfaceVisibility) { UpdateActiveTreeDrawProperties(); EXPECT_TRUE(GetEffectNode(render_surface1)->hidden_by_backface_visibility); + EXPECT_EQ(gfx::Rect(), render_surface1->visible_layer_rect()); EXPECT_TRUE(GetEffectNode(render_surface2)->hidden_by_backface_visibility); + EXPECT_EQ(gfx::Rect(), render_surface2->visible_layer_rect()); + + EXPECT_EQ(1u, GetRenderSurfaceList().size()); root->layer_tree_impl()->SetTransformMutated(back_facing->element_id(), gfx::Transform()); @@ -4527,13 +4528,24 @@ TEST_F(DrawPropertiesTest, TransformAnimationUpdatesBackfaceVisibility) { rotate_about_y); UpdateActiveTreeDrawProperties(); EXPECT_FALSE(GetEffectNode(render_surface1)->hidden_by_backface_visibility); + EXPECT_EQ(gfx::Rect(0, 0, 30, 30), render_surface1->visible_layer_rect()); EXPECT_TRUE(GetEffectNode(render_surface2)->hidden_by_backface_visibility); + EXPECT_EQ(gfx::Rect(), render_surface2->visible_layer_rect()); + + EXPECT_EQ(2u, GetRenderSurfaceList().size()); root->layer_tree_impl()->SetTransformMutated(render_surface1->element_id(), rotate_about_y); UpdateActiveTreeDrawProperties(); EXPECT_TRUE(GetEffectNode(render_surface1)->hidden_by_backface_visibility); + // Draw properties are only updated for visible layers, so this remains the + // cached value from last time. The expectation is commented out because + // this result is not required. + // EXPECT_EQ(gfx::Rect(0, 0, 30, 30), render_surface1->visible_layer_rect()); EXPECT_TRUE(GetEffectNode(render_surface2)->hidden_by_backface_visibility); + EXPECT_EQ(gfx::Rect(), render_surface2->visible_layer_rect()); + + EXPECT_EQ(1u, GetRenderSurfaceList().size()); } TEST_F(DrawPropertiesTest, ScrollChildAndScrollParentDifferentTargets) { @@ -6616,7 +6628,6 @@ TEST_F(DrawPropertiesTestWithLayerTree, SkippingLayerImpl) { // A double sided render surface with backface visible should not be skipped ImplOf(grandchild)->set_visible_layer_rect(gfx::Rect()); child->SetForceRenderSurfaceForTesting(true); - child->SetDoubleSided(true); child->SetTransform(rotate_back_and_translate); CommitAndActivate(); EXPECT_EQ(gfx::Rect(10, 10), ImplOf(grandchild)->visible_layer_rect()); @@ -6817,12 +6828,10 @@ TEST_F(DrawPropertiesTestWithLayerTree, SkippingLayer) { child->SetBounds(gfx::Size(10, 10)); gfx::Transform rotate; - child->SetDoubleSided(false); rotate.RotateAboutXAxis(180.f); child->SetTransform(rotate); CommitAndActivate(); EXPECT_EQ(gfx::Rect(0, 0), ImplOf(child)->visible_layer_rect()); - child->SetDoubleSided(true); child->SetTransform(gfx::Transform()); child->SetOpacity(0.f); diff --git a/chromium/cc/trees/draw_property_utils.cc b/chromium/cc/trees/draw_property_utils.cc index 654c341b8b8..f9fdebf8218 100644 --- a/chromium/cc/trees/draw_property_utils.cc +++ b/chromium/cc/trees/draw_property_utils.cc @@ -445,16 +445,21 @@ bool LayerNeedsUpdate(LayerType* layer, // backface is not visible. if (TransformToScreenIsKnown(layer, backface_transform_id, tree) && !HasSingularTransform(backface_transform_id, tree) && - IsLayerBackFaceVisible(layer, backface_transform_id, property_trees)) + IsLayerBackFaceVisible(layer, backface_transform_id, property_trees)) { + UMA_HISTOGRAM_BOOLEAN( + "Compositing.Renderer.LayerUpdateSkippedDueToBackface", true); return false; + } } + UMA_HISTOGRAM_BOOLEAN("Compositing.Renderer.LayerUpdateSkippedDueToBackface", + false); + return true; } -template <typename LayerType> inline bool LayerShouldBeSkippedForDrawPropertiesComputation( - LayerType* layer, + Layer* layer, const TransformTree& transform_tree, const EffectTree& effect_tree) { const EffectNode* effect_node = effect_tree.Node(layer->effect_tree_index()); @@ -466,7 +471,36 @@ inline bool LayerShouldBeSkippedForDrawPropertiesComputation( const TransformNode* transform_node = transform_tree.Node(layer->transform_tree_index()); return !transform_node->node_and_ancestors_are_animated_or_invertible || - effect_node->hidden_by_backface_visibility || !effect_node->is_drawn; + !effect_node->is_drawn; +} + +inline bool LayerShouldBeSkippedForDrawPropertiesComputation( + LayerImpl* layer, + const TransformTree& transform_tree, + const EffectTree& effect_tree) { + const EffectNode* effect_node = effect_tree.Node(layer->effect_tree_index()); + + if (effect_node->HasRenderSurface() && effect_node->subtree_has_copy_request) + return false; + + // Skip if the node's subtree is hidden and no need to cache. + if (effect_node->subtree_hidden && !effect_node->cache_render_surface) + return true; + + // If the layer transform is not invertible, it should be skipped. In case the + // transform is animating and singular, we should not skip it. + const TransformNode* transform_node = + transform_tree.Node(layer->transform_tree_index()); + + if (!transform_node->node_and_ancestors_are_animated_or_invertible || + !effect_node->is_drawn) + return true; + + UMA_HISTOGRAM_BOOLEAN( + "Compositing.Renderer.LayerSkippedForDrawPropertiesDueToBackface", + effect_node->hidden_by_backface_visibility); + + return effect_node->hidden_by_backface_visibility; } gfx::Rect LayerDrawableContentRect( diff --git a/chromium/cc/trees/frame_rate_estimator.cc b/chromium/cc/trees/frame_rate_estimator.cc new file mode 100644 index 00000000000..577b6a1d710 --- /dev/null +++ b/chromium/cc/trees/frame_rate_estimator.cc @@ -0,0 +1,84 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/trees/frame_rate_estimator.h" + +#include "components/viz/common/frame_sinks/begin_frame_args.h" + +namespace cc { +namespace { + +constexpr auto kInputPriorityDelay = base::TimeDelta::FromMilliseconds(250); + +} // namespace + +FrameRateEstimator::FrameRateEstimator(base::SequencedTaskRunner* task_runner) + : notifier_( + task_runner, + base::BindRepeating(&FrameRateEstimator::OnExitInputPriorityMode, + base::Unretained(this)), + kInputPriorityDelay) {} + +FrameRateEstimator::~FrameRateEstimator() = default; + +void FrameRateEstimator::SetFrameEstimationEnabled(bool enabled) { + if (enabled == frame_rate_estimation_enabled_) + return; + + frame_rate_estimation_enabled_ = enabled; + last_draw_time_ = base::TimeTicks(); + num_of_consecutive_frames_with_min_delta_ = 0u; +} + +void FrameRateEstimator::WillDraw(base::TimeTicks now) { + if (!frame_rate_estimation_enabled_ || input_priority_mode_) + return; + + if (last_draw_time_ == base::TimeTicks()) { + last_draw_time_ = now; + return; + } + + auto draw_delta = now - last_draw_time_; + last_draw_time_ = now; + + // If we see that the page is animating consistently at 30 fps or more, then + // we assume that BeginFrames can not be throttled. But if the animation + // frequency is lower than that, then using a lower frame rate is permitted. + // The delta below is to account for minor offsets in frame times. + constexpr auto kFudgeDelta = base::TimeDelta::FromMilliseconds(1); + constexpr auto kMinDelta = + (viz::BeginFrameArgs::DefaultInterval() * 2) + kFudgeDelta; + if (draw_delta < kMinDelta) + num_of_consecutive_frames_with_min_delta_++; + else + num_of_consecutive_frames_with_min_delta_ = 0u; +} + +base::TimeDelta FrameRateEstimator::GetPreferredInterval() const { + if (!frame_rate_estimation_enabled_ || input_priority_mode_) + return viz::BeginFrameArgs::MinInterval(); + + constexpr size_t kMinNumOfFramesWithMinDelta = 4u; + if (num_of_consecutive_frames_with_min_delta_ >= kMinNumOfFramesWithMinDelta) + return viz::BeginFrameArgs::MinInterval(); + + return viz::BeginFrameArgs::DefaultInterval() * 2; +} + +void FrameRateEstimator::NotifyInputEvent() { + if (!frame_rate_estimation_enabled_) + return; + + input_priority_mode_ = true; + notifier_.Schedule(); +} + +void FrameRateEstimator::OnExitInputPriorityMode() { + input_priority_mode_ = false; + last_draw_time_ = base::TimeTicks(); + num_of_consecutive_frames_with_min_delta_ = 0u; +} + +} // namespace cc diff --git a/chromium/cc/trees/frame_rate_estimator.h b/chromium/cc/trees/frame_rate_estimator.h new file mode 100644 index 00000000000..7ea16833ca5 --- /dev/null +++ b/chromium/cc/trees/frame_rate_estimator.h @@ -0,0 +1,48 @@ +// 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_TREES_FRAME_RATE_ESTIMATOR_H_ +#define CC_TREES_FRAME_RATE_ESTIMATOR_H_ + +#include "base/single_thread_task_runner.h" +#include "cc/base/delayed_unique_notifier.h" +#include "cc/cc_export.h" + +namespace cc { + +class CC_EXPORT FrameRateEstimator { + public: + explicit FrameRateEstimator(base::SequencedTaskRunner* task_runner); + ~FrameRateEstimator(); + + void SetFrameEstimationEnabled(bool enabled); + void WillDraw(base::TimeTicks now); + void NotifyInputEvent(); + base::TimeDelta GetPreferredInterval() const; + + private: + void OnExitInputPriorityMode(); + + // Set if an estimated frame rate should be used or we should assume the + // highest frame rate available. + bool frame_rate_estimation_enabled_ = false; + + // The frame time for the last drawn frame since frame estimation was + // enabled. + base::TimeTicks last_draw_time_; + + // The number of consecutive frames drawn within the time delta required to + // lower the frame rate. + size_t num_of_consecutive_frames_with_min_delta_ = 0u; + + // We conservatively switch to high frame rate after an input event to lower + // the input latency for a minimum duration. This tracks when we are in this + // mode. + bool input_priority_mode_ = false; + DelayedUniqueNotifier notifier_; +}; + +} // namespace cc + +#endif // CC_TREES_FRAME_RATE_ESTIMATOR_H_ diff --git a/chromium/cc/trees/frame_rate_estimator_unittest.cc b/chromium/cc/trees/frame_rate_estimator_unittest.cc new file mode 100644 index 00000000000..d99fabf842b --- /dev/null +++ b/chromium/cc/trees/frame_rate_estimator_unittest.cc @@ -0,0 +1,73 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/trees/frame_rate_estimator.h" + +#include "base/test/test_simple_task_runner.h" +#include "components/viz/common/frame_sinks/begin_frame_args.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace cc { +namespace { + +class FrameRateEstimatorTest : public testing::Test { + public: + FrameRateEstimatorTest() = default; + ~FrameRateEstimatorTest() override = default; + + void SetUp() override { + task_runner_ = base::MakeRefCounted<base::TestSimpleTaskRunner>(); + estimator_ = std::make_unique<FrameRateEstimator>(task_runner_.get()); + } + + void TearDown() override { + estimator_.reset(); + task_runner_.reset(); + } + + protected: + scoped_refptr<base::TestSimpleTaskRunner> task_runner_; + std::unique_ptr<FrameRateEstimator> estimator_; +}; + +TEST_F(FrameRateEstimatorTest, ToggleEstimationEnabled) { + EXPECT_EQ(estimator_->GetPreferredInterval(), + viz::BeginFrameArgs::MinInterval()); + estimator_->SetFrameEstimationEnabled(true); + EXPECT_NE(estimator_->GetPreferredInterval(), + viz::BeginFrameArgs::MinInterval()); + estimator_->SetFrameEstimationEnabled(false); + EXPECT_EQ(estimator_->GetPreferredInterval(), + viz::BeginFrameArgs::MinInterval()); +} + +TEST_F(FrameRateEstimatorTest, FrameHistoryUsed) { + estimator_->SetFrameEstimationEnabled(true); + EXPECT_NE(estimator_->GetPreferredInterval(), + viz::BeginFrameArgs::MinInterval()); + base::TimeTicks time; + for (int i = 0; i < 10; ++i) { + estimator_->WillDraw(time); + time += viz::BeginFrameArgs::DefaultInterval(); + } + EXPECT_EQ(estimator_->GetPreferredInterval(), + viz::BeginFrameArgs::MinInterval()); + + estimator_->WillDraw(time + (3 * viz::BeginFrameArgs::DefaultInterval())); + EXPECT_NE(estimator_->GetPreferredInterval(), + viz::BeginFrameArgs::MinInterval()); +} + +TEST_F(FrameRateEstimatorTest, InputPriorityMode) { + estimator_->SetFrameEstimationEnabled(true); + estimator_->NotifyInputEvent(); + EXPECT_EQ(estimator_->GetPreferredInterval(), + viz::BeginFrameArgs::MinInterval()); + + task_runner_->RunUntilIdle(); + EXPECT_NE(estimator_->GetPreferredInterval(), + viz::BeginFrameArgs::MinInterval()); +} +} // namespace +} // namespace cc diff --git a/chromium/cc/trees/latency_info_swap_promise.cc b/chromium/cc/trees/latency_info_swap_promise.cc index 2edc793bbc7..183e912e3b1 100644 --- a/chromium/cc/trees/latency_info_swap_promise.cc +++ b/chromium/cc/trees/latency_info_swap_promise.cc @@ -6,7 +6,7 @@ #include <stdint.h> -#include "base/logging.h" +#include "base/check.h" #include "base/trace_event/trace_event.h" #include "services/tracing/public/cpp/perfetto/flow_event_utils.h" #include "services/tracing/public/cpp/perfetto/macros.h" diff --git a/chromium/cc/trees/layer_tree_host.cc b/chromium/cc/trees/layer_tree_host.cc index cf848af9541..b1a4798682b 100644 --- a/chromium/cc/trees/layer_tree_host.cc +++ b/chromium/cc/trees/layer_tree_host.cc @@ -246,8 +246,9 @@ SwapPromiseManager* LayerTreeHost::GetSwapPromiseManager() { } std::unique_ptr<EventsMetricsManager::ScopedMonitor> -LayerTreeHost::GetScopedEventMetricsMonitor(const EventMetrics& event_metrics) { - return events_metrics_manager_.GetScopedMonitor(event_metrics); +LayerTreeHost::GetScopedEventMetricsMonitor( + std::unique_ptr<EventMetrics> event_metrics) { + return events_metrics_manager_.GetScopedMonitor(std::move(event_metrics)); } void LayerTreeHost::ClearEventsMetrics() { @@ -979,6 +980,11 @@ void LayerTreeHost::RecordEndOfFrameMetrics( client_->RecordEndOfFrameMetrics(frame_begin_time, trackers); } +void LayerTreeHost::NotifyThroughputTrackerResults( + CustomTrackerResults results) { + client_->NotifyThroughputTrackerResults(std::move(results)); +} + const base::WeakPtr<InputHandler>& LayerTreeHost::GetInputHandler() const { return input_handler_weak_ptr_; } diff --git a/chromium/cc/trees/layer_tree_host.h b/chromium/cc/trees/layer_tree_host.h index 60d3ff91e45..8aae435b230 100644 --- a/chromium/cc/trees/layer_tree_host.h +++ b/chromium/cc/trees/layer_tree_host.h @@ -179,7 +179,7 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient { SwapPromiseManager* GetSwapPromiseManager(); std::unique_ptr<EventsMetricsManager::ScopedMonitor> - GetScopedEventMetricsMonitor(const EventMetrics& event_metrics); + GetScopedEventMetricsMonitor(std::unique_ptr<EventMetrics> event_metrics); void ClearEventsMetrics(); // Visibility and LayerTreeFrameSink ------------------------------- @@ -592,6 +592,7 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient { void RecordStartOfFrameMetrics(); void RecordEndOfFrameMetrics(base::TimeTicks frame_begin_time, ActiveFrameSequenceTrackers trackers); + void NotifyThroughputTrackerResults(CustomTrackerResults results); LayerTreeHostClient* client() { return client_; } LayerTreeHostSchedulingClient* scheduling_client() { diff --git a/chromium/cc/trees/layer_tree_host_client.h b/chromium/cc/trees/layer_tree_host_client.h index cae833370e8..ae48bd94a18 100644 --- a/chromium/cc/trees/layer_tree_host_client.h +++ b/chromium/cc/trees/layer_tree_host_client.h @@ -10,7 +10,7 @@ #include "base/memory/ref_counted.h" #include "base/time/time.h" #include "cc/input/browser_controls_state.h" -#include "cc/metrics/frame_sequence_tracker.h" +#include "cc/metrics/frame_sequence_tracker_collection.h" #include "ui/gfx/geometry/scroll_offset.h" #include "ui/gfx/geometry/vector2d_f.h" @@ -168,9 +168,10 @@ class LayerTreeHostClient { // committed to the compositor, which is before the call to // RecordEndOfFrameMetrics. virtual std::unique_ptr<BeginMainFrameMetrics> GetBeginMainFrameMetrics() = 0; + virtual void NotifyThroughputTrackerResults(CustomTrackerResults results) = 0; protected: - virtual ~LayerTreeHostClient() {} + virtual ~LayerTreeHostClient() = default; }; // LayerTreeHost->WebThreadScheduler callback interface. Instances of this class diff --git a/chromium/cc/trees/layer_tree_host_impl.cc b/chromium/cc/trees/layer_tree_host_impl.cc index 1889f1df493..4fcc3210311 100644 --- a/chromium/cc/trees/layer_tree_host_impl.cc +++ b/chromium/cc/trees/layer_tree_host_impl.cc @@ -13,6 +13,7 @@ #include "base/auto_reset.h" #include "base/bind.h" +#include "base/command_line.h" #include "base/compiler_specific.h" #include "base/containers/adapters.h" #include "base/containers/flat_map.h" @@ -31,13 +32,13 @@ #include "cc/base/devtools_instrumentation.h" #include "cc/base/histograms.h" #include "cc/base/math_util.h" +#include "cc/base/switches.h" #include "cc/benchmarks/benchmark_instrumentation.h" #include "cc/debug/rendering_stats_instrumentation.h" #include "cc/input/browser_controls_offset_manager.h" #include "cc/input/main_thread_scrolling_reason.h" #include "cc/input/page_scale_animation.h" #include "cc/input/scroll_elasticity_helper.h" -#include "cc/input/scroll_input_type.h" #include "cc/input/scroll_state.h" #include "cc/input/scrollbar.h" #include "cc/input/scrollbar_animation_controller.h" @@ -53,6 +54,8 @@ #include "cc/layers/surface_layer_impl.h" #include "cc/layers/viewport.h" #include "cc/metrics/compositor_frame_reporting_controller.h" +#include "cc/metrics/frame_sequence_metrics.h" +#include "cc/metrics/lcd_text_metrics_reporter.h" #include "cc/paint/display_item_list.h" #include "cc/paint/paint_worklet_job.h" #include "cc/paint/paint_worklet_layer_painter.h" @@ -115,6 +118,7 @@ #include "services/metrics/public/cpp/ukm_recorder.h" #include "third_party/perfetto/protos/perfetto/trace/track_event/chrome_latency_info.pbzero.h" #include "third_party/skia/include/gpu/GrContext.h" +#include "ui/events/types/scroll_input_type.h" #include "ui/gfx/display_color_spaces.h" #include "ui/gfx/geometry/point_conversions.h" #include "ui/gfx/geometry/rect_conversions.h" @@ -138,15 +142,16 @@ const float kMobileViewportWidthEpsilon = 0.15f; // kHitTestAsk after the threshold is reached. const size_t kAssumeOverlapThreshold = 100; -FrameSequenceTrackerType GetTrackerTypeForScroll(ScrollInputType input_type) { +FrameSequenceTrackerType GetTrackerTypeForScroll( + ui::ScrollInputType input_type) { switch (input_type) { - case ScrollInputType::kWheel: + case ui::ScrollInputType::kWheel: return FrameSequenceTrackerType::kWheelScroll; - case ScrollInputType::kTouchscreen: + case ui::ScrollInputType::kTouchscreen: return FrameSequenceTrackerType::kTouchScroll; - case ScrollInputType::kScrollbar: + case ui::ScrollInputType::kScrollbar: return FrameSequenceTrackerType::kScrollbarScroll; - case ScrollInputType::kAutoscroll: + case ui::ScrollInputType::kAutoscroll: return FrameSequenceTrackerType::kMaxType; } } @@ -221,13 +226,13 @@ void DidVisibilityChange(LayerTreeHostImpl* id, bool visible) { enum ScrollThread { MAIN_THREAD, CC_THREAD }; -void RecordCompositorSlowScrollMetric(ScrollInputType type, +void RecordCompositorSlowScrollMetric(ui::ScrollInputType type, ScrollThread scroll_thread) { bool scroll_on_main_thread = (scroll_thread == MAIN_THREAD); - if (type == ScrollInputType::kWheel) { + if (type == ui::ScrollInputType::kWheel) { UMA_HISTOGRAM_BOOLEAN("Renderer4.CompositorWheelScrollUpdateThread", scroll_on_main_thread); - } else if (type == ScrollInputType::kTouchscreen) { + } else if (type == ui::ScrollInputType::kTouchscreen) { UMA_HISTOGRAM_BOOLEAN("Renderer4.CompositorTouchScrollUpdateThread", scroll_on_main_thread); } @@ -243,6 +248,30 @@ void PopulateMetadataContentColorUsage( } } +// These values are persisted to logs. Entries should not be renumbered and +// numeric values should never be reused. +enum class SourceIdConsistency : int { + kConsistent = 0, + kContainsInvalid = 1, + kNonUnique = 2, + kInvalidAndNonUnique = 3, + kMaxValue = kInvalidAndNonUnique, +}; + +void RecordSourceIdConsistency(bool all_valid, bool all_unique) { + SourceIdConsistency consistency = + all_unique ? (all_valid ? SourceIdConsistency::kConsistent + : SourceIdConsistency::kContainsInvalid) + : (all_valid ? SourceIdConsistency::kNonUnique + : SourceIdConsistency::kInvalidAndNonUnique); + + // TODO(crbug.com/1062764): we're sometimes seeing unexpected values for the + // ukm::SourceId. We'll use this histogram to track how often it happens so + // we can properly (de-)prioritize a fix. + UMA_HISTOGRAM_ENUMERATION("Event.LatencyInfo.Debug.SourceIdConsistency", + consistency); +} + } // namespace DEFINE_SCOPED_UMA_HISTOGRAM_TIMER(PendingTreeRasterDurationHistogramTimer, @@ -323,7 +352,9 @@ LayerTreeHostImpl::LayerTreeHostImpl( scrollbar_controller_(std::make_unique<ScrollbarController>(this)), frame_trackers_(settings.single_thread_proxy_scheduler, compositor_frame_reporting_controller_.get()), - scroll_gesture_did_end_(false) { + scroll_gesture_did_end_(false), + lcd_text_metrics_reporter_(LCDTextMetricsReporter::CreateIfNeeded(this)), + frame_rate_estimator_(GetTaskRunner()) { DCHECK(mutator_host_); mutator_host_->SetMutatorHostClient(this); mutator_events_ = mutator_host_->CreateEvents(); @@ -346,9 +377,12 @@ LayerTreeHostImpl::LayerTreeHostImpl( this, settings.top_controls_show_threshold, settings.top_controls_hide_threshold); - memory_pressure_listener_.reset( - new base::MemoryPressureListener(base::BindRepeating( - &LayerTreeHostImpl::OnMemoryPressure, base::Unretained(this)))); + if (!base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kDisableLayerTreeHostMemoryPressure)) { + memory_pressure_listener_.reset( + new base::MemoryPressureListener(base::BindRepeating( + &LayerTreeHostImpl::OnMemoryPressure, base::Unretained(this)))); + } SetDebugState(settings.initial_debug_state); } @@ -491,6 +525,15 @@ void LayerTreeHostImpl::CommitComplete() { frame_trackers_.StartSequence( FrameSequenceTrackerType::kMainThreadAnimation); } + + for (const auto& info : mutator_host_->TakePendingThroughputTrackerInfos()) { + const MutatorHost::TrackedAnimationSequenceId sequence_id = info.id; + const bool start = info.start; + if (start) + frame_trackers_.StartCustomSequence(sequence_id); + else + frame_trackers_.StopCustomSequence(sequence_id); + } } void LayerTreeHostImpl::UpdateSyncTreeAfterCommitOrImplSideInvalidation() { @@ -856,33 +899,6 @@ bool LayerTreeHostImpl::IsCurrentlyScrollingViewport() const { return viewport().ShouldScroll(*node); } -bool LayerTreeHostImpl::IsCurrentlyScrollingLayerAt( - const gfx::Point& viewport_point) const { - auto* scrolling_node = CurrentlyScrollingNode(); - if (!scrolling_node) - return false; - - gfx::PointF device_viewport_point = gfx::ScalePoint( - gfx::PointF(viewport_point), active_tree_->device_scale_factor()); - - LayerImpl* layer_impl = - active_tree_->FindLayerThatIsHitByPoint(device_viewport_point); - - bool scroll_on_main_thread = false; - uint32_t main_thread_scrolling_reasons; - auto* test_scroll_node = FindScrollNodeForDeviceViewportPoint( - device_viewport_point, layer_impl, &scroll_on_main_thread, - &main_thread_scrolling_reasons); - - if (scroll_on_main_thread) - return false; - - if (scrolling_node == test_scroll_node) - return true; - - return false; -} - EventListenerProperties LayerTreeHostImpl::GetEventListenerProperties( EventListenerClass event_class) const { return active_tree_->event_listener_properties(event_class); @@ -979,8 +995,8 @@ LayerTreeHostImpl::CreateLatencyInfoSwapPromiseMonitor( std::unique_ptr<EventsMetricsManager::ScopedMonitor> LayerTreeHostImpl::GetScopedEventMetricsMonitor( - const EventMetrics& event_metrics) { - return events_metrics_manager_.GetScopedMonitor(event_metrics); + std::unique_ptr<EventMetrics> event_metrics) { + return events_metrics_manager_.GetScopedMonitor(std::move(event_metrics)); } ScrollElasticityHelper* LayerTreeHostImpl::CreateScrollElasticityHelper() { @@ -1033,6 +1049,10 @@ bool LayerTreeHostImpl::ScrollingShouldSwitchtoMainThread() { return false; } +void LayerTreeHostImpl::NotifyInputEvent() { + frame_rate_estimator_.NotifyInputEvent(); +} + void LayerTreeHostImpl::QueueSwapPromiseForMainThreadScrollUpdate( std::unique_ptr<SwapPromise> swap_promise) { swap_promises_for_main_thread_scroll_update_.push_back( @@ -1257,6 +1277,7 @@ DrawResult LayerTreeHostImpl::CalculateRenderPasses(FrameData* frame) { bool have_copy_request = active_tree()->property_trees()->effect_tree.HasCopyRequests(); bool have_missing_animated_tiles = false; + int num_of_layers_with_videos = 0; // Advance our de-jelly state. This is a no-op if de-jelly is not active. de_jelly_state_.AdvanceFrame(active_tree_.get()); @@ -1296,8 +1317,10 @@ DrawResult LayerTreeHostImpl::CalculateRenderPasses(FrameData* frame) { DCHECK_EQ(active_tree_.get(), layer->layer_tree_impl()); frame->will_draw_layers.push_back(layer); - if (layer->may_contain_video()) + if (layer->may_contain_video()) { + num_of_layers_with_videos++; frame->may_contain_video = true; + } layer->AppendQuads(target_render_pass, &append_quads_data); if (settings_.allow_de_jelly_effect) { @@ -1362,6 +1385,11 @@ DrawResult LayerTreeHostImpl::CalculateRenderPasses(FrameData* frame) { draw_result = DRAW_ABORTED_MISSING_HIGH_RES_CONTENT; } + // Only enable frame rate estimation if it would help lower the composition + // rate for videos. + const bool enable_frame_rate_estimation = num_of_layers_with_videos > 1; + frame_rate_estimator_.SetFrameEstimationEnabled(enable_frame_rate_estimation); + // When doing a resourceless software draw, we don't have control over the // surface the compositor draws to, so even though the frame may not be // complete, the previous frame has already been potentially lost, so an @@ -2010,10 +2038,19 @@ void LayerTreeHostImpl::DidPresentCompositorFrame( // is in charge of posting them to the main thread. client_->DidPresentCompositorFrameOnImplThread( frame_token, std::move(activated.main_thread_callbacks), details); + + // Send throughput tracker results to main-thread if any. + auto throughput_tracker_results = frame_trackers_.TakeCustomTrackerResults(); + if (!throughput_tracker_results.empty()) { + client_->NotifyThroughputTrackerResults( + std::move(throughput_tracker_results)); + } } void LayerTreeHostImpl::DidNotNeedBeginFrame() { frame_trackers_.NotifyPauseFrameProduction(); + if (lcd_text_metrics_reporter_) + lcd_text_metrics_reporter_->NotifyPauseFrameProduction(); } void LayerTreeHostImpl::ReclaimResources( @@ -2318,6 +2355,11 @@ bool LayerTreeHostImpl::DrawLayers(FrameData* frame) { FrameSequenceTrackerType::kMainThreadAnimation); } + if (lcd_text_metrics_reporter_) { + lcd_text_metrics_reporter_->NotifySubmitFrame( + frame->origin_begin_main_frame_args); + } + // Clears the list of swap promises after calling DidSwap on each of them to // signal that the swap is over. active_tree()->ClearSwapPromises(); @@ -2404,6 +2446,10 @@ viz::CompositorFrame LayerTreeHostImpl::GenerateCompositorFrame( frame->deadline_in_frames.value_or(0u), CurrentBeginFrameArgs().interval, frame->use_default_lower_bound_deadline); + frame_rate_estimator_.WillDraw(CurrentBeginFrameArgs().frame_time); + metadata.preferred_frame_interval = + frame_rate_estimator_.GetPreferredInterval(); + metadata.activation_dependencies = std::move(frame->activation_dependencies); active_tree()->FinishSwapPromises(&metadata); // The swap-promises should not change the frame-token. @@ -2427,12 +2473,20 @@ viz::CompositorFrame LayerTreeHostImpl::GenerateCompositorFrame( active_tree()->TakeForceSendMetadataRequest()); } - if (!CommitToActiveTree()) { + if (!CommitToActiveTree() && !metadata.latency_info.empty()) { base::TimeTicks draw_time = base::TimeTicks::Now(); + + ukm::SourceId exemplar = metadata.latency_info.front().ukm_source_id(); + bool all_valid = true; + bool all_unique = true; for (auto& latency : metadata.latency_info) { latency.AddLatencyNumberWithTimestamp( ui::INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT, draw_time); + all_valid &= latency.ukm_source_id() != ukm::kInvalidSourceId; + all_unique &= latency.ukm_source_id() == exemplar; } + + RecordSourceIdConsistency(all_valid, all_unique); } ui::LatencyInfo::TraceIntermediateFlowEvents( metadata.latency_info, @@ -3745,7 +3799,7 @@ base::flat_set<int> LayerTreeHostImpl::NonFastScrollableNodes( return non_fast_scrollable_nodes; } -ScrollNode* LayerTreeHostImpl::FindScrollNodeForDeviceViewportPoint( +ScrollNode* LayerTreeHostImpl::FindScrollNodeForCompositedScrolling( const gfx::PointF& device_viewport_point, LayerImpl* layer_impl, bool* scroll_on_main_thread, @@ -3768,9 +3822,9 @@ ScrollNode* LayerTreeHostImpl::FindScrollNodeForDeviceViewportPoint( // layer corresponding to the scrollbars owner and then use its // scroll_tree_index instead. int scroll_tree_index = layer_impl->scroll_tree_index(); - if (layer_impl->ToScrollbarLayer()) { + if (layer_impl->IsScrollbarLayer()) { LayerImpl* owner_scroll_layer = active_tree_->LayerByElementId( - layer_impl->ToScrollbarLayer()->scroll_element_id()); + ToScrollbarLayer(layer_impl)->scroll_element_id()); scroll_tree_index = owner_scroll_layer->scroll_tree_index(); } @@ -3828,7 +3882,7 @@ ScrollNode* LayerTreeHostImpl::FindScrollNodeForDeviceViewportPoint( InputHandler::ScrollStatus LayerTreeHostImpl::RootScrollBegin( ScrollState* scroll_state, - ScrollInputType type) { + ui::ScrollInputType type) { TRACE_EVENT0("cc", "LayerTreeHostImpl::RootScrollBegin"); if (!OuterViewportScrollNode()) { ScrollStatus scroll_status; @@ -3845,7 +3899,7 @@ InputHandler::ScrollStatus LayerTreeHostImpl::RootScrollBegin( InputHandler::ScrollStatus LayerTreeHostImpl::ScrollBegin( ScrollState* scroll_state, - ScrollInputType type) { + ui::ScrollInputType type) { DCHECK(scroll_state); DCHECK(scroll_state->delta_x() == 0 && scroll_state->delta_y() == 0); @@ -3866,7 +3920,7 @@ InputHandler::ScrollStatus LayerTreeHostImpl::ScrollBegin( scroll_animating_snap_target_ids_ = TargetSnapAreaElementIds(); } - if (CurrentlyScrollingNode()) { + if (CurrentlyScrollingNode() && type == latched_scroll_type_) { // It's possible we haven't yet cleared the CurrentlyScrollingNode if we // received a GSE but we're still animating the last scroll. If that's the // case, we'll simply un-defer the GSE and continue latching to the same @@ -3932,7 +3986,7 @@ InputHandler::ScrollStatus LayerTreeHostImpl::ScrollBegin( } } - ScrollNode* starting_node = FindScrollNodeForDeviceViewportPoint( + ScrollNode* starting_node = FindScrollNodeForCompositedScrolling( device_viewport_point, layer_impl, &scroll_on_main_thread, &scroll_status.main_thread_scrolling_reasons); @@ -3980,14 +4034,51 @@ InputHandler::ScrollStatus LayerTreeHostImpl::ScrollBegin( return scroll_status; } +ScrollNode* LayerTreeHostImpl::HitTestScrollNode( + const gfx::PointF& device_viewport_point) const { + LayerImpl* layer_impl = + active_tree_->FindLayerThatIsHitByPoint(device_viewport_point); + + if (!layer_impl) + return nullptr; + + // There are some cases where the hit layer may not be correct (e.g. layer + // squashing). If we detect this case, we can't target a scroll node here. + { + LayerImpl* first_scrolling_layer_or_scrollbar = + active_tree_->FindFirstScrollingLayerOrScrollbarThatIsHitByPoint( + device_viewport_point); + + if (!IsInitialScrollHitTestReliable(layer_impl, + first_scrolling_layer_or_scrollbar)) { + TRACE_EVENT_INSTANT0("cc", "Failed Hit Test", TRACE_EVENT_SCOPE_THREAD); + return nullptr; + } + } + + // If we hit a scrollbar layer, get the ScrollNode from its associated + // scrolling layer, rather than directly from the scrollbar layer. The latter + // would return the parent scroller's ScrollNode. + if (layer_impl->IsScrollbarLayer()) { + layer_impl = active_tree_->LayerByElementId( + ToScrollbarLayer(layer_impl)->scroll_element_id()); + DCHECK(layer_impl); + } + + ScrollTree& scroll_tree = active_tree_->property_trees()->scroll_tree; + ScrollNode* scroll_node = scroll_tree.Node(layer_impl->scroll_tree_index()); + + return GetNodeToScroll(scroll_node); +} + // Requires falling back to main thread scrolling when it hit tests in scrollbar // from touch. bool LayerTreeHostImpl::IsTouchDraggingScrollbar( LayerImpl* first_scrolling_layer_or_scrollbar, - ScrollInputType type) { + ui::ScrollInputType type) { return first_scrolling_layer_or_scrollbar && - first_scrolling_layer_or_scrollbar->is_scrollbar() && - type == ScrollInputType::kTouchscreen; + first_scrolling_layer_or_scrollbar->IsScrollbarLayer() && + type == ui::ScrollInputType::kTouchscreen; } ScrollNode* LayerTreeHostImpl::GetNodeToScroll(ScrollNode* node) const { @@ -4022,28 +4113,14 @@ ScrollNode* LayerTreeHostImpl::GetNodeToScroll(ScrollNode* node) const { return node; } -// Initial scroll hit testing can be unreliable in the presence of squashed -// layers. In this case, we fall back to main thread scrolling. This function -// compares |layer_impl| returned from a regular hit test to the layer -// returned from a hit test performed only on scrollers and scrollbars. If the -// closest scrolling ancestor of |layer_impl| is not the other layer, then the -// layer_impl must be a squasing layer overtop of some other scroller and we -// must rely on the main thread. -// -// 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 LayerTreeHostImpl::IsInitialScrollHitTestReliable( LayerImpl* layer_impl, - LayerImpl* first_scrolling_layer_or_scrollbar) { + LayerImpl* first_scrolling_layer_or_scrollbar) const { if (!first_scrolling_layer_or_scrollbar) return true; // Hit tests directly on a composited scrollbar are always reliable. - if (layer_impl->ToScrollbarLayer()) { + if (layer_impl->IsScrollbarLayer()) { DCHECK(layer_impl == first_scrolling_layer_or_scrollbar); return true; } @@ -4065,7 +4142,7 @@ bool LayerTreeHostImpl::IsInitialScrollHitTestReliable( // a scrollabe layer with a scroll node. If this scroll node corresponds to // first scrollable ancestor along the scroll tree for |layer_impl|, the hit // test has not escaped to other areas of the scroll tree and is reliable. - if (!first_scrolling_layer_or_scrollbar->is_scrollbar()) { + if (!first_scrolling_layer_or_scrollbar->IsScrollbarLayer()) { return closest_scroll_node->id == first_scrolling_layer_or_scrollbar->scroll_tree_index(); } @@ -4382,7 +4459,7 @@ void LayerTreeHostImpl::ScrollLatchedScroller(ScrollState* scroll_state, // controls if needed. Viewport::ScrollResult result = viewport().ScrollBy( delta, viewport_point, scroll_state->is_direct_manipulation(), - latched_scroll_type_ != ScrollInputType::kWheel, + latched_scroll_type_ != ui::ScrollInputType::kWheel, scroll_node.scrolls_outer_viewport); applied_delta = result.consumed_delta; @@ -4438,7 +4515,7 @@ static bool CanPropagate(ScrollNode* scroll_node, float x, float y) { ScrollNode* LayerTreeHostImpl::FindNodeToLatch(ScrollState* scroll_state, ScrollNode* starting_node, - ScrollInputType type) { + ui::ScrollInputType type) { ScrollTree& scroll_tree = active_tree_->property_trees()->scroll_tree; ScrollNode* scroll_node = nullptr; for (ScrollNode* cur_node = starting_node; cur_node; @@ -4456,7 +4533,7 @@ ScrollNode* LayerTreeHostImpl::FindNodeToLatch(ScrollState* scroll_state, // For UX reasons, autoscrolling should always latch to the top-most // scroller, even if it can't scroll in the initial direction. - if (type == ScrollInputType::kAutoscroll || + if (type == ui::ScrollInputType::kAutoscroll || CanConsumeDelta(*scroll_state, *cur_node)) { scroll_node = cur_node; break; @@ -4483,7 +4560,7 @@ ScrollNode* LayerTreeHostImpl::FindNodeToLatch(ScrollState* scroll_state, } void LayerTreeHostImpl::DidLatchToScroller(const ScrollState& scroll_state, - ScrollInputType type) { + ui::ScrollInputType type) { DCHECK(CurrentlyScrollingNode()); deferred_scroll_end_ = false; browser_controls_offset_manager_->ScrollBegin(); @@ -4493,6 +4570,7 @@ void LayerTreeHostImpl::DidLatchToScroller(const ScrollState& scroll_state, scroll_animating_snap_target_ids_ = TargetSnapAreaElementIds(); last_latched_scroller_ = CurrentlyScrollingNode()->element_id; latched_scroll_type_ = type; + last_scroll_begin_state_ = scroll_state; client_->RenewTreePriority(); RecordCompositorSlowScrollMetric(type, CC_THREAD); @@ -4524,6 +4602,10 @@ bool LayerTreeHostImpl::CanConsumeDelta(const ScrollState& scroll_state, return false; } delta_to_scroll = local_scroll_delta; + } else if (scroll_state.delta_granularity() == + ui::ScrollGranularity::kScrollByPercentage) { + delta_to_scroll = + ResolveScrollPercentageToPixels(scroll_node, delta_to_scroll); } if (ComputeScrollDelta(scroll_node, delta_to_scroll) != gfx::Vector2dF()) @@ -4559,7 +4641,7 @@ bool LayerTreeHostImpl::ShouldAnimateScroll( // Mac does not smooth scroll wheel events (crbug.com/574283). We allow tests // to force it on. - return latched_scroll_type_ == ScrollInputType::kScrollbar + return latched_scroll_type_ == ui::ScrollInputType::kScrollbar ? true : force_smooth_wheel_scrolling_for_testing_; #else @@ -4737,13 +4819,20 @@ bool LayerTreeHostImpl::SnapAtScrollEnd() { SnapContainerData& data = scroll_node->snap_container_data.value(); gfx::ScrollOffset current_position = GetVisualScrollOffset(*scroll_node); - DCHECK(last_scroll_update_state_); + // You might think that if a scroll never received a scroll update we could + // just drop the snap. However, if the GSB+GSE arrived while we were mid-snap + // from a previous gesture, this would leave the scroller at a + // non-snap-point. + DCHECK(last_scroll_update_state_ || last_scroll_begin_state_); + ScrollState& last_scroll_state = last_scroll_update_state_ + ? *last_scroll_update_state_ + : *last_scroll_begin_state_; + bool imprecise_wheel_scrolling = - latched_scroll_type_ == ScrollInputType::kWheel && - last_scroll_update_state_->delta_granularity() != + latched_scroll_type_ == ui::ScrollInputType::kWheel && + last_scroll_state.delta_granularity() != ui::ScrollGranularity::kScrollByPrecisePixel; - gfx::ScrollOffset last_scroll_delta(last_scroll_update_state_->delta_x(), - last_scroll_update_state_->delta_y()); + gfx::ScrollOffset last_scroll_delta = last_scroll_state.DeltaOrHint(); std::unique_ptr<SnapSelectionStrategy> strategy; @@ -4869,9 +4958,11 @@ void LayerTreeHostImpl::ClearCurrentlyScrollingNode() { scroll_animating_snap_target_ids_ = TargetSnapAreaElementIds(); latched_scroll_type_.reset(); last_scroll_update_state_.reset(); + last_scroll_begin_state_.reset(); } void LayerTreeHostImpl::ScrollEnd(bool should_snap) { + scrollbar_controller_->ResetState(); if (!CurrentlyScrollingNode()) return; @@ -4899,7 +4990,7 @@ void LayerTreeHostImpl::ScrollEnd(bool should_snap) { } void LayerTreeHostImpl::RecordScrollBegin( - ScrollInputType input_type, + ui::ScrollInputType input_type, ScrollBeginThreadState scroll_start_state) { auto tracker_type = GetTrackerTypeForScroll(input_type); DCHECK_NE(tracker_type, FrameSequenceTrackerType::kMaxType); @@ -4927,7 +5018,7 @@ void LayerTreeHostImpl::RecordScrollBegin( metrics->SetScrollingThread(scrolling_thread); } -void LayerTreeHostImpl::RecordScrollEnd(ScrollInputType input_type) { +void LayerTreeHostImpl::RecordScrollEnd(ui::ScrollInputType input_type) { frame_trackers_.StopSequence(GetTrackerTypeForScroll(input_type)); } @@ -4987,33 +5078,18 @@ InputHandlerPointerResult LayerTreeHostImpl::MouseMoveAt( gfx::PointF device_viewport_point = gfx::ScalePoint( gfx::PointF(viewport_point), active_tree_->device_scale_factor()); - LayerImpl* layer_impl = - active_tree_->FindLayerThatIsHitByPoint(device_viewport_point); + ScrollNode* scroll_node = HitTestScrollNode(device_viewport_point); - // Check if mouse is over a scrollbar or not. - // TODO(sahel): get rid of this extera checking when - // FindScrollNodeForDeviceViewportPoint finds the proper node for scrolling on - // the main thread when the mouse is over a scrollbar as well. - ElementId scroll_element_id; - if (layer_impl && layer_impl->ToScrollbarLayer()) - scroll_element_id = layer_impl->ToScrollbarLayer()->scroll_element_id(); - if (!scroll_element_id) { - bool scroll_on_main_thread = false; - uint32_t main_thread_scrolling_reasons; - auto* scroll_node = FindScrollNodeForDeviceViewportPoint( - device_viewport_point, layer_impl, &scroll_on_main_thread, - &main_thread_scrolling_reasons); - if (scroll_node) - scroll_element_id = scroll_node->element_id; - - // Scrollbars for the viewport are registered with the outer viewport layer. - if (InnerViewportScrollNode() && - scroll_element_id == InnerViewportScrollNode()->element_id) { - DCHECK(OuterViewportScrollNode()); - scroll_element_id = OuterViewportScrollNode()->element_id; - } - } + // The hit test can fail in some cases, e.g. we don't know if a region of a + // squashed layer has content or is empty. + if (!scroll_node) + return result; + + // Scrollbars for the viewport are registered with the outer viewport layer. + if (scroll_node->scrolls_inner_viewport) + scroll_node = OuterViewportScrollNode(); + ElementId scroll_element_id = scroll_node->element_id; ScrollbarAnimationController* new_animation_controller = ScrollbarAnimationControllerForElementId(scroll_element_id); if (scroll_element_id != scroll_element_id_mouse_currently_over_) { @@ -5025,7 +5101,7 @@ InputHandlerPointerResult LayerTreeHostImpl::MouseMoveAt( scroll_element_id_mouse_currently_over_ = scroll_element_id; - // Experiment: Enables will flash scorllbar when user move mouse enter a + // Experiment: Enables will flash scrollbar when user move mouse enter a // scrollable area. if (settings_.scrollbar_flash_when_mouse_enter && new_animation_controller) new_animation_controller->DidScrollUpdate(); @@ -5418,7 +5494,7 @@ TreePriority LayerTreeHostImpl::GetTreePriority() const { return global_tile_state_.tree_priority; } -viz::BeginFrameArgs LayerTreeHostImpl::CurrentBeginFrameArgs() const { +const viz::BeginFrameArgs& LayerTreeHostImpl::CurrentBeginFrameArgs() const { // TODO(mithro): Replace call with current_begin_frame_tracker_.Current() // once all calls which happens outside impl frames are fixed. return current_begin_frame_tracker_.DangerousMethodCurrentOrLast(); @@ -6120,14 +6196,14 @@ void LayerTreeHostImpl::SetContextVisibility(bool is_visible) { } void LayerTreeHostImpl::UpdateScrollSourceInfo(const ScrollState& scroll_state, - ScrollInputType type) { - if (type == ScrollInputType::kWheel && + ui::ScrollInputType type) { + if (type == ui::ScrollInputType::kWheel && scroll_state.delta_granularity() == ui::ScrollGranularity::kScrollByPrecisePixel) { has_scrolled_by_precisiontouchpad_ = true; - } else if (type == ScrollInputType::kWheel) { + } else if (type == ui::ScrollInputType::kWheel) { has_scrolled_by_wheel_ = true; - } else if (type == ScrollInputType::kTouchscreen) { + } else if (type == ui::ScrollInputType::kTouchscreen) { has_scrolled_by_touch_ = true; } } diff --git a/chromium/cc/trees/layer_tree_host_impl.h b/chromium/cc/trees/layer_tree_host_impl.h index 2352d8e17c3..f7ce4e673e6 100644 --- a/chromium/cc/trees/layer_tree_host_impl.h +++ b/chromium/cc/trees/layer_tree_host_impl.h @@ -18,6 +18,7 @@ #include "base/containers/flat_set.h" #include "base/memory/memory_pressure_listener.h" #include "base/memory/shared_memory_mapping.h" +#include "base/optional.h" #include "base/sequenced_task_runner.h" #include "base/time/time.h" #include "cc/base/synced_property.h" @@ -25,13 +26,12 @@ #include "cc/cc_export.h" #include "cc/input/browser_controls_offset_manager_client.h" #include "cc/input/input_handler.h" -#include "cc/input/scroll_input_type.h" #include "cc/input/scrollbar_animation_controller.h" #include "cc/input/scrollbar_controller.h" #include "cc/layers/layer_collections.h" #include "cc/metrics/event_metrics.h" #include "cc/metrics/events_metrics_manager.h" -#include "cc/metrics/frame_sequence_tracker.h" +#include "cc/metrics/frame_sequence_tracker_collection.h" #include "cc/paint/discardable_image_map.h" #include "cc/paint/paint_worklet_job.h" #include "cc/resources/ui_resource_client.h" @@ -45,6 +45,7 @@ #include "cc/tiles/tile_manager.h" #include "cc/trees/animated_paint_worklet_tracker.h" #include "cc/trees/de_jelly_state.h" +#include "cc/trees/frame_rate_estimator.h" #include "cc/trees/layer_tree_frame_sink_client.h" #include "cc/trees/layer_tree_host.h" #include "cc/trees/layer_tree_mutator.h" @@ -63,6 +64,7 @@ #include "components/viz/common/surfaces/local_surface_id.h" #include "components/viz/common/surfaces/surface_id.h" #include "components/viz/common/surfaces/surface_range.h" +#include "ui/events/types/scroll_input_type.h" #include "ui/gfx/geometry/rect.h" namespace gfx { @@ -83,6 +85,7 @@ class DebugRectHistory; class EvictionTilePriorityQueue; class FrameRateCounter; class ImageAnimationController; +class LCDTextMetricsReporter; class LayerImpl; class LayerTreeFrameSink; class LayerTreeImpl; @@ -171,8 +174,10 @@ class LayerTreeHostImplClient { virtual void NotifyPaintWorkletStateChange( Scheduler::PaintWorkletState state) = 0; + virtual void NotifyThroughputTrackerResults(CustomTrackerResults results) = 0; + protected: - virtual ~LayerTreeHostImplClient() {} + virtual ~LayerTreeHostImplClient() = default; }; // LayerTreeHostImpl owns the LayerImpl trees as well as associated rendering @@ -261,9 +266,9 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler, // InputHandler implementation void BindToClient(InputHandlerClient* client) override; InputHandler::ScrollStatus ScrollBegin(ScrollState* scroll_state, - ScrollInputType type) override; + ui::ScrollInputType type) override; InputHandler::ScrollStatus RootScrollBegin(ScrollState* scroll_state, - ScrollInputType type) override; + ui::ScrollInputType type) override; InputHandlerScrollResult ScrollUpdate( ScrollState* scroll_state, base::TimeDelta delayed_by = base::TimeDelta()) override; @@ -271,9 +276,9 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler, void SetSynchronousInputHandlerRootScrollOffset( const gfx::ScrollOffset& root_content_offset) override; void ScrollEnd(bool should_snap = false) override; - void RecordScrollBegin(ScrollInputType input_type, + void RecordScrollBegin(ui::ScrollInputType input_type, ScrollBeginThreadState scroll_start_state) override; - void RecordScrollEnd(ScrollInputType input_type) override; + void RecordScrollEnd(ui::ScrollInputType input_type) override; InputHandlerPointerResult MouseDown(const gfx::PointF& viewport_point, bool shift_modifier) override; @@ -297,8 +302,6 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler, base::TimeDelta duration); void SetNeedsAnimateInput() override; bool IsCurrentlyScrollingViewport() const override; - bool IsCurrentlyScrollingLayerAt( - const gfx::Point& viewport_point) const override; EventListenerProperties GetEventListenerProperties( EventListenerClass event_class) const override; InputHandler::TouchStartOrMoveEventListenerType @@ -310,13 +313,15 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler, std::unique_ptr<SwapPromiseMonitor> CreateLatencyInfoSwapPromiseMonitor( ui::LatencyInfo* latency) override; std::unique_ptr<EventsMetricsManager::ScopedMonitor> - GetScopedEventMetricsMonitor(const EventMetrics& event_metrics) override; + GetScopedEventMetricsMonitor( + std::unique_ptr<EventMetrics> event_metrics) override; ScrollElasticityHelper* CreateScrollElasticityHelper() override; bool GetScrollOffsetForLayer(ElementId element_id, gfx::ScrollOffset* offset) override; bool ScrollLayerTo(ElementId element_id, const gfx::ScrollOffset& offset) override; bool ScrollingShouldSwitchtoMainThread() override; + void NotifyInputEvent() override; // BrowserControlsOffsetManagerClient implementation. float TopControlsHeight() const override; @@ -637,6 +642,9 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler, std::unique_ptr<ScrollAndScaleSet> ProcessScrollDeltas(); FrameRateCounter* fps_counter() { return fps_counter_.get(); } + base::Optional<int> current_universal_throughput() { + return frame_trackers_.current_universal_throughput(); + } MemoryHistory* memory_history() { return memory_history_.get(); } DebugRectHistory* debug_rect_history() { return debug_rect_history_.get(); } viz::ClientResourceProvider* resource_provider() { @@ -654,6 +662,9 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler, } MutatorHost* mutator_host() const { return mutator_host_.get(); } + ScrollbarController* scrollbar_controller_for_testing() const { + return scrollbar_controller_.get(); + } void SetDebugState(const LayerTreeDebugState& new_debug_state); const LayerTreeDebugState& debug_state() const { return debug_state_; } @@ -682,7 +693,7 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler, // TODO(mithro): Remove this methods which exposes the internal // viz::BeginFrameArgs to external callers. - virtual viz::BeginFrameArgs CurrentBeginFrameArgs() const; + virtual const viz::BeginFrameArgs& CurrentBeginFrameArgs() const; // Expected time between two begin impl frame calls. base::TimeDelta CurrentBeginFrameInterval() const; @@ -944,10 +955,25 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler, bool IsTouchDraggingScrollbar( LayerImpl* first_scrolling_layer_or_drawn_scrollbar, - ScrollInputType type); + ui::ScrollInputType type); + + // Initial scroll hit testing can be unreliable in the presence of squashed + // layers. In this case, we fall back to main thread scrolling. This function + // compares |layer_impl| returned from a regular hit test to the layer + // returned from a hit test performed only on scrollers and scrollbars. If the + // closest scrolling ancestor of |layer_impl| is not the other layer, then the + // layer_impl must be a squasing layer overtop of some other scroller and we + // must rely on the main thread. + // + // 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( LayerImpl* layer, - LayerImpl* first_scrolling_layer_or_drawn_scrollbar); + LayerImpl* first_scrolling_layer_or_drawn_scrollbar) 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 @@ -955,7 +981,7 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler, // starting_node is nullptr, returns nullptr; ScrollNode* FindNodeToLatch(ScrollState* scroll_state, ScrollNode* starting_node, - ScrollInputType type); + ui::ScrollInputType type); // Called during ScrollBegin once a scroller was successfully latched to // (i.e. it can and will consume scroll delta on the compositor thread). The @@ -964,7 +990,7 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler, // 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, - ScrollInputType type); + ui::ScrollInputType type); // Applies the scroll_state to the currently latched scroller. See comment in // InputHandler::ScrollUpdate declaration for the meaning of |delayed_by|. @@ -991,7 +1017,18 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler, void ClearCurrentlyScrollingNode(); - ScrollNode* FindScrollNodeForDeviceViewportPoint( + // Performs a hit test to determine the ScrollNode to use when scrolling at + // |viewport_point|. Can return nullptr if the hit test fails; see the + // comment in IsInitialScrollHitTestReliable + ScrollNode* HitTestScrollNode(const gfx::PointF& device_viewport_point) 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. + ScrollNode* FindScrollNodeForCompositedScrolling( const gfx::PointF& device_viewport_point, LayerImpl* layer_hit_by_point, bool* scroll_on_main_thread, @@ -1038,7 +1075,7 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler, // 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, - ScrollInputType type); + ui::ScrollInputType type); bool IsScrolledBy(LayerImpl* child, ScrollNode* ancestor); void ShowScrollbarsForImplScroll(ElementId element_id); @@ -1140,8 +1177,9 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler, // time a CompositorFrame is generated. gfx::Vector2dF scroll_accumulated_this_frame_; - // Tracks the last scroll update state received. Used to infer the most + // Tracks the last scroll update/begin state received. Used to infer the most // recent scroll type and direction. + base::Optional<ScrollState> last_scroll_begin_state_; base::Optional<ScrollState> last_scroll_update_state_; std::vector<std::unique_ptr<SwapPromise>> @@ -1321,7 +1359,7 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler, // The source device type that started the scroll gesture. Only set between a // ScrollBegin and ScrollEnd. - base::Optional<ScrollInputType> latched_scroll_type_; + base::Optional<ui::ScrollInputType> latched_scroll_type_; // Scroll animation can finish either before or after GSE arrival. // deferred_scroll_end_ is set when the GSE has arrvied before scroll @@ -1359,6 +1397,10 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler, EventsMetricsManager events_metrics_manager_; + std::unique_ptr<LCDTextMetricsReporter> lcd_text_metrics_reporter_; + + FrameRateEstimator frame_rate_estimator_; + // 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 c5c6a044aaa..3d1cfc76a5d 100644 --- a/chromium/cc/trees/layer_tree_host_impl_unittest.cc +++ b/chromium/cc/trees/layer_tree_host_impl_unittest.cc @@ -31,7 +31,6 @@ #include "cc/input/browser_controls_offset_manager.h" #include "cc/input/main_thread_scrolling_reason.h" #include "cc/input/page_scale_animation.h" -#include "cc/input/scroll_input_type.h" #include "cc/layers/append_quads_data.h" #include "cc/layers/heads_up_display_layer_impl.h" #include "cc/layers/layer_impl.h" @@ -93,6 +92,7 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/skia/include/core/SkMallocPixelRef.h" +#include "ui/events/types/scroll_input_type.h" #include "ui/gfx/geometry/angle_conversions.h" #include "ui/gfx/geometry/rect_conversions.h" #include "ui/gfx/geometry/size_conversions.h" @@ -261,6 +261,7 @@ class LayerTreeHostImplTest : public testing::Test, ElementListType tree_type) override {} void NotifyPaintWorkletStateChange( Scheduler::PaintWorkletState state) override {} + void NotifyThroughputTrackerResults(CustomTrackerResults results) override {} void set_reduce_memory_result(bool reduce_memory_result) { reduce_memory_result_ = reduce_memory_result; @@ -498,9 +499,9 @@ class LayerTreeHostImplTest : public testing::Test, // is not an ancestor of squash1 layer, we cannot scroll on impl thread. InputHandler::ScrollStatus status = host_impl_->ScrollBegin( BeginState(gfx::Point(230, 150), gfx::Vector2dF(0, 10), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel); + ui::ScrollInputType::kWheel); ASSERT_EQ(InputHandler::SCROLL_UNKNOWN, status.thread); ASSERT_EQ(MainThreadScrollingReason::kFailedHitTest, status.main_thread_scrolling_reasons); @@ -508,9 +509,9 @@ class LayerTreeHostImplTest : public testing::Test, // The point hits squash1 layer and also scrollbar layer. status = host_impl_->ScrollBegin( BeginState(gfx::Point(350, 150), gfx::Vector2dF(0, 10), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel); + ui::ScrollInputType::kWheel); ASSERT_EQ(InputHandler::SCROLL_UNKNOWN, status.thread); ASSERT_EQ(MainThreadScrollingReason::kFailedHitTest, status.main_thread_scrolling_reasons); @@ -519,9 +520,9 @@ class LayerTreeHostImplTest : public testing::Test, // is an ancestor of squash2 layer, we should scroll on impl. status = host_impl_->ScrollBegin( BeginState(gfx::Point(230, 450), gfx::Vector2dF(0, 10), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel); + ui::ScrollInputType::kWheel); ASSERT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread); } @@ -581,7 +582,7 @@ class LayerTreeHostImplTest : public testing::Test, std::unique_ptr<ScrollState> BeginState(const gfx::Point& point, const gfx::Vector2dF& delta_hint, - ScrollInputType type) { + ui::ScrollInputType type) { ScrollStateData scroll_state_data; scroll_state_data.is_beginning = true; scroll_state_data.position_x = point.x(); @@ -589,7 +590,7 @@ class LayerTreeHostImplTest : public testing::Test, scroll_state_data.delta_x_hint = delta_hint.x(); scroll_state_data.delta_y_hint = delta_hint.y(); scroll_state_data.is_direct_manipulation = - type == ScrollInputType::kTouchscreen; + type == ui::ScrollInputType::kTouchscreen; std::unique_ptr<ScrollState> scroll_state( new ScrollState(scroll_state_data)); return scroll_state; @@ -597,14 +598,14 @@ class LayerTreeHostImplTest : public testing::Test, std::unique_ptr<ScrollState> UpdateState(const gfx::Point& point, const gfx::Vector2dF& delta, - ScrollInputType type) { + ui::ScrollInputType type) { ScrollStateData scroll_state_data; scroll_state_data.delta_x = delta.x(); scroll_state_data.delta_y = delta.y(); scroll_state_data.position_x = point.x(); scroll_state_data.position_y = point.y(); scroll_state_data.is_direct_manipulation = - type == ScrollInputType::kTouchscreen; + type == ui::ScrollInputType::kTouchscreen; std::unique_ptr<ScrollState> scroll_state( new ScrollState(scroll_state_data)); return scroll_state; @@ -613,7 +614,7 @@ class LayerTreeHostImplTest : public testing::Test, std::unique_ptr<ScrollState> AnimatedUpdateState( const gfx::Point& point, const gfx::Vector2dF& delta) { - auto state = UpdateState(point, delta, ScrollInputType::kWheel); + auto state = UpdateState(point, delta, ui::ScrollInputType::kWheel); state->data()->delta_granularity = ui::ScrollGranularity::kScrollByPixel; return state; } @@ -1075,18 +1076,20 @@ TEST_F(CommitToPendingTreeLayerTreeHostImplTest, } TEST_F(LayerTreeHostImplTest, ScrollBeforeRootLayerAttached) { - InputHandler::ScrollStatus status = host_impl_->ScrollBegin( - BeginState(gfx::Point(), gfx::Vector2dF(0, 1), ScrollInputType::kWheel) - .get(), - ScrollInputType::kWheel); + InputHandler::ScrollStatus status = + host_impl_->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 1), + ui::ScrollInputType::kWheel) + .get(), + ui::ScrollInputType::kWheel); EXPECT_EQ(InputHandler::SCROLL_IGNORED, status.thread); EXPECT_EQ(MainThreadScrollingReason::kNoScrollingLayer, status.main_thread_scrolling_reasons); - status = host_impl_->RootScrollBegin( - BeginState(gfx::Point(), gfx::Vector2dF(0, 1), ScrollInputType::kWheel) - .get(), - ScrollInputType::kWheel); + status = + host_impl_->RootScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 1), + ui::ScrollInputType::kWheel) + .get(), + ui::ScrollInputType::kWheel); EXPECT_EQ(InputHandler::SCROLL_IGNORED, status.thread); EXPECT_EQ(MainThreadScrollingReason::kNoScrollingLayer, status.main_thread_scrolling_reasons); @@ -1106,10 +1109,10 @@ TEST_F(LayerTreeHostImplTest, ScrollUpdateAndEndNoOpWithoutBegin) { { EXPECT_FALSE(host_impl_->CurrentlyScrollingNode()); host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, 10), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, 10), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); EXPECT_FALSE(host_impl_->CurrentlyScrollingNode()); host_impl_->ScrollEnd(); @@ -1119,14 +1122,14 @@ TEST_F(LayerTreeHostImplTest, ScrollUpdateAndEndNoOpWithoutBegin) { { InputHandler::ScrollStatus status = host_impl_->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen); + ui::ScrollInputType::kTouchscreen); EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread); EXPECT_TRUE(host_impl_->CurrentlyScrollingNode()); host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, 10), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 10), CurrentScrollOffset(OuterViewportScrollLayer())); @@ -1148,8 +1151,8 @@ TEST_F(LayerTreeHostImplTest, TargetMainThreadScroller) { // Try on a node that should scroll on the compositor. Confirm it works. { - InputHandler::ScrollStatus status = - host_impl_->ScrollBegin(scroll_state.get(), ScrollInputType::kWheel); + InputHandler::ScrollStatus status = host_impl_->ScrollBegin( + scroll_state.get(), ui::ScrollInputType::kWheel); EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread); EXPECT_EQ(host_impl_->OuterViewportScrollNode(), host_impl_->CurrentlyScrollingNode()); @@ -1162,8 +1165,8 @@ TEST_F(LayerTreeHostImplTest, TargetMainThreadScroller) { MainThreadScrollingReason::kThreadedScrollingDisabled; { - InputHandler::ScrollStatus status = - host_impl_->ScrollBegin(scroll_state.get(), ScrollInputType::kWheel); + InputHandler::ScrollStatus status = host_impl_->ScrollBegin( + scroll_state.get(), ui::ScrollInputType::kWheel); EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread); EXPECT_EQ( host_impl_->OuterViewportScrollNode()->main_thread_scrolling_reasons, @@ -1176,21 +1179,21 @@ TEST_F(LayerTreeHostImplTest, ScrollRootCallsCommitAndRedraw) { SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100)); DrawFrame(); - InputHandler::ScrollStatus status = host_impl_->ScrollBegin( - BeginState(gfx::Point(), gfx::Vector2dF(0, 10), ScrollInputType::kWheel) - .get(), - ScrollInputType::kWheel); + InputHandler::ScrollStatus status = + host_impl_->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10), + ui::ScrollInputType::kWheel) + .get(), + ui::ScrollInputType::kWheel); EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread); EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain, status.main_thread_scrolling_reasons); - EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point())); + EXPECT_TRUE(host_impl_->CurrentlyScrollingNode()); host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, 10), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); - EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(0, 10))); host_impl_->ScrollEnd(); - EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point())); + EXPECT_FALSE(host_impl_->CurrentlyScrollingNode()); EXPECT_TRUE(did_request_redraw_); EXPECT_TRUE(did_request_commit_); } @@ -1212,9 +1215,9 @@ TEST_F(LayerTreeHostImplTest, ActivelyTouchScrollingOnlyAfterScrollMovement) { { InputHandler::ScrollStatus status = host_impl_->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen); + ui::ScrollInputType::kTouchscreen); EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread); EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain, status.main_thread_scrolling_reasons); @@ -1222,13 +1225,13 @@ TEST_F(LayerTreeHostImplTest, ActivelyTouchScrollingOnlyAfterScrollMovement) { // There is no extent upwards so the scroll won't consume any delta. host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, -10), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); EXPECT_FALSE(host_impl_->IsActivelyPrecisionScrolling()); // This should scroll so ensure the bit flips to true. host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, 10), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); EXPECT_TRUE(host_impl_->IsActivelyPrecisionScrolling()); host_impl_->ScrollEnd(); @@ -1240,10 +1243,11 @@ TEST_F(LayerTreeHostImplTest, ActivelyTouchScrollingOnlyAfterScrollMovement) { // Ensure an animated wheel scroll doesn't cause the bit to flip even when // scrolling occurs. { - InputHandler::ScrollStatus status = host_impl_->ScrollBegin( - BeginState(gfx::Point(), gfx::Vector2dF(0, 10), ScrollInputType::kWheel) - .get(), - ScrollInputType::kWheel); + InputHandler::ScrollStatus status = + host_impl_->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10), + ui::ScrollInputType::kWheel) + .get(), + ui::ScrollInputType::kWheel); EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread); EXPECT_FALSE(host_impl_->IsActivelyPrecisionScrolling()); @@ -1285,10 +1289,11 @@ TEST_F(LayerTreeHostImplTest, ActivelyTouchScrollingOnlyAfterScrollMovement) { TEST_F(LayerTreeHostImplTest, ScrollWithoutRootLayer) { // We should not crash when trying to scroll an empty layer tree. - InputHandler::ScrollStatus status = host_impl_->ScrollBegin( - BeginState(gfx::Point(), gfx::Vector2dF(0, 10), ScrollInputType::kWheel) - .get(), - ScrollInputType::kWheel); + InputHandler::ScrollStatus status = + host_impl_->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10), + ui::ScrollInputType::kWheel) + .get(), + ui::ScrollInputType::kWheel); EXPECT_EQ(InputHandler::SCROLL_IGNORED, status.thread); EXPECT_EQ(MainThreadScrollingReason::kNoScrollingLayer, status.main_thread_scrolling_reasons); @@ -1308,10 +1313,11 @@ TEST_F(LayerTreeHostImplTest, ScrollWithoutRenderer) { // We should not crash when trying to scroll after the renderer initialization // fails. - InputHandler::ScrollStatus status = host_impl_->ScrollBegin( - BeginState(gfx::Point(), gfx::Vector2dF(0, 10), ScrollInputType::kWheel) - .get(), - ScrollInputType::kWheel); + InputHandler::ScrollStatus status = + host_impl_->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10), + ui::ScrollInputType::kWheel) + .get(), + ui::ScrollInputType::kWheel); EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread); EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain, status.main_thread_scrolling_reasons); @@ -1328,9 +1334,9 @@ TEST_F(LayerTreeHostImplTest, ReplaceTreeWhileScrolling) { host_impl_ ->ScrollBegin(BeginState(gfx::Point(), gfx::ScrollOffsetToVector2dF(scroll_delta), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); ClearLayersAndPropertyTrees(host_impl_->active_tree()); @@ -1341,7 +1347,7 @@ TEST_F(LayerTreeHostImplTest, ReplaceTreeWhileScrolling) { // new tree. host_impl_->ScrollUpdate( UpdateState(gfx::Point(), gfx::ScrollOffsetToVector2dF(scroll_delta), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get()); host_impl_->ScrollEnd(); std::unique_ptr<ScrollAndScaleSet> scroll_info = @@ -1355,15 +1361,15 @@ TEST_F(LayerTreeHostImplTest, ActivateTreeScrollingNodeDisappeared) { auto status = host_impl_->ScrollBegin( BeginState(gfx::Point(30, 30), gfx::Vector2d(0, 10), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel); + ui::ScrollInputType::kWheel); EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread); EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain, status.main_thread_scrolling_reasons); - host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), gfx::Vector2d(0, 10), ScrollInputType::kWheel) - .get()); + host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, 10), + ui::ScrollInputType::kWheel) + .get()); EXPECT_TRUE(host_impl_->active_tree()->CurrentlyScrollingNode()); // Create the pending tree containing only the root layer. @@ -1398,10 +1404,11 @@ TEST_F(LayerTreeHostImplTest, ScrollBlocksOnWheelEventHandlers) { EXPECT_FALSE(host_impl_->HasBlockingWheelEventHandlerAt(gfx::Point(30, 30))); // But they don't influence the actual handling of the scroll gestures. - InputHandler::ScrollStatus status = host_impl_->ScrollBegin( - BeginState(gfx::Point(), gfx::Vector2d(0, 10), ScrollInputType::kWheel) - .get(), - ScrollInputType::kWheel); + InputHandler::ScrollStatus status = + host_impl_->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(0, 10), + ui::ScrollInputType::kWheel) + .get(), + ui::ScrollInputType::kWheel); EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread); EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain, status.main_thread_scrolling_reasons); @@ -1435,9 +1442,9 @@ TEST_F(LayerTreeHostImplTest, ScrollBlocksOnTouchEventHandlers) { // But they don't influence the actual handling of the scroll gestures. InputHandler::ScrollStatus status = host_impl_->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(0, 10), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen); + ui::ScrollInputType::kTouchscreen); EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread); EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain, status.main_thread_scrolling_reasons); @@ -1466,19 +1473,20 @@ TEST_F(LayerTreeHostImplTest, ShouldScrollOnMainThread) { MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects; DrawFrame(); - InputHandler::ScrollStatus status = host_impl_->ScrollBegin( - BeginState(gfx::Point(), gfx::Vector2d(0, 10), ScrollInputType::kWheel) - .get(), - ScrollInputType::kWheel); + InputHandler::ScrollStatus status = + host_impl_->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(0, 10), + ui::ScrollInputType::kWheel) + .get(), + ui::ScrollInputType::kWheel); EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread); EXPECT_EQ(MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects, status.main_thread_scrolling_reasons); status = host_impl_->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(0, 10), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen); + ui::ScrollInputType::kTouchscreen); EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread); EXPECT_EQ(MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects, status.main_thread_scrolling_reasons); @@ -1523,9 +1531,9 @@ TEST_F(LayerTreeHostImplTest, ScrolledOverlappingDrawnScrollbarLayer) { // scrollbar layer is a drawn scrollbar, we cannot scroll on the impl thread. auto status = host_impl_->ScrollBegin( BeginState(gfx::Point(350, 150), gfx::Vector2d(0, 10), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel); + ui::ScrollInputType::kWheel); EXPECT_EQ(InputHandler::SCROLL_UNKNOWN, status.thread); EXPECT_EQ(MainThreadScrollingReason::kFailedHitTest, status.main_thread_scrolling_reasons); @@ -1534,9 +1542,9 @@ TEST_F(LayerTreeHostImplTest, ScrolledOverlappingDrawnScrollbarLayer) { // the impl thread. status = host_impl_->ScrollBegin( BeginState(gfx::Point(350, 500), gfx::Vector2d(0, 10), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel); + ui::ScrollInputType::kWheel); EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread); EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain, status.main_thread_scrolling_reasons); @@ -1626,56 +1634,49 @@ TEST_F(LayerTreeHostImplTest, NonFastScrollableRegionBasic) { // All scroll types inside the non-fast scrollable region should fail. InputHandler::ScrollStatus status = host_impl_->ScrollBegin( BeginState(gfx::Point(25, 25), gfx::Vector2d(0, 10), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel); + ui::ScrollInputType::kWheel); EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread); EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion, status.main_thread_scrolling_reasons); - EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(25, 25))); status = host_impl_->ScrollBegin( BeginState(gfx::Point(25, 25), gfx::Vector2d(0, 10), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen); + ui::ScrollInputType::kTouchscreen); EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread); EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion, status.main_thread_scrolling_reasons); - EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(25, 25))); // All scroll types outside this region should succeed. status = host_impl_->ScrollBegin( BeginState(gfx::Point(75, 75), gfx::Vector2d(0, 10), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel); + ui::ScrollInputType::kWheel); EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread); EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain, status.main_thread_scrolling_reasons); - EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(75, 75))); - host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), gfx::Vector2d(0, 10), ScrollInputType::kWheel) - .get()); - EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(25, 25))); + host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, 10), + ui::ScrollInputType::kWheel) + .get()); host_impl_->ScrollEnd(); - EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(75, 75))); status = host_impl_->ScrollBegin( BeginState(gfx::Point(75, 75), gfx::Vector2d(0, 10), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen); + ui::ScrollInputType::kTouchscreen); EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread); EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain, status.main_thread_scrolling_reasons); - EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(75, 75))); host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, 10), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); host_impl_->ScrollEnd(); - EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(75, 75))); } TEST_F(LayerTreeHostImplTest, NonFastScrollableRegionWithOffset) { @@ -1692,25 +1693,24 @@ TEST_F(LayerTreeHostImplTest, NonFastScrollableRegionWithOffset) { // moved the layer left by 25 pixels. InputHandler::ScrollStatus status = host_impl_->ScrollBegin( BeginState(gfx::Point(40, 10), gfx::Vector2d(0, 1), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel); + ui::ScrollInputType::kWheel); EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread); EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain, status.main_thread_scrolling_reasons); - EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(40, 10))); - host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), gfx::Vector2d(0, 1), ScrollInputType::kWheel) - .get()); + host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, 1), + ui::ScrollInputType::kWheel) + .get()); host_impl_->ScrollEnd(); // This point is still inside the non-fast region. status = host_impl_->ScrollBegin( BeginState(gfx::Point(10, 10), gfx::Vector2d(0, 1), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel); + ui::ScrollInputType::kWheel); EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread); EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion, status.main_thread_scrolling_reasons); @@ -1723,9 +1723,9 @@ TEST_F(LayerTreeHostImplTest, ScrollHandlerNotPresent) { EXPECT_FALSE(host_impl_->scroll_affects_scroll_handler()); host_impl_->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(0, 10), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen); + ui::ScrollInputType::kTouchscreen); EXPECT_FALSE(host_impl_->scroll_affects_scroll_handler()); host_impl_->ScrollEnd(); EXPECT_FALSE(host_impl_->scroll_affects_scroll_handler()); @@ -1738,9 +1738,9 @@ TEST_F(LayerTreeHostImplTest, ScrollHandlerPresent) { EXPECT_FALSE(host_impl_->scroll_affects_scroll_handler()); host_impl_->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(0, 10), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen); + ui::ScrollInputType::kTouchscreen); EXPECT_TRUE(host_impl_->scroll_affects_scroll_handler()); host_impl_->ScrollEnd(); EXPECT_FALSE(host_impl_->scroll_affects_scroll_handler()); @@ -1752,9 +1752,9 @@ TEST_F(LayerTreeHostImplTest, ScrollUpdateReturnsCorrectValue) { InputHandler::ScrollStatus status = host_impl_->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(-10, 0), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen); + ui::ScrollInputType::kTouchscreen); EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread); EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain, status.main_thread_scrolling_reasons); @@ -1763,37 +1763,37 @@ TEST_F(LayerTreeHostImplTest, ScrollUpdateReturnsCorrectValue) { EXPECT_FALSE( host_impl_ ->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(-10, 0), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()) .did_scroll); EXPECT_FALSE( host_impl_ ->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, -10), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()) .did_scroll); EXPECT_FALSE( host_impl_ ->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(-10, -10), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()) .did_scroll); // Scrolling to the right/bottom will succeed. EXPECT_TRUE(host_impl_ ->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(10, 0), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()) .did_scroll); EXPECT_TRUE(host_impl_ ->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, 10), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()) .did_scroll); EXPECT_TRUE( host_impl_ ->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(10, 10), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()) .did_scroll); @@ -1801,19 +1801,19 @@ TEST_F(LayerTreeHostImplTest, ScrollUpdateReturnsCorrectValue) { EXPECT_TRUE( host_impl_ ->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(-10, 0), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()) .did_scroll); EXPECT_TRUE( host_impl_ ->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, -10), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()) .did_scroll); EXPECT_TRUE( host_impl_ ->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(-10, -10), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()) .did_scroll); @@ -1821,19 +1821,19 @@ TEST_F(LayerTreeHostImplTest, ScrollUpdateReturnsCorrectValue) { EXPECT_TRUE( host_impl_ ->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(10, -10), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()) .did_scroll); EXPECT_TRUE( host_impl_ ->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(-10, 0), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()) .did_scroll); EXPECT_TRUE( host_impl_ ->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(-10, 10), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()) .did_scroll); @@ -1841,7 +1841,7 @@ TEST_F(LayerTreeHostImplTest, ScrollUpdateReturnsCorrectValue) { EXPECT_TRUE( host_impl_ ->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(5000, 5000), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()) .did_scroll); } @@ -1856,16 +1856,17 @@ TEST_F(LayerTreeHostImplTest, ScrollSnapOnX) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(pointer_position, x_delta, - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), CurrentScrollOffset(overflow)); EXPECT_EQ(TargetSnapAreaElementIds(), GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds()); host_impl_->ScrollUpdate( - UpdateState(pointer_position, x_delta, ScrollInputType::kWheel).get()); + UpdateState(pointer_position, x_delta, ui::ScrollInputType::kWheel) + .get()); EXPECT_EQ(TargetSnapAreaElementIds(), GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds()); @@ -1900,16 +1901,17 @@ TEST_F(LayerTreeHostImplTest, ScrollSnapOnY) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(pointer_position, y_delta, - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), CurrentScrollOffset(overflow)); EXPECT_EQ(TargetSnapAreaElementIds(), GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds()); host_impl_->ScrollUpdate( - UpdateState(pointer_position, y_delta, ScrollInputType::kWheel).get()); + UpdateState(pointer_position, y_delta, ui::ScrollInputType::kWheel) + .get()); EXPECT_EQ(TargetSnapAreaElementIds(), GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds()); @@ -1943,17 +1945,17 @@ TEST_F(LayerTreeHostImplTest, ScrollSnapOnBoth) { gfx::Vector2dF delta(20, 20); EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ - ->ScrollBegin( - BeginState(pointer_position, delta, ScrollInputType::kWheel) - .get(), - ScrollInputType::kWheel) + ->ScrollBegin(BeginState(pointer_position, delta, + ui::ScrollInputType::kWheel) + .get(), + ui::ScrollInputType::kWheel) .thread); EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), CurrentScrollOffset(overflow)); EXPECT_EQ(TargetSnapAreaElementIds(), GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds()); host_impl_->ScrollUpdate( - UpdateState(pointer_position, delta, ScrollInputType::kWheel).get()); + UpdateState(pointer_position, delta, ui::ScrollInputType::kWheel).get()); EXPECT_EQ(TargetSnapAreaElementIds(), GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds()); @@ -1980,6 +1982,23 @@ TEST_F(LayerTreeHostImplTest, ScrollSnapOnBoth) { GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds()); } +// Simulate a ScrollBegin and ScrollEnd without any intervening ScrollUpdate. +// This test passes if it doesn't crash. +TEST_F(LayerTreeHostImplTest, SnapAfterEmptyScroll) { + CreateLayerForSnapping(); + + gfx::Point pointer_position(10, 10); + gfx::Vector2dF y_delta(0, 20); + EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, + host_impl_ + ->ScrollBegin(BeginState(pointer_position, y_delta, + ui::ScrollInputType::kWheel) + .get(), + ui::ScrollInputType::kWheel) + .thread); + host_impl_->ScrollEnd(true); +} + TEST_F(LayerTreeHostImplTest, ScrollSnapAfterAnimatedScroll) { LayerImpl* overflow = CreateLayerForSnapping(); @@ -1988,10 +2007,10 @@ TEST_F(LayerTreeHostImplTest, ScrollSnapAfterAnimatedScroll) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ - ->ScrollBegin( - BeginState(pointer_position, delta, ScrollInputType::kWheel) - .get(), - ScrollInputType::kWheel) + ->ScrollBegin(BeginState(pointer_position, delta, + ui::ScrollInputType::kWheel) + .get(), + ui::ScrollInputType::kWheel) .thread); host_impl_->ScrollUpdate(AnimatedUpdateState(pointer_position, delta).get()); host_impl_->ScrollEnd(); @@ -2046,14 +2065,15 @@ TEST_F(LayerTreeHostImplTest, SnapAnimationTargetUpdated) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(pointer_position, y_delta, - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), CurrentScrollOffset(overflow)); host_impl_->ScrollUpdate( - UpdateState(pointer_position, y_delta, ScrollInputType::kWheel).get()); + UpdateState(pointer_position, y_delta, ui::ScrollInputType::kWheel) + .get()); EXPECT_FALSE(host_impl_->IsAnimatingForSnap()); viz::BeginFrameArgs begin_frame_args = @@ -2108,14 +2128,15 @@ TEST_F(LayerTreeHostImplTest, SnapAnimationCancelledByScroll) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(pointer_position, x_delta, - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), CurrentScrollOffset(overflow)); host_impl_->ScrollUpdate( - UpdateState(pointer_position, x_delta, ScrollInputType::kWheel).get()); + UpdateState(pointer_position, x_delta, ui::ScrollInputType::kWheel) + .get()); EXPECT_FALSE(host_impl_->IsAnimatingForSnap()); viz::BeginFrameArgs begin_frame_args = @@ -2139,12 +2160,13 @@ TEST_F(LayerTreeHostImplTest, SnapAnimationCancelledByScroll) { // Interrupt the snap animation with ScrollBegin. auto begin_state = - BeginState(pointer_position, x_delta, ScrollInputType::kWheel); + BeginState(pointer_position, x_delta, ui::ScrollInputType::kWheel); begin_state->data()->delta_granularity = ui::ScrollGranularity::kScrollByPrecisePixel; - EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_->ScrollBegin(begin_state.get(), ScrollInputType::kWheel) - .thread); + EXPECT_EQ( + InputHandler::SCROLL_ON_IMPL_THREAD, + host_impl_->ScrollBegin(begin_state.get(), ui::ScrollInputType::kWheel) + .thread); EXPECT_FALSE(host_impl_->IsAnimatingForSnap()); EXPECT_EQ(TargetSnapAreaElementIds(), GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds()); @@ -2170,15 +2192,16 @@ TEST_F(LayerTreeHostImplTest, EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(pointer_position, x_delta, - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), CurrentScrollOffset(overflow)); // There is a snap target at 50, scroll to it directly. host_impl_->ScrollUpdate( - UpdateState(pointer_position, x_delta, ScrollInputType::kWheel).get()); + UpdateState(pointer_position, x_delta, ui::ScrollInputType::kWheel) + .get()); EXPECT_FALSE(host_impl_->IsAnimatingForSnap()); viz::BeginFrameArgs begin_frame_args = @@ -2218,16 +2241,16 @@ TEST_F(LayerTreeHostImplTest, gfx::Vector2dF delta(4, 4); EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ - ->ScrollBegin( - BeginState(pointer_position, delta, ScrollInputType::kWheel) - .get(), - ScrollInputType::kWheel) + ->ScrollBegin(BeginState(pointer_position, delta, + ui::ScrollInputType::kWheel) + .get(), + ui::ScrollInputType::kWheel) .thread); EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), CurrentScrollOffset(overflow)); // Should be (20, 20) in the scroller's coordinate. InputHandlerScrollResult result = host_impl_->ScrollUpdate( - UpdateState(pointer_position, delta, ScrollInputType::kWheel).get()); + UpdateState(pointer_position, delta, ui::ScrollInputType::kWheel).get()); EXPECT_VECTOR_EQ(gfx::Vector2dF(20, 20), CurrentScrollOffset(overflow)); EXPECT_VECTOR_EQ(gfx::Vector2dF(4, 4), result.current_visual_offset); @@ -2257,16 +2280,16 @@ TEST_F(LayerTreeHostImplTest, SnapFlingAnimationEndWithoutFinishing) { gfx::Vector2dF delta(4, 4); EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ - ->ScrollBegin( - BeginState(pointer_position, delta, ScrollInputType::kWheel) - .get(), - ScrollInputType::kWheel) + ->ScrollBegin(BeginState(pointer_position, delta, + ui::ScrollInputType::kWheel) + .get(), + ui::ScrollInputType::kWheel) .thread); EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), CurrentScrollOffset(overflow)); // Should be (20, 20) in the scroller's coordinate. InputHandlerScrollResult result = host_impl_->ScrollUpdate( - UpdateState(pointer_position, delta, ScrollInputType::kWheel).get()); + UpdateState(pointer_position, delta, ui::ScrollInputType::kWheel).get()); EXPECT_VECTOR_EQ(gfx::Vector2dF(20, 20), CurrentScrollOffset(overflow)); EXPECT_VECTOR_EQ(gfx::Vector2dF(4, 4), result.current_visual_offset); @@ -2309,15 +2332,16 @@ TEST_F(LayerTreeHostImplTest, OverscrollBehaviorPreventsPropagation) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(pointer_position, x_delta, - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); EXPECT_VECTOR_EQ(gfx::Vector2dF(30, 30), CurrentScrollOffset(scroll_layer)); EXPECT_VECTOR_EQ(gfx::Vector2dF(), CurrentScrollOffset(overflow)); host_impl_->ScrollUpdate( - UpdateState(pointer_position, x_delta, ScrollInputType::kWheel).get()); + UpdateState(pointer_position, x_delta, ui::ScrollInputType::kWheel) + .get()); host_impl_->ScrollEnd(); EXPECT_VECTOR_EQ(gfx::Vector2dF(20, 30), CurrentScrollOffset(scroll_layer)); EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), CurrentScrollOffset(overflow)); @@ -2333,15 +2357,16 @@ TEST_F(LayerTreeHostImplTest, OverscrollBehaviorPreventsPropagation) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(pointer_position, x_delta, - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); EXPECT_VECTOR_EQ(gfx::Vector2dF(20, 30), CurrentScrollOffset(scroll_layer)); EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), CurrentScrollOffset(overflow)); host_impl_->ScrollUpdate( - UpdateState(pointer_position, x_delta, ScrollInputType::kWheel).get()); + UpdateState(pointer_position, x_delta, ui::ScrollInputType::kWheel) + .get()); host_impl_->ScrollEnd(); EXPECT_VECTOR_EQ(gfx::Vector2dF(20, 30), CurrentScrollOffset(scroll_layer)); EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), CurrentScrollOffset(overflow)); @@ -2351,15 +2376,16 @@ TEST_F(LayerTreeHostImplTest, OverscrollBehaviorPreventsPropagation) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(pointer_position, y_delta, - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); EXPECT_VECTOR_EQ(gfx::Vector2dF(20, 30), CurrentScrollOffset(scroll_layer)); EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), CurrentScrollOffset(overflow)); host_impl_->ScrollUpdate( - UpdateState(pointer_position, y_delta, ScrollInputType::kWheel).get()); + UpdateState(pointer_position, y_delta, ui::ScrollInputType::kWheel) + .get()); host_impl_->ScrollEnd(); EXPECT_VECTOR_EQ(gfx::Vector2dF(20, 20), CurrentScrollOffset(scroll_layer)); EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), CurrentScrollOffset(overflow)); @@ -2369,15 +2395,15 @@ TEST_F(LayerTreeHostImplTest, OverscrollBehaviorPreventsPropagation) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(pointer_position, diagonal_delta, - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); EXPECT_VECTOR_EQ(gfx::Vector2dF(20, 20), CurrentScrollOffset(scroll_layer)); EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), CurrentScrollOffset(overflow)); host_impl_->ScrollUpdate( - UpdateState(pointer_position, diagonal_delta, ScrollInputType::kWheel) + UpdateState(pointer_position, diagonal_delta, ui::ScrollInputType::kWheel) .get()); host_impl_->ScrollEnd(); EXPECT_VECTOR_EQ(gfx::Vector2dF(20, 20), CurrentScrollOffset(scroll_layer)); @@ -2395,15 +2421,16 @@ TEST_F(LayerTreeHostImplTest, OverscrollBehaviorPreventsPropagation) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(pointer_position, x_delta, - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); EXPECT_VECTOR_EQ(gfx::Vector2dF(20, 20), CurrentScrollOffset(scroll_layer)); EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), CurrentScrollOffset(overflow)); host_impl_->ScrollUpdate( - UpdateState(pointer_position, x_delta, ScrollInputType::kWheel).get()); + UpdateState(pointer_position, x_delta, ui::ScrollInputType::kWheel) + .get()); host_impl_->ScrollEnd(); EXPECT_VECTOR_EQ(gfx::Vector2dF(10, 20), CurrentScrollOffset(scroll_layer)); EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), CurrentScrollOffset(overflow)); @@ -2413,15 +2440,16 @@ TEST_F(LayerTreeHostImplTest, OverscrollBehaviorPreventsPropagation) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(pointer_position, y_delta, - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); EXPECT_VECTOR_EQ(gfx::Vector2dF(10, 20), CurrentScrollOffset(scroll_layer)); EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), CurrentScrollOffset(overflow)); host_impl_->ScrollUpdate( - UpdateState(pointer_position, y_delta, ScrollInputType::kWheel).get()); + UpdateState(pointer_position, y_delta, ui::ScrollInputType::kWheel) + .get()); host_impl_->ScrollEnd(); EXPECT_VECTOR_EQ(gfx::Vector2dF(10, 20), CurrentScrollOffset(scroll_layer)); EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), CurrentScrollOffset(overflow)); @@ -2431,15 +2459,15 @@ TEST_F(LayerTreeHostImplTest, OverscrollBehaviorPreventsPropagation) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(pointer_position, diagonal_delta, - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); EXPECT_VECTOR_EQ(gfx::Vector2dF(10, 20), CurrentScrollOffset(scroll_layer)); EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), CurrentScrollOffset(overflow)); host_impl_->ScrollUpdate( - UpdateState(pointer_position, diagonal_delta, ScrollInputType::kWheel) + UpdateState(pointer_position, diagonal_delta, ui::ScrollInputType::kWheel) .get()); host_impl_->ScrollEnd(); EXPECT_VECTOR_EQ(gfx::Vector2dF(10, 20), CurrentScrollOffset(scroll_layer)); @@ -2456,21 +2484,25 @@ TEST_F(LayerTreeHostImplTest, OverscrollBehaviorPreventsPropagation) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(pointer_position, x_delta, - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); EXPECT_VECTOR_EQ(gfx::Vector2dF(10, 20), CurrentScrollOffset(scroll_layer)); EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), CurrentScrollOffset(overflow)); host_impl_->ScrollUpdate( - UpdateState(pointer_position, x_delta, ScrollInputType::kWheel).get()); + UpdateState(pointer_position, x_delta, ui::ScrollInputType::kWheel) + .get()); host_impl_->ScrollUpdate( - UpdateState(pointer_position, -x_delta, ScrollInputType::kWheel).get()); + UpdateState(pointer_position, -x_delta, ui::ScrollInputType::kWheel) + .get()); host_impl_->ScrollUpdate( - UpdateState(pointer_position, y_delta, ScrollInputType::kWheel).get()); + UpdateState(pointer_position, y_delta, ui::ScrollInputType::kWheel) + .get()); host_impl_->ScrollUpdate( - UpdateState(pointer_position, -y_delta, ScrollInputType::kWheel).get()); + UpdateState(pointer_position, -y_delta, ui::ScrollInputType::kWheel) + .get()); host_impl_->ScrollEnd(); EXPECT_VECTOR_EQ(gfx::Vector2dF(10, 20), CurrentScrollOffset(scroll_layer)); EXPECT_VECTOR_EQ(gfx::Vector2dF(10, 10), CurrentScrollOffset(overflow)); @@ -2494,15 +2526,15 @@ TEST_F(LayerTreeHostImplTest, ScrollWithUserUnscrollableLayers) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(scroll_position, scroll_delta, - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); EXPECT_VECTOR_EQ(gfx::Vector2dF(), CurrentScrollOffset(scroll_layer)); EXPECT_VECTOR_EQ(gfx::Vector2dF(), CurrentScrollOffset(overflow)); host_impl_->ScrollUpdate( - UpdateState(scroll_position, scroll_delta, ScrollInputType::kWheel) + UpdateState(scroll_position, scroll_delta, ui::ScrollInputType::kWheel) .get()); host_impl_->ScrollEnd(); EXPECT_VECTOR_EQ(gfx::Vector2dF(), CurrentScrollOffset(scroll_layer)); @@ -2515,15 +2547,15 @@ TEST_F(LayerTreeHostImplTest, ScrollWithUserUnscrollableLayers) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(scroll_position, scroll_delta, - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); EXPECT_VECTOR_EQ(gfx::Vector2dF(), CurrentScrollOffset(scroll_layer)); EXPECT_VECTOR_EQ(gfx::Vector2dF(10, 10), CurrentScrollOffset(overflow)); host_impl_->ScrollUpdate( - UpdateState(scroll_position, scroll_delta, ScrollInputType::kWheel) + UpdateState(scroll_position, scroll_delta, ui::ScrollInputType::kWheel) .get()); host_impl_->ScrollEnd(); EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), CurrentScrollOffset(scroll_layer)); @@ -2535,15 +2567,15 @@ TEST_F(LayerTreeHostImplTest, ScrollWithUserUnscrollableLayers) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(scroll_position, scroll_delta, - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), CurrentScrollOffset(scroll_layer)); EXPECT_VECTOR_EQ(gfx::Vector2dF(10, 20), CurrentScrollOffset(overflow)); host_impl_->ScrollUpdate( - UpdateState(scroll_position, scroll_delta, ScrollInputType::kWheel) + UpdateState(scroll_position, scroll_delta, ui::ScrollInputType::kWheel) .get()); host_impl_->ScrollEnd(); EXPECT_VECTOR_EQ(gfx::Vector2dF(10, 10), CurrentScrollOffset(scroll_layer)); @@ -2560,9 +2592,9 @@ TEST_F(LayerTreeHostImplTest, ForceMainThreadScrollWithoutScrollLayer) { InputHandler::ScrollStatus status = host_impl_->ScrollBegin( BeginState(gfx::Point(25, 25), gfx::Vector2dF(0, 10), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel); + ui::ScrollInputType::kWheel); EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread); EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion, status.main_thread_scrolling_reasons); @@ -2819,9 +2851,9 @@ TEST_F(LayerTreeHostImplTest, ImplPinchZoom) { // gesture that doesn't cause (initial) scrolling? // https://crbug.com/1030262 host_impl_->ScrollBegin(BeginState(gfx::Point(50, 50), gfx::Vector2dF(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen); + ui::ScrollInputType::kTouchscreen); host_impl_->PinchGestureBegin(); host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point(50, 50)); host_impl_->PinchGestureEnd(gfx::Point(50, 50), true); @@ -2848,9 +2880,9 @@ TEST_F(LayerTreeHostImplTest, ImplPinchZoom) { float page_scale_delta = 2; host_impl_->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen); + ui::ScrollInputType::kTouchscreen); host_impl_->PinchGestureBegin(); host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point()); host_impl_->PinchGestureEnd(gfx::Point(), true); @@ -2860,12 +2892,13 @@ TEST_F(LayerTreeHostImplTest, ImplPinchZoom) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(5, 5), scroll_delta, - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), scroll_delta, ScrollInputType::kWheel).get()); + UpdateState(gfx::Point(), scroll_delta, ui::ScrollInputType::kWheel) + .get()); host_impl_->ScrollEnd(); std::unique_ptr<ScrollAndScaleSet> scroll_info = @@ -2952,9 +2985,9 @@ TEST_F(LayerTreeHostImplTest, ViewportScrollOrder) { MaxScrollOffset(outer_scroll_layer)); host_impl_->ScrollBegin(BeginState(gfx::Point(250, 250), gfx::Vector2dF(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen); + ui::ScrollInputType::kTouchscreen); host_impl_->PinchGestureBegin(); host_impl_->PinchGestureUpdate(2, gfx::Point(0, 0)); host_impl_->PinchGestureEnd(gfx::Point(0, 0), true); @@ -2968,12 +3001,12 @@ TEST_F(LayerTreeHostImplTest, ViewportScrollOrder) { // Scroll down - only the inner viewport should scroll. host_impl_->ScrollBegin(BeginState(gfx::Point(0, 0), gfx::Vector2dF(100, 100), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen); + ui::ScrollInputType::kTouchscreen); host_impl_->ScrollUpdate(UpdateState(gfx::Point(0, 0), gfx::Vector2dF(100, 100), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); host_impl_->ScrollEnd(); @@ -2986,12 +3019,12 @@ TEST_F(LayerTreeHostImplTest, ViewportScrollOrder) { // its maximum. host_impl_->ScrollBegin( BeginState(gfx::Point(0, 0), gfx::Vector2dF(1000, 1000), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen); + ui::ScrollInputType::kTouchscreen); host_impl_->ScrollUpdate(UpdateState(gfx::Point(0, 0), gfx::Vector2dF(1000, 1000), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); host_impl_->ScrollEnd(); @@ -3025,12 +3058,12 @@ TEST_F(LayerTreeHostImplTest, ScrollViewportWithFractionalAmounts) { // Scroll only the layout viewport. host_impl_->ScrollBegin( BeginState(gfx::Point(250, 250), gfx::Vector2dF(0.125f, 0.125f), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen); + ui::ScrollInputType::kTouchscreen); host_impl_->ScrollUpdate(UpdateState(gfx::Point(250, 250), gfx::Vector2dF(0.125f, 0.125f), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); EXPECT_VECTOR2DF_EQ(gfx::Vector2dF(0.125f, 0.125f), CurrentScrollOffset(outer_scroll_layer)); @@ -3043,12 +3076,12 @@ TEST_F(LayerTreeHostImplTest, ScrollViewportWithFractionalAmounts) { // Now that we zoomed in, the scroll should be applied to the inner viewport. host_impl_->ScrollBegin( BeginState(gfx::Point(250, 250), gfx::Vector2dF(0.5f, 0.5f), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen); + ui::ScrollInputType::kTouchscreen); host_impl_->ScrollUpdate(UpdateState(gfx::Point(250, 250), gfx::Vector2dF(0.5f, 0.5f), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); EXPECT_VECTOR2DF_EQ(gfx::Vector2dF(0.125f, 0.125f), CurrentScrollOffset(outer_scroll_layer)); @@ -3079,9 +3112,9 @@ TEST_F(LayerTreeHostImplTest, ScrollDuringPinchGesture) { MaxScrollOffset(outer_scroll_layer)); host_impl_->ScrollBegin(BeginState(gfx::Point(250, 250), gfx::Vector2dF(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen); + ui::ScrollInputType::kTouchscreen); host_impl_->PinchGestureBegin(); host_impl_->PinchGestureUpdate(2, gfx::Point(250, 250)); @@ -3095,7 +3128,7 @@ TEST_F(LayerTreeHostImplTest, ScrollDuringPinchGesture) { host_impl_->ScrollUpdate(UpdateState(gfx::Point(250, 250), gfx::Vector2dF(10, 10), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), CurrentScrollOffset(outer_scroll_layer)); @@ -3106,7 +3139,7 @@ TEST_F(LayerTreeHostImplTest, ScrollDuringPinchGesture) { host_impl_->ScrollUpdate(UpdateState(gfx::Point(250, 250), gfx::Vector2dF(400, 400), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); EXPECT_VECTOR_EQ(gfx::Vector2dF(80, 80), CurrentScrollOffset(outer_scroll_layer)); @@ -3136,8 +3169,9 @@ TEST_F(LayerTreeHostImplTest, PinchZoomSnapsToScreenEdge) { // Pinch in within the margins. The scroll should stay exactly locked to the // bottom and right. host_impl_->ScrollBegin( - BeginState(anchor, gfx::Vector2dF(), ScrollInputType::kTouchscreen).get(), - ScrollInputType::kTouchscreen); + BeginState(anchor, gfx::Vector2dF(), ui::ScrollInputType::kTouchscreen) + .get(), + ui::ScrollInputType::kTouchscreen); host_impl_->PinchGestureBegin(); host_impl_->PinchGestureUpdate(2, anchor); host_impl_->PinchGestureEnd(anchor, true); @@ -3155,8 +3189,9 @@ TEST_F(LayerTreeHostImplTest, PinchZoomSnapsToScreenEdge) { // top and left. anchor = gfx::Point(offsetFromEdge, offsetFromEdge); host_impl_->ScrollBegin( - BeginState(anchor, gfx::Vector2dF(), ScrollInputType::kTouchscreen).get(), - ScrollInputType::kTouchscreen); + BeginState(anchor, gfx::Vector2dF(), ui::ScrollInputType::kTouchscreen) + .get(), + ui::ScrollInputType::kTouchscreen); host_impl_->PinchGestureBegin(); host_impl_->PinchGestureUpdate(2, anchor); host_impl_->PinchGestureEnd(anchor, true); @@ -3174,8 +3209,9 @@ TEST_F(LayerTreeHostImplTest, PinchZoomSnapsToScreenEdge) { offsetFromEdge = Viewport::kPinchZoomSnapMarginDips; anchor = gfx::Point(offsetFromEdge, offsetFromEdge); host_impl_->ScrollBegin( - BeginState(anchor, gfx::Vector2dF(), ScrollInputType::kTouchscreen).get(), - ScrollInputType::kTouchscreen); + BeginState(anchor, gfx::Vector2dF(), ui::ScrollInputType::kTouchscreen) + .get(), + ui::ScrollInputType::kTouchscreen); host_impl_->PinchGestureBegin(); host_impl_->PinchGestureUpdate(2, anchor); host_impl_->PinchGestureEnd(anchor, true); @@ -3194,8 +3230,9 @@ TEST_F(LayerTreeHostImplTest, PinchZoomSnapsToScreenEdge) { anchor = gfx::Point(viewport_size.width() - offsetFromEdge, viewport_size.height() - offsetFromEdge); host_impl_->ScrollBegin( - BeginState(anchor, gfx::Vector2dF(), ScrollInputType::kTouchscreen).get(), - ScrollInputType::kTouchscreen); + BeginState(anchor, gfx::Vector2dF(), ui::ScrollInputType::kTouchscreen) + .get(), + ui::ScrollInputType::kTouchscreen); host_impl_->PinchGestureBegin(); host_impl_->PinchGestureUpdate(2, anchor); host_impl_->PinchGestureEnd(anchor, true); @@ -3223,11 +3260,11 @@ TEST_F(LayerTreeHostImplTest, ImplPinchZoomWheelBubbleBetweenViewports) { // Scroll by a small amount, there should be no bubbling to the outer // viewport. host_impl_->ScrollBegin(BeginState(gfx::Point(0, 0), gfx::Vector2dF(10, 20), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel); + ui::ScrollInputType::kWheel); host_impl_->ScrollUpdate(UpdateState(gfx::Point(0, 0), gfx::Vector2dF(10, 20), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get()); host_impl_->ScrollEnd(); @@ -3238,12 +3275,12 @@ TEST_F(LayerTreeHostImplTest, ImplPinchZoomWheelBubbleBetweenViewports) { // Scroll by the inner viewport's max scroll extent, the remainder // should bubble up to the outer viewport. host_impl_->ScrollBegin(BeginState(gfx::Point(0, 0), gfx::Vector2dF(100, 100), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel); + ui::ScrollInputType::kWheel); host_impl_->ScrollUpdate(UpdateState(gfx::Point(0, 0), gfx::Vector2dF(100, 100), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get()); host_impl_->ScrollEnd(); @@ -3255,12 +3292,12 @@ TEST_F(LayerTreeHostImplTest, ImplPinchZoomWheelBubbleBetweenViewports) { // Scroll by the outer viewport's max scroll extent, it should all go to the // outer viewport. host_impl_->ScrollBegin(BeginState(gfx::Point(0, 0), gfx::Vector2dF(190, 180), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel); + ui::ScrollInputType::kWheel); host_impl_->ScrollUpdate(UpdateState(gfx::Point(0, 0), gfx::Vector2dF(190, 180), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get()); host_impl_->ScrollEnd(); @@ -3281,12 +3318,12 @@ TEST_F(LayerTreeHostImplTest, ScrollWithSwapPromises) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(0, 10), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .thread); host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, 10), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); host_impl_->QueueSwapPromiseForMainThreadScrollUpdate( std::move(swap_promise)); @@ -3323,16 +3360,16 @@ TEST_F(LayerTreeHostImplTest, ScrollDoesntBubble) { { host_impl_->ScrollBegin(BeginState(gfx::Point(21, 21), gfx::Vector2d(5, 5), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen); + ui::ScrollInputType::kTouchscreen); host_impl_->ScrollUpdate(UpdateState(gfx::Point(21, 21), gfx::Vector2d(5, 5), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); host_impl_->ScrollUpdate(UpdateState(gfx::Point(21, 21), gfx::Vector2d(100, 100), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); host_impl_->ScrollEnd(); @@ -3351,24 +3388,24 @@ TEST_F(LayerTreeHostImplTest, ScrollDoesntBubble) { { host_impl_->ScrollBegin(BeginState(gfx::Point(21, 21), gfx::Vector2d(3, 4), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen); + ui::ScrollInputType::kTouchscreen); host_impl_->ScrollUpdate(UpdateState(gfx::Point(21, 21), gfx::Vector2d(3, 4), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); host_impl_->ScrollUpdate(UpdateState(gfx::Point(21, 21), gfx::Vector2d(2, 1), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); host_impl_->ScrollUpdate(UpdateState(gfx::Point(21, 21), gfx::Vector2d(2, 1), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); host_impl_->ScrollUpdate(UpdateState(gfx::Point(21, 21), gfx::Vector2d(2, 1), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); host_impl_->ScrollEnd(); @@ -3401,9 +3438,9 @@ TEST_F(LayerTreeHostImplTest, PinchGesture) { float page_scale_delta = 2; host_impl_->ScrollBegin(BeginState(gfx::Point(50, 50), gfx::Vector2dF(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen); + ui::ScrollInputType::kTouchscreen); host_impl_->PinchGestureBegin(); host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point(50, 50)); host_impl_->PinchGestureEnd(gfx::Point(50, 50), true); @@ -3425,9 +3462,9 @@ TEST_F(LayerTreeHostImplTest, PinchGesture) { float page_scale_delta = 10; host_impl_->ScrollBegin(BeginState(gfx::Point(50, 50), gfx::Vector2dF(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen); + ui::ScrollInputType::kTouchscreen); host_impl_->PinchGestureBegin(); host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point(50, 50)); host_impl_->PinchGestureEnd(gfx::Point(50, 50), true); @@ -3453,9 +3490,9 @@ TEST_F(LayerTreeHostImplTest, PinchGesture) { float page_scale_delta = 0.1f; host_impl_->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen); + ui::ScrollInputType::kTouchscreen); host_impl_->PinchGestureBegin(); host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point()); host_impl_->PinchGestureEnd(gfx::Point(), true); @@ -3483,9 +3520,9 @@ TEST_F(LayerTreeHostImplTest, PinchGesture) { float page_scale_delta = 1; host_impl_->ScrollBegin(BeginState(gfx::Point(10, 10), gfx::Vector2dF(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen); + ui::ScrollInputType::kTouchscreen); host_impl_->PinchGestureBegin(); host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point(10, 10)); host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point(20, 20)); @@ -3514,14 +3551,14 @@ TEST_F(LayerTreeHostImplTest, PinchGesture) { float page_scale_delta = 1; host_impl_->ScrollBegin( BeginState(gfx::Point(10, 10), gfx::Vector2dF(-10, -10), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen); + ui::ScrollInputType::kTouchscreen); host_impl_->PinchGestureBegin(); host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point(10, 10)); host_impl_->ScrollUpdate(UpdateState(gfx::Point(10, 10), gfx::Vector2d(-10, -10), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point(20, 20)); host_impl_->PinchGestureEnd(gfx::Point(20, 20), true); @@ -3547,9 +3584,9 @@ TEST_F(LayerTreeHostImplTest, PinchGesture) { scroll_layer->element_id(), gfx::ScrollOffset(0, 0)); host_impl_->ScrollBegin(BeginState(gfx::Point(0, 0), gfx::Vector2dF(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen); + ui::ScrollInputType::kTouchscreen); host_impl_->PinchGestureBegin(); host_impl_->PinchGestureUpdate(2, gfx::Point(0, 0)); host_impl_->PinchGestureUpdate(1, gfx::Point(0, 0)); @@ -3559,7 +3596,7 @@ TEST_F(LayerTreeHostImplTest, PinchGesture) { host_impl_->ScrollUpdate(UpdateState(gfx::Point(0, 0), gfx::Vector2d(10, 10), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); host_impl_->PinchGestureUpdate(1, gfx::Point(10, 10)); host_impl_->PinchGestureEnd(gfx::Point(10, 10), true); @@ -3596,14 +3633,14 @@ TEST_F(LayerTreeHostImplTest, SyncSubpixelScrollDelta) { float page_scale_delta = 1; host_impl_->ScrollBegin(BeginState(gfx::Point(10, 10), gfx::Vector2dF(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen); + ui::ScrollInputType::kTouchscreen); host_impl_->PinchGestureBegin(); host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point(10, 10)); host_impl_->ScrollUpdate(UpdateState(gfx::Point(10, 10), gfx::Vector2dF(0, -1.001f), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point(10, 9)); host_impl_->PinchGestureEnd(gfx::Point(10, 9), true); @@ -3640,12 +3677,12 @@ TEST_F(LayerTreeHostImplTest, SyncSubpixelScrollFromFractionalActiveBase) { scroll_layer->element_id(), gfx::ScrollOffset(0, 20.5f)); host_impl_->ScrollBegin(BeginState(gfx::Point(10, 10), gfx::Vector2dF(0, -1), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel); + ui::ScrollInputType::kWheel); host_impl_->ScrollUpdate(UpdateState(gfx::Point(10, 10), gfx::Vector2dF(0, -1), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get()); host_impl_->ScrollEnd(); @@ -4181,10 +4218,10 @@ TEST_F(LayerTreeHostImplTest, AnimatedGranularityCausesSmoothScroll) { // TODO(bokan): Unfortunately, Mac currently doesn't support smooth scrolling // wheel events. https://crbug.com/574283. #if defined(OS_MACOSX) - std::vector<ScrollInputType> types = {ScrollInputType::kScrollbar}; + std::vector<ui::ScrollInputType> types = {ui::ScrollInputType::kScrollbar}; #else - std::vector<ScrollInputType> types = {ScrollInputType::kScrollbar, - ScrollInputType::kWheel}; + std::vector<ui::ScrollInputType> types = {ui::ScrollInputType::kScrollbar, + ui::ScrollInputType::kWheel}; #endif for (auto type : types) { auto begin_state = BeginState(position, offset, type); @@ -4251,8 +4288,8 @@ TEST_F(LayerTreeHostImplTest, NonAnimatedGranularityCausesInstantScroll) { gfx::Point position(295, 195); gfx::Vector2dF offset(0, 50); - std::vector<ScrollInputType> types = {ScrollInputType::kScrollbar, - ScrollInputType::kWheel}; + std::vector<ui::ScrollInputType> types = {ui::ScrollInputType::kScrollbar, + ui::ScrollInputType::kWheel}; for (auto type : types) { auto begin_state = BeginState(position, offset, type); begin_state->data()->set_current_native_scrolling_element( @@ -4299,17 +4336,17 @@ class LayerTreeHostImplOverridePhysicalTime : public LayerTreeHostImpl { nullptr, nullptr) {} - viz::BeginFrameArgs CurrentBeginFrameArgs() const override { - return viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1, - fake_current_physical_time_); + const viz::BeginFrameArgs& CurrentBeginFrameArgs() const override { + return current_begin_frame_args_; } void SetCurrentPhysicalTimeTicksForTest(base::TimeTicks fake_now) { - fake_current_physical_time_ = fake_now; + current_begin_frame_args_ = viz::CreateBeginFrameArgsForTesting( + BEGINFRAME_FROM_HERE, 0, 1, fake_now); } private: - base::TimeTicks fake_current_physical_time_; + viz::BeginFrameArgs current_begin_frame_args_; }; class LayerTreeHostImplTestScrollbarAnimation : public LayerTreeHostImplTest { @@ -4381,9 +4418,9 @@ class LayerTreeHostImplTestScrollbarAnimation : public LayerTreeHostImplTest { // If no scroll happened during a scroll gesture, it should have no effect. host_impl_->ScrollBegin( - BeginState(gfx::Point(), gfx::Vector2dF(), ScrollInputType::kWheel) + BeginState(gfx::Point(), gfx::Vector2dF(), ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel); + ui::ScrollInputType::kWheel); host_impl_->ScrollEnd(); EXPECT_FALSE(did_request_next_frame_); EXPECT_FALSE(did_request_redraw_); @@ -4393,12 +4430,12 @@ class LayerTreeHostImplTestScrollbarAnimation : public LayerTreeHostImplTest { // For Aura Overlay Scrollbar, if no scroll happened during a scroll // gesture, shows scrollbars and schedules a delay fade out. host_impl_->ScrollBegin( - BeginState(gfx::Point(), gfx::Vector2dF(), ScrollInputType::kWheel) + BeginState(gfx::Point(), gfx::Vector2dF(), ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel); - host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), gfx::Vector2dF(0, 0), ScrollInputType::kWheel) - .get()); + ui::ScrollInputType::kWheel); + host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2dF(0, 0), + ui::ScrollInputType::kWheel) + .get()); host_impl_->ScrollEnd(); EXPECT_FALSE(did_request_next_frame_); EXPECT_FALSE(did_request_redraw_); @@ -4428,13 +4465,13 @@ class LayerTreeHostImplTestScrollbarAnimation : public LayerTreeHostImplTest { // After a scroll, a scrollbar animation should be scheduled about 20ms from // now. - host_impl_->ScrollBegin( - BeginState(gfx::Point(), gfx::Vector2dF(0, 5), ScrollInputType::kWheel) - .get(), - ScrollInputType::kWheel); - host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), gfx::Vector2dF(0, 5), ScrollInputType::kWheel) - .get()); + host_impl_->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 5), + ui::ScrollInputType::kWheel) + .get(), + ui::ScrollInputType::kWheel); + host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2dF(0, 5), + ui::ScrollInputType::kWheel) + .get()); EXPECT_FALSE(did_request_next_frame_); EXPECT_TRUE(did_request_redraw_); did_request_redraw_ = false; @@ -4580,13 +4617,13 @@ class LayerTreeHostImplTestScrollbarOpacity : public LayerTreeHostImplTest { EXPECT_EQ(nullptr, host_impl_->ScrollbarAnimationControllerForElementId( scroll->element_id())); } - host_impl_->ScrollBegin( - BeginState(gfx::Point(), gfx::Vector2dF(0, 5), ScrollInputType::kWheel) - .get(), - ScrollInputType::kWheel); - host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), gfx::Vector2dF(0, 5), ScrollInputType::kWheel) - .get()); + host_impl_->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 5), + ui::ScrollInputType::kWheel) + .get(), + ui::ScrollInputType::kWheel); + host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2dF(0, 5), + ui::ScrollInputType::kWheel) + .get()); host_impl_->ScrollEnd(); CreatePendingTree(); @@ -4711,11 +4748,11 @@ TEST_F(LayerTreeHostImplTestMultiScrollable, // Scroll on root should flash all scrollbars. host_impl_->RootScrollBegin( BeginState(gfx::Point(20, 20), gfx::Vector2dF(0, 10), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel); + ui::ScrollInputType::kWheel); host_impl_->ScrollUpdate(UpdateState(gfx::Point(20, 20), gfx::Vector2d(0, 10), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get()); host_impl_->ScrollEnd(); @@ -4727,9 +4764,9 @@ TEST_F(LayerTreeHostImplTestMultiScrollable, // Scroll on child should flash all scrollbars. host_impl_->ScrollBegin(BeginState(gfx::Point(70, 70), gfx::Vector2dF(0, 100), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel); + ui::ScrollInputType::kWheel); host_impl_->ScrollUpdate( AnimatedUpdateState(gfx::Point(70, 70), gfx::Vector2d(0, 100)).get()); host_impl_->ScrollEnd(); @@ -4808,10 +4845,11 @@ TEST_F(LayerTreeHostImplTest, ScrollHitTestOnScrollbar) { // Wheel scroll on root scrollbar should process on impl thread. { - InputHandler::ScrollStatus status = host_impl_->ScrollBegin( - BeginState(gfx::Point(1, 1), gfx::Vector2dF(), ScrollInputType::kWheel) - .get(), - ScrollInputType::kWheel); + InputHandler::ScrollStatus status = + host_impl_->ScrollBegin(BeginState(gfx::Point(1, 1), gfx::Vector2dF(), + ui::ScrollInputType::kWheel) + .get(), + ui::ScrollInputType::kWheel); EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread); host_impl_->ScrollEnd(); } @@ -4820,9 +4858,9 @@ TEST_F(LayerTreeHostImplTest, ScrollHitTestOnScrollbar) { { InputHandler::ScrollStatus status = host_impl_->ScrollBegin(BeginState(gfx::Point(1, 1), gfx::Vector2dF(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen); + ui::ScrollInputType::kTouchscreen); EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread); EXPECT_EQ(MainThreadScrollingReason::kScrollbarScrolling, status.main_thread_scrolling_reasons); @@ -4832,9 +4870,9 @@ TEST_F(LayerTreeHostImplTest, ScrollHitTestOnScrollbar) { { InputHandler::ScrollStatus status = host_impl_->ScrollBegin(BeginState(gfx::Point(51, 51), gfx::Vector2dF(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel); + ui::ScrollInputType::kWheel); EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread); EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain, status.main_thread_scrolling_reasons); @@ -4845,9 +4883,9 @@ TEST_F(LayerTreeHostImplTest, ScrollHitTestOnScrollbar) { { InputHandler::ScrollStatus status = host_impl_->ScrollBegin(BeginState(gfx::Point(51, 51), gfx::Vector2dF(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen); + ui::ScrollInputType::kTouchscreen); EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread); EXPECT_EQ(MainThreadScrollingReason::kScrollbarScrolling, status.main_thread_scrolling_reasons); @@ -4992,13 +5030,13 @@ TEST_F(LayerTreeHostImplTest, ScrollbarRegistration) { // Scrolling the viewport should result in a scrollbar animation update. animation_task_.Reset(); - host_impl_->ScrollBegin( - BeginState(gfx::Point(), gfx::Vector2dF(10, 10), ScrollInputType::kWheel) - .get(), - ScrollInputType::kWheel); - host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), gfx::Vector2d(10, 10), ScrollInputType::kWheel) - .get()); + host_impl_->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(10, 10), + ui::ScrollInputType::kWheel) + .get(), + ui::ScrollInputType::kWheel); + host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(10, 10), + ui::ScrollInputType::kWheel) + .get()); host_impl_->ScrollEnd(); EXPECT_FALSE(animation_task_.is_null()); animation_task_.Reset(); @@ -5052,11 +5090,13 @@ TEST_F(LayerTreeHostImplTest, ScrollBeforeMouseMove) { SetupViewportLayersInnerScrolls(viewport_size, content_size); auto* root_scroll = OuterViewportScrollLayer(); + const int kScrollbarThickness = 5; auto* vert_scrollbar = AddLayer<SolidColorScrollbarLayerImpl>( - host_impl_->active_tree(), VERTICAL, 5, 0, false); + host_impl_->active_tree(), VERTICAL, kScrollbarThickness, 0, false); SetupScrollbarLayer(root_scroll, vert_scrollbar); vert_scrollbar->SetBounds(gfx::Size(10, 200)); - vert_scrollbar->SetOffsetToTransformParent(gfx::Vector2dF(300, 0)); + vert_scrollbar->SetOffsetToTransformParent( + gfx::Vector2dF(300 - kScrollbarThickness, 0)); host_impl_->active_tree()->UpdateScrollbarGeometries(); @@ -5066,26 +5106,27 @@ TEST_F(LayerTreeHostImplTest, ScrollBeforeMouseMove) { root_scroll->element_id()); const float kDistanceToTriggerThumb = + vert_scrollbar->ComputeThumbQuadRect().height() + SingleScrollbarAnimationControllerThinning:: kMouseMoveDistanceToTriggerExpand; - // Move the mouse near the thumb in the top position. - auto near_thumb_at_top = gfx::Point(300, -kDistanceToTriggerThumb + 1); + // Move the mouse near the thumb while its at the viewport top. + auto near_thumb_at_top = gfx::Point(295, kDistanceToTriggerThumb - 1); host_impl_->MouseMoveAt(near_thumb_at_top); EXPECT_TRUE(scrollbar_controller->MouseIsNearScrollbarThumb(VERTICAL)); // Move the mouse away from the thumb. - host_impl_->MouseMoveAt(gfx::Point(300, -kDistanceToTriggerThumb - 1)); + host_impl_->MouseMoveAt(gfx::Point(295, kDistanceToTriggerThumb + 1)); EXPECT_FALSE(scrollbar_controller->MouseIsNearScrollbarThumb(VERTICAL)); - // Scroll the page down which moves the thumb down. - host_impl_->ScrollBegin( - BeginState(gfx::Point(), gfx::Vector2dF(0, 100), ScrollInputType::kWheel) - .get(), - ScrollInputType::kWheel); - host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), gfx::Vector2d(0, 100), ScrollInputType::kWheel) - .get()); + // Scroll the page down which moves the thumb down to the viewport bottom. + host_impl_->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 800), + ui::ScrollInputType::kWheel) + .get(), + ui::ScrollInputType::kWheel); + host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, 800), + ui::ScrollInputType::kWheel) + .get()); host_impl_->ScrollEnd(); // Move the mouse near the thumb in the top position. @@ -5093,13 +5134,13 @@ TEST_F(LayerTreeHostImplTest, ScrollBeforeMouseMove) { EXPECT_FALSE(scrollbar_controller->MouseIsNearScrollbarThumb(VERTICAL)); // Scroll the page up which moves the thumb back up. - host_impl_->ScrollBegin( - BeginState(gfx::Point(), gfx::Vector2dF(0, -100), ScrollInputType::kWheel) - .get(), - ScrollInputType::kWheel); - host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), gfx::Vector2d(0, -100), ScrollInputType::kWheel) - .get()); + host_impl_->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, -800), + ui::ScrollInputType::kWheel) + .get(), + ui::ScrollInputType::kWheel); + host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, -800), + ui::ScrollInputType::kWheel) + .get()); host_impl_->ScrollEnd(); // Move the mouse near the thumb in the top position. @@ -5316,13 +5357,13 @@ TEST_F(LayerTreeHostImplTest, CompositorFrameMetadata) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); - host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), gfx::Vector2d(0, 10), ScrollInputType::kWheel) - .get()); + host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, 10), + ui::ScrollInputType::kWheel) + .get()); { viz::CompositorFrameMetadata metadata = host_impl_->MakeCompositorFrameMetadata(); @@ -5336,10 +5377,10 @@ TEST_F(LayerTreeHostImplTest, CompositorFrameMetadata) { } // Page scale should update metadata correctly (shrinking only the viewport). - host_impl_->ScrollBegin( - BeginState(gfx::Point(), gfx::Vector2dF(), ScrollInputType::kTouchscreen) - .get(), - ScrollInputType::kTouchscreen); + host_impl_->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(), + ui::ScrollInputType::kTouchscreen) + .get(), + ui::ScrollInputType::kTouchscreen); host_impl_->PinchGestureBegin(); host_impl_->PinchGestureUpdate(2, gfx::Point()); host_impl_->PinchGestureEnd(gfx::Point(), true); @@ -5870,10 +5911,11 @@ TEST_F(LayerTreeHostImplTest, ScrollRootIgnored) { DrawFrame(); // Scroll event is ignored because layer is not scrollable. - InputHandler::ScrollStatus status = host_impl_->ScrollBegin( - BeginState(gfx::Point(), gfx::Vector2dF(0, 10), ScrollInputType::kWheel) - .get(), - ScrollInputType::kWheel); + InputHandler::ScrollStatus status = + host_impl_->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10), + ui::ScrollInputType::kWheel) + .get(), + ui::ScrollInputType::kWheel); EXPECT_EQ(InputHandler::SCROLL_IGNORED, status.thread); EXPECT_EQ(MainThreadScrollingReason::kNoScrollingLayer, status.main_thread_scrolling_reasons); @@ -6005,9 +6047,9 @@ TEST_F(LayerTreeHostImplBrowserControlsTest, EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 25), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .thread); host_impl_->browser_controls_manager()->ScrollBegin(); @@ -6062,9 +6104,9 @@ TEST_F(LayerTreeHostImplBrowserControlsTest, ASSERT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 25), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .thread); // Hide the browser controls by 25px. The outer clip should expand by 50px as @@ -6072,7 +6114,7 @@ TEST_F(LayerTreeHostImplBrowserControlsTest, // case 0.5. Therefore, changes to the outer viewport need to be divided by // the minimum scale as well. host_impl_->ScrollUpdate(UpdateState(gfx::Point(0, 0), gfx::Vector2dF(0, 25), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); EXPECT_VIEWPORT_GEOMETRIES(0.5f); @@ -6112,9 +6154,9 @@ TEST_F(LayerTreeHostImplBrowserControlsTest, EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 25), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .thread); host_impl_->browser_controls_manager()->ScrollBegin(); @@ -6167,9 +6209,9 @@ TEST_F(LayerTreeHostImplBrowserControlsTest, InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), top_controls_scroll_delta, - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .thread); // Make the test scroll delta a fractional amount, to verify that the @@ -6207,12 +6249,12 @@ TEST_F(LayerTreeHostImplBrowserControlsTest, EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 50), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .thread); host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2dF(0, 50), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); // The entire scroll delta should have been used to hide the browser controls. @@ -6224,7 +6266,7 @@ TEST_F(LayerTreeHostImplBrowserControlsTest, // The inner viewport should be scrollable by 50px * page_scale. host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2dF(0, 100), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); EXPECT_EQ(50, CurrentScrollOffset(inner_scroll).y()); EXPECT_EQ(0, CurrentScrollOffset(outer_scroll).y()); @@ -6235,15 +6277,15 @@ TEST_F(LayerTreeHostImplBrowserControlsTest, EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, -50), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .thread); EXPECT_EQ(host_impl_->CurrentlyScrollingNode()->id, outer_scroll->scroll_tree_index()); host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2dF(0, -50), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); // The entire scroll delta should have been used to show the browser controls. @@ -6258,19 +6300,19 @@ TEST_F(LayerTreeHostImplBrowserControlsTest, // Now when we continue scrolling, make sure the outer viewport gets scrolled // since it wasn't scrollable when the scroll began. host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2dF(0, -20), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); EXPECT_EQ(25, CurrentScrollOffset(outer_scroll).y()); EXPECT_EQ(15, CurrentScrollOffset(inner_scroll).y()); host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2dF(0, -30), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); EXPECT_EQ(25, CurrentScrollOffset(outer_scroll).y()); EXPECT_EQ(0, CurrentScrollOffset(inner_scroll).y()); host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2dF(0.f, -50), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); host_impl_->ScrollEnd(); @@ -6296,9 +6338,9 @@ TEST_F(LayerTreeHostImplBrowserControlsTest, FixedContainerDelta) { InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), top_controls_scroll_delta, - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .thread); // Scroll down, the browser controls hiding should expand the viewport size so @@ -6319,9 +6361,9 @@ TEST_F(LayerTreeHostImplBrowserControlsTest, FixedContainerDelta) { InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), top_controls_scroll_delta, - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .thread); host_impl_->browser_controls_manager()->ScrollBegin(); host_impl_->browser_controls_manager()->ScrollBy(top_controls_scroll_delta); @@ -6337,9 +6379,9 @@ TEST_F(LayerTreeHostImplBrowserControlsTest, FixedContainerDelta) { InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), -top_controls_scroll_delta, - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .thread); host_impl_->browser_controls_manager()->ScrollBegin(); host_impl_->browser_controls_manager()->ScrollBy(-top_controls_scroll_delta); @@ -6410,12 +6452,12 @@ TEST_F(LayerTreeHostImplBrowserControlsTest, EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), scroll_delta, - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .thread); host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), scroll_delta, ScrollInputType::kTouchscreen) + UpdateState(gfx::Point(), scroll_delta, ui::ScrollInputType::kTouchscreen) .get()); host_impl_->ScrollEnd(); @@ -6611,12 +6653,12 @@ TEST_F(LayerTreeHostImplBrowserControlsTest, EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), scroll_delta, - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .thread); host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), scroll_delta, ScrollInputType::kTouchscreen) + UpdateState(gfx::Point(), scroll_delta, ui::ScrollInputType::kTouchscreen) .get()); // scrolling down at the max extents no longer hides the browser controls @@ -6642,12 +6684,12 @@ TEST_F(LayerTreeHostImplBrowserControlsTest, EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), scroll_delta, - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .thread); host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), scroll_delta, ScrollInputType::kTouchscreen) + UpdateState(gfx::Point(), scroll_delta, ui::ScrollInputType::kTouchscreen) .get()); host_impl_->ScrollEnd(); @@ -6676,12 +6718,12 @@ TEST_F(LayerTreeHostImplBrowserControlsTest, BrowserControlsAspectRatio) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), scroll_delta, - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .thread); host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), scroll_delta, ScrollInputType::kTouchscreen) + UpdateState(gfx::Point(), scroll_delta, ui::ScrollInputType::kTouchscreen) .get()); host_impl_->ScrollEnd(); @@ -6718,12 +6760,12 @@ TEST_F(LayerTreeHostImplBrowserControlsTest, EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), scroll_delta, - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .thread); host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), scroll_delta, ScrollInputType::kTouchscreen) + UpdateState(gfx::Point(), scroll_delta, ui::ScrollInputType::kTouchscreen) .get()); EXPECT_EQ(OuterViewportScrollLayer()->scroll_tree_index(), @@ -6739,12 +6781,12 @@ TEST_F(LayerTreeHostImplBrowserControlsTest, EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), scroll_delta, - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .thread); host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), scroll_delta, ScrollInputType::kTouchscreen) + UpdateState(gfx::Point(), scroll_delta, ui::ScrollInputType::kTouchscreen) .get()); EXPECT_EQ(0, host_impl_->browser_controls_manager()->ContentTopOffset()); @@ -6762,12 +6804,12 @@ TEST_F(LayerTreeHostImplBrowserControlsTest, EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), scroll_delta, - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .thread); host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), scroll_delta, ScrollInputType::kTouchscreen) + UpdateState(gfx::Point(), scroll_delta, ui::ScrollInputType::kTouchscreen) .get()); EXPECT_EQ(top_controls_height_, @@ -6788,9 +6830,9 @@ TEST_F(LayerTreeHostImplBrowserControlsTest, EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 50), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .thread); host_impl_->browser_controls_manager()->ScrollBegin(); @@ -6807,9 +6849,9 @@ TEST_F(LayerTreeHostImplBrowserControlsTest, EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, -25), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .thread); float scroll_increment_y = -25; @@ -6839,9 +6881,9 @@ TEST_F(LayerTreeHostImplBrowserControlsTest, EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .thread); } @@ -6878,11 +6920,11 @@ TEST_F(LayerTreeHostImplBrowserControlsTest, // Fully scroll the viewport. host_impl_->ScrollBegin( BeginState(gfx::Point(75, 75), gfx::Vector2dF(0, 2000), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen); + ui::ScrollInputType::kTouchscreen); host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, 2000), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); host_impl_->ScrollEnd(); } @@ -7000,13 +7042,13 @@ TEST_F(LayerTreeHostImplTest, ScrollNonCompositedRoot) { InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(5, 5), gfx::Vector2dF(0, 10), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); - host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), gfx::Vector2d(0, 10), ScrollInputType::kWheel) - .get()); + host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, 10), + ui::ScrollInputType::kWheel) + .get()); host_impl_->ScrollEnd(); EXPECT_TRUE(did_request_redraw_); EXPECT_TRUE(did_request_commit_); @@ -7029,13 +7071,13 @@ TEST_F(LayerTreeHostImplTest, ScrollChildCallsCommitAndRedraw) { InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(5, 5), gfx::Vector2dF(0, 10), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); - host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), gfx::Vector2d(0, 10), ScrollInputType::kWheel) - .get()); + host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, 10), + ui::ScrollInputType::kWheel) + .get()); host_impl_->ScrollEnd(); EXPECT_TRUE(did_request_redraw_); EXPECT_TRUE(did_request_commit_); @@ -7052,9 +7094,9 @@ TEST_F(LayerTreeHostImplTest, ScrollMissesChild) { // boundaries. InputHandler::ScrollStatus status = host_impl_->ScrollBegin( BeginState(gfx::Point(15, 5), gfx::Vector2dF(0, 10), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel); + ui::ScrollInputType::kWheel); EXPECT_EQ(InputHandler::SCROLL_IGNORED, status.thread); EXPECT_EQ(MainThreadScrollingReason::kNoScrollingLayer, status.main_thread_scrolling_reasons); @@ -7080,9 +7122,9 @@ TEST_F(LayerTreeHostImplTest, ScrollMissesBackfacingChild) { // viewer and there is nothing scrollable behind it. InputHandler::ScrollStatus status = host_impl_->ScrollBegin( BeginState(gfx::Point(5, 5), gfx::Vector2dF(0, 10), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel); + ui::ScrollInputType::kWheel); EXPECT_EQ(InputHandler::SCROLL_IGNORED, status.thread); EXPECT_EQ(MainThreadScrollingReason::kNoScrollingLayer, status.main_thread_scrolling_reasons); @@ -7110,9 +7152,9 @@ TEST_F(LayerTreeHostImplTest, ScrollBlockedByContentLayer) { // main thread. InputHandler::ScrollStatus status = host_impl_->ScrollBegin( BeginState(gfx::Point(5, 5), gfx::Vector2dF(0, 10), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel); + ui::ScrollInputType::kWheel); EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread); EXPECT_EQ(MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects, status.main_thread_scrolling_reasons); @@ -7134,12 +7176,13 @@ TEST_F(LayerTreeHostImplTest, ScrollRootAndChangePageScaleOnMainThread) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(5, 5), scroll_delta, - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), scroll_delta, ScrollInputType::kWheel).get()); + UpdateState(gfx::Point(), scroll_delta, ui::ScrollInputType::kWheel) + .get()); host_impl_->ScrollEnd(); // Set new page scale from main thread. @@ -7176,20 +7219,21 @@ TEST_F(LayerTreeHostImplTest, ScrollRootAndChangePageScaleOnImplThread) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(5, 5), scroll_delta, - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), scroll_delta, ScrollInputType::kWheel).get()); + UpdateState(gfx::Point(), scroll_delta, ui::ScrollInputType::kWheel) + .get()); host_impl_->ScrollEnd(); // Set new page scale on impl thread by pinching. float page_scale = 2; - host_impl_->ScrollBegin( - BeginState(gfx::Point(), gfx::Vector2dF(), ScrollInputType::kTouchscreen) - .get(), - ScrollInputType::kTouchscreen); + host_impl_->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(), + ui::ScrollInputType::kTouchscreen) + .get(), + ui::ScrollInputType::kTouchscreen); host_impl_->PinchGestureBegin(); host_impl_->PinchGestureUpdate(page_scale, gfx::Point()); host_impl_->PinchGestureEnd(gfx::Point(), true); @@ -7235,10 +7279,10 @@ TEST_F(LayerTreeHostImplTest, PageScaleDeltaAppliedToRootScrollLayerOnly) { UpdateDrawProperties(host_impl_->active_tree()); // Set new page scale on impl thread by pinching. - host_impl_->ScrollBegin( - BeginState(gfx::Point(), gfx::Vector2dF(), ScrollInputType::kTouchscreen) - .get(), - ScrollInputType::kTouchscreen); + host_impl_->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(), + ui::ScrollInputType::kTouchscreen) + .get(), + ui::ScrollInputType::kTouchscreen); host_impl_->PinchGestureBegin(); host_impl_->PinchGestureUpdate(new_page_scale, gfx::Point()); host_impl_->PinchGestureEnd(gfx::Point(), true); @@ -7274,12 +7318,13 @@ TEST_F(LayerTreeHostImplTest, ScrollChildAndChangePageScaleOnMainThread) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(5, 5), scroll_delta, - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), scroll_delta, ScrollInputType::kWheel).get()); + UpdateState(gfx::Point(), scroll_delta, ui::ScrollInputType::kWheel) + .get()); host_impl_->ScrollEnd(); float page_scale = 2; @@ -7331,12 +7376,13 @@ TEST_F(LayerTreeHostImplTest, ScrollChildBeyondLimit) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), scroll_delta, - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), scroll_delta, ScrollInputType::kWheel).get()); + UpdateState(gfx::Point(), scroll_delta, ui::ScrollInputType::kWheel) + .get()); host_impl_->ScrollEnd(); std::unique_ptr<ScrollAndScaleSet> scroll_info = @@ -7388,9 +7434,9 @@ TEST_F(LayerTreeHostImplTimelinesTest, ScrollAnimatedLatchToChild) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(0, -100), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); host_impl_->ScrollUpdate( AnimatedUpdateState(gfx::Point(), gfx::Vector2d(0, -100)).get()); @@ -7472,13 +7518,13 @@ TEST_F(LayerTreeHostImplTest, ScrollWithoutBubbling) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), scroll_delta, - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .thread); - host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), scroll_delta, ScrollInputType::kTouchscreen) - .get()); + host_impl_->ScrollUpdate(UpdateState(gfx::Point(), scroll_delta, + ui::ScrollInputType::kTouchscreen) + .get()); host_impl_->ScrollEnd(); std::unique_ptr<ScrollAndScaleSet> scroll_info = @@ -7497,15 +7543,15 @@ TEST_F(LayerTreeHostImplTest, ScrollWithoutBubbling) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(5, 5), scroll_delta, - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .thread); EXPECT_EQ(host_impl_->CurrentlyScrollingNode()->id, child_layer->scroll_tree_index()); - host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), scroll_delta, ScrollInputType::kTouchscreen) - .get()); + host_impl_->ScrollUpdate(UpdateState(gfx::Point(), scroll_delta, + ui::ScrollInputType::kTouchscreen) + .get()); EXPECT_EQ(host_impl_->CurrentlyScrollingNode()->id, child_layer->scroll_tree_index()); host_impl_->ScrollEnd(); @@ -7528,15 +7574,15 @@ TEST_F(LayerTreeHostImplTest, ScrollWithoutBubbling) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(5, 5), scroll_delta, - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .thread); EXPECT_EQ(host_impl_->CurrentlyScrollingNode()->id, grand_child_layer->scroll_tree_index()); - host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), scroll_delta, ScrollInputType::kTouchscreen) - .get()); + host_impl_->ScrollUpdate(UpdateState(gfx::Point(), scroll_delta, + ui::ScrollInputType::kTouchscreen) + .get()); EXPECT_EQ(host_impl_->CurrentlyScrollingNode()->id, grand_child_layer->scroll_tree_index()); host_impl_->ScrollEnd(); @@ -7561,15 +7607,15 @@ TEST_F(LayerTreeHostImplTest, ScrollWithoutBubbling) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(1, 1), scroll_delta, - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .thread); EXPECT_EQ(grand_child_layer->scroll_tree_index(), host_impl_->CurrentlyScrollingNode()->id); - host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), scroll_delta, ScrollInputType::kTouchscreen) - .get()); + host_impl_->ScrollUpdate(UpdateState(gfx::Point(), scroll_delta, + ui::ScrollInputType::kTouchscreen) + .get()); host_impl_->ScrollEnd(); scroll_info = host_impl_->ProcessScrollDeltas(); @@ -7606,13 +7652,13 @@ TEST_F(LayerTreeHostImplTest, ChildrenOfInnerScrollNodeCanScrollOnThread) { host_impl_ ->ScrollBegin(BeginState(gfx::Point(5, 5), gfx::ScrollOffsetToVector2dF(scroll_delta), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); host_impl_->ScrollUpdate( UpdateState(gfx::Point(), gfx::ScrollOffsetToVector2dF(scroll_delta), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get()); host_impl_->ScrollEnd(); @@ -7649,13 +7695,13 @@ TEST_F(LayerTreeHostImplTest, ScrollEventBubbling) { host_impl_ ->ScrollBegin(BeginState(gfx::Point(5, 5), gfx::ScrollOffsetToVector2dF(scroll_delta), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); host_impl_->ScrollUpdate( UpdateState(gfx::Point(), gfx::ScrollOffsetToVector2dF(scroll_delta), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get()); host_impl_->ScrollEnd(); @@ -7688,9 +7734,9 @@ TEST_F(LayerTreeHostImplTest, ScrollBeforeRedraw) { InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(5, 5), gfx::Vector2dF(0, 10), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); } @@ -7711,12 +7757,12 @@ TEST_F(LayerTreeHostImplTest, ScrollAxisAlignedRotatedLayer) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), gesture_scroll_delta, - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .thread); host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gesture_scroll_delta, - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); host_impl_->ScrollEnd(); @@ -7735,14 +7781,14 @@ TEST_F(LayerTreeHostImplTest, ScrollAxisAlignedRotatedLayer) { ->ScrollBegin( BeginState(gfx::Point(), gfx::ScrollOffsetToVector2dF(wheel_scroll_delta), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); host_impl_->ScrollUpdate( UpdateState(gfx::Point(), gfx::ScrollOffsetToVector2dF(wheel_scroll_delta), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get()); host_impl_->ScrollEnd(); @@ -7790,12 +7836,12 @@ TEST_F(LayerTreeHostImplTest, ScrollNonAxisAlignedRotatedLayer) { InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(1, 1), gesture_scroll_delta, - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .thread); host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gesture_scroll_delta, - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); host_impl_->ScrollEnd(); @@ -7821,12 +7867,12 @@ TEST_F(LayerTreeHostImplTest, ScrollNonAxisAlignedRotatedLayer) { InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(1, 1), gesture_scroll_delta, - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .thread); host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gesture_scroll_delta, - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); host_impl_->ScrollEnd(); @@ -7904,14 +7950,14 @@ TEST_F(LayerTreeHostImplTest, ScrollPerspectiveTransformedLayer) { ->ScrollBegin(BeginState(viewport_point, gfx::ScrollOffsetToVector2dF( gesture_scroll_deltas[i]), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .thread); host_impl_->ScrollUpdate( UpdateState(viewport_point, gfx::ScrollOffsetToVector2dF(gesture_scroll_deltas[i]), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); viewport_point += gfx::ScrollOffsetToFlooredVector2d(gesture_scroll_deltas[i]); @@ -7944,12 +7990,12 @@ TEST_F(LayerTreeHostImplTest, ScrollScaledLayer) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), scroll_delta, - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .thread); host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), scroll_delta, ScrollInputType::kTouchscreen) + UpdateState(gfx::Point(), scroll_delta, ui::ScrollInputType::kTouchscreen) .get()); host_impl_->ScrollEnd(); @@ -7969,14 +8015,14 @@ TEST_F(LayerTreeHostImplTest, ScrollScaledLayer) { ->ScrollBegin( BeginState(gfx::Point(), gfx::ScrollOffsetToVector2dF(wheel_scroll_delta), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); host_impl_->ScrollUpdate( UpdateState(gfx::Point(), gfx::ScrollOffsetToVector2dF(wheel_scroll_delta), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get()); host_impl_->ScrollEnd(); @@ -8055,10 +8101,10 @@ TEST_F(LayerTreeHostImplTest, RootLayerScrollOffsetDelegation) { // The pinch gesture doesn't put the delegate into a state where the scroll // offset is outside of the scroll range. (this is verified by DCHECKs in the // delegate). - host_impl_->ScrollBegin( - BeginState(gfx::Point(), gfx::Vector2dF(), ScrollInputType::kTouchscreen) - .get(), - ScrollInputType::kTouchscreen); + host_impl_->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(), + ui::ScrollInputType::kTouchscreen) + .get(), + ui::ScrollInputType::kTouchscreen); host_impl_->PinchGestureBegin(); host_impl_->PinchGestureUpdate(2, gfx::Point()); host_impl_->PinchGestureUpdate(.5f, gfx::Point()); @@ -8072,14 +8118,14 @@ TEST_F(LayerTreeHostImplTest, RootLayerScrollOffsetDelegation) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), scroll_delta, - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .thread); host_impl_->SetSynchronousInputHandlerRootScrollOffset(current_offset); host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), scroll_delta, ScrollInputType::kTouchscreen) + UpdateState(gfx::Point(), scroll_delta, ui::ScrollInputType::kTouchscreen) .get()); EXPECT_EQ(ScrollOffsetWithDelta(current_offset, scroll_delta), scroll_watcher.last_set_scroll_offset()); @@ -8087,7 +8133,7 @@ TEST_F(LayerTreeHostImplTest, RootLayerScrollOffsetDelegation) { current_offset = gfx::ScrollOffset(42, 41); host_impl_->SetSynchronousInputHandlerRootScrollOffset(current_offset); host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), scroll_delta, ScrollInputType::kTouchscreen) + UpdateState(gfx::Point(), scroll_delta, ui::ScrollInputType::kTouchscreen) .get()); EXPECT_EQ(current_offset + gfx::ScrollOffset(scroll_delta), scroll_watcher.last_set_scroll_offset()); @@ -8182,17 +8228,17 @@ TEST_F(LayerTreeHostImplTest, ViewportUserScrollable) { gfx::Vector2dF scroll_delta(30 * page_scale_factor, 0); { - auto begin_state = - BeginState(gfx::Point(), scroll_delta, ScrollInputType::kTouchscreen); + auto begin_state = BeginState(gfx::Point(), scroll_delta, + ui::ScrollInputType::kTouchscreen); EXPECT_EQ( InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ - ->ScrollBegin(begin_state.get(), ScrollInputType::kTouchscreen) + ->ScrollBegin(begin_state.get(), ui::ScrollInputType::kTouchscreen) .thread); // Try scrolling right, the inner viewport should be allowed to scroll. - auto update_state = - UpdateState(gfx::Point(), scroll_delta, ScrollInputType::kTouchscreen); + auto update_state = UpdateState(gfx::Point(), scroll_delta, + ui::ScrollInputType::kTouchscreen); host_impl_->ScrollUpdate(update_state.get()); EXPECT_VECTOR_EQ(gfx::ScrollOffset(30, 0), @@ -8202,14 +8248,14 @@ TEST_F(LayerTreeHostImplTest, ViewportUserScrollable) { // Continue scrolling. The inner viewport should scroll until its extent, // however, the outer viewport should not accept any scroll. - update_state = - UpdateState(gfx::Point(), scroll_delta, ScrollInputType::kTouchscreen); + update_state = UpdateState(gfx::Point(), scroll_delta, + ui::ScrollInputType::kTouchscreen); host_impl_->ScrollUpdate(update_state.get()); - update_state = - UpdateState(gfx::Point(), scroll_delta, ScrollInputType::kTouchscreen); + update_state = UpdateState(gfx::Point(), scroll_delta, + ui::ScrollInputType::kTouchscreen); host_impl_->ScrollUpdate(update_state.get()); - update_state = - UpdateState(gfx::Point(), scroll_delta, ScrollInputType::kTouchscreen); + update_state = UpdateState(gfx::Point(), scroll_delta, + ui::ScrollInputType::kTouchscreen); host_impl_->ScrollUpdate(update_state.get()); EXPECT_VECTOR_EQ(gfx::ScrollOffset(50, 0), @@ -8226,10 +8272,10 @@ TEST_F(LayerTreeHostImplTest, ViewportUserScrollable) { { auto begin_state = - BeginState(gfx::Point(), scroll_delta, ScrollInputType::kWheel); + BeginState(gfx::Point(), scroll_delta, ui::ScrollInputType::kWheel); EXPECT_EQ( InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_->ScrollBegin(begin_state.get(), ScrollInputType::kWheel) + host_impl_->ScrollBegin(begin_state.get(), ui::ScrollInputType::kWheel) .thread); // Try scrolling right, the inner viewport should be allowed to scroll. @@ -8453,22 +8499,24 @@ TEST_F(LayerTreeHostImplTest, OverscrollRoot) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); - scroll_result = host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), gfx::Vector2d(0, 10), ScrollInputType::kWheel) - .get()); + scroll_result = + host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, 10), + ui::ScrollInputType::kWheel) + .get()); EXPECT_TRUE(scroll_result.did_scroll); EXPECT_FALSE(scroll_result.did_overscroll_root); EXPECT_EQ(gfx::Vector2dF(), scroll_result.unused_scroll_delta); EXPECT_EQ(gfx::Vector2dF(), host_impl_->accumulated_root_overscroll()); // Overscroll events are reflected immediately. - scroll_result = host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), gfx::Vector2d(0, 50), ScrollInputType::kWheel) - .get()); + scroll_result = + host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, 50), + ui::ScrollInputType::kWheel) + .get()); EXPECT_TRUE(scroll_result.did_scroll); EXPECT_TRUE(scroll_result.did_overscroll_root); EXPECT_EQ(gfx::Vector2dF(0, 10), scroll_result.unused_scroll_delta); @@ -8477,9 +8525,10 @@ TEST_F(LayerTreeHostImplTest, OverscrollRoot) { host_impl_->accumulated_root_overscroll()); // In-bounds scrolling resets accumulated overscroll for the scrolled axes. - scroll_result = host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), gfx::Vector2d(0, -50), ScrollInputType::kWheel) - .get()); + scroll_result = + host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, -50), + ui::ScrollInputType::kWheel) + .get()); EXPECT_TRUE(scroll_result.did_scroll); EXPECT_FALSE(scroll_result.did_overscroll_root); EXPECT_EQ(gfx::Vector2dF(), scroll_result.unused_scroll_delta); @@ -8487,9 +8536,10 @@ TEST_F(LayerTreeHostImplTest, OverscrollRoot) { EXPECT_EQ(scroll_result.accumulated_root_overscroll, host_impl_->accumulated_root_overscroll()); - scroll_result = host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), gfx::Vector2d(0, -10), ScrollInputType::kWheel) - .get()); + scroll_result = + host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, -10), + ui::ScrollInputType::kWheel) + .get()); EXPECT_FALSE(scroll_result.did_scroll); EXPECT_TRUE(scroll_result.did_overscroll_root); EXPECT_EQ(gfx::Vector2dF(0, -10), scroll_result.unused_scroll_delta); @@ -8497,9 +8547,10 @@ TEST_F(LayerTreeHostImplTest, OverscrollRoot) { EXPECT_EQ(scroll_result.accumulated_root_overscroll, host_impl_->accumulated_root_overscroll()); - scroll_result = host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), gfx::Vector2d(10, 0), ScrollInputType::kWheel) - .get()); + scroll_result = + host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(10, 0), + ui::ScrollInputType::kWheel) + .get()); EXPECT_TRUE(scroll_result.did_scroll); EXPECT_FALSE(scroll_result.did_overscroll_root); EXPECT_EQ(gfx::Vector2dF(0, 0), scroll_result.unused_scroll_delta); @@ -8507,9 +8558,10 @@ TEST_F(LayerTreeHostImplTest, OverscrollRoot) { EXPECT_EQ(scroll_result.accumulated_root_overscroll, host_impl_->accumulated_root_overscroll()); - scroll_result = host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), gfx::Vector2d(-15, 0), ScrollInputType::kWheel) - .get()); + scroll_result = + host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(-15, 0), + ui::ScrollInputType::kWheel) + .get()); EXPECT_TRUE(scroll_result.did_scroll); EXPECT_TRUE(scroll_result.did_overscroll_root); EXPECT_EQ(gfx::Vector2dF(-5, 0), scroll_result.unused_scroll_delta); @@ -8517,9 +8569,10 @@ TEST_F(LayerTreeHostImplTest, OverscrollRoot) { EXPECT_EQ(scroll_result.accumulated_root_overscroll, host_impl_->accumulated_root_overscroll()); - scroll_result = host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), gfx::Vector2d(0, 60), ScrollInputType::kWheel) - .get()); + scroll_result = + host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, 60), + ui::ScrollInputType::kWheel) + .get()); EXPECT_TRUE(scroll_result.did_scroll); EXPECT_TRUE(scroll_result.did_overscroll_root); EXPECT_EQ(gfx::Vector2dF(0, 10), scroll_result.unused_scroll_delta); @@ -8527,9 +8580,10 @@ TEST_F(LayerTreeHostImplTest, OverscrollRoot) { EXPECT_EQ(scroll_result.accumulated_root_overscroll, host_impl_->accumulated_root_overscroll()); - scroll_result = host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), gfx::Vector2d(10, -60), ScrollInputType::kWheel) - .get()); + scroll_result = + host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(10, -60), + ui::ScrollInputType::kWheel) + .get()); EXPECT_TRUE(scroll_result.did_scroll); EXPECT_TRUE(scroll_result.did_overscroll_root); EXPECT_EQ(gfx::Vector2dF(0, -10), scroll_result.unused_scroll_delta); @@ -8539,9 +8593,10 @@ TEST_F(LayerTreeHostImplTest, OverscrollRoot) { // Overscroll accumulates within the scope of ScrollBegin/ScrollEnd as long // as no scroll occurs. - scroll_result = host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), gfx::Vector2d(0, -20), ScrollInputType::kWheel) - .get()); + scroll_result = + host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, -20), + ui::ScrollInputType::kWheel) + .get()); EXPECT_FALSE(scroll_result.did_scroll); EXPECT_TRUE(scroll_result.did_overscroll_root); EXPECT_EQ(gfx::Vector2dF(0, -20), scroll_result.unused_scroll_delta); @@ -8549,9 +8604,10 @@ TEST_F(LayerTreeHostImplTest, OverscrollRoot) { EXPECT_EQ(scroll_result.accumulated_root_overscroll, host_impl_->accumulated_root_overscroll()); - scroll_result = host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), gfx::Vector2d(0, -20), ScrollInputType::kWheel) - .get()); + scroll_result = + host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, -20), + ui::ScrollInputType::kWheel) + .get()); EXPECT_FALSE(scroll_result.did_scroll); EXPECT_TRUE(scroll_result.did_overscroll_root); EXPECT_EQ(gfx::Vector2dF(0, -20), scroll_result.unused_scroll_delta); @@ -8560,9 +8616,10 @@ TEST_F(LayerTreeHostImplTest, OverscrollRoot) { host_impl_->accumulated_root_overscroll()); // Overscroll resets on valid scroll. - scroll_result = host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), gfx::Vector2d(0, 10), ScrollInputType::kWheel) - .get()); + scroll_result = + host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, 10), + ui::ScrollInputType::kWheel) + .get()); EXPECT_TRUE(scroll_result.did_scroll); EXPECT_FALSE(scroll_result.did_overscroll_root); EXPECT_EQ(gfx::Vector2dF(0, 0), scroll_result.unused_scroll_delta); @@ -8570,9 +8627,10 @@ TEST_F(LayerTreeHostImplTest, OverscrollRoot) { EXPECT_EQ(scroll_result.accumulated_root_overscroll, host_impl_->accumulated_root_overscroll()); - scroll_result = host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), gfx::Vector2d(0, -20), ScrollInputType::kWheel) - .get()); + scroll_result = + host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, -20), + ui::ScrollInputType::kWheel) + .get()); EXPECT_TRUE(scroll_result.did_scroll); EXPECT_TRUE(scroll_result.did_overscroll_root); EXPECT_EQ(gfx::Vector2dF(0, -10), scroll_result.unused_scroll_delta); @@ -8615,13 +8673,14 @@ TEST_F(LayerTreeHostImplTest, OverscrollChildWithoutBubbling) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), scroll_delta, - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .thread); - scroll_result = host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), scroll_delta, ScrollInputType::kTouchscreen) - .get()); + scroll_result = + host_impl_->ScrollUpdate(UpdateState(gfx::Point(), scroll_delta, + ui::ScrollInputType::kTouchscreen) + .get()); EXPECT_EQ(host_impl_->CurrentlyScrollingNode()->id, grand_child_layer->scroll_tree_index()); EXPECT_TRUE(scroll_result.did_scroll); @@ -8635,9 +8694,9 @@ TEST_F(LayerTreeHostImplTest, OverscrollChildWithoutBubbling) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(5, 5), scroll_delta, - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .thread); EXPECT_EQ(host_impl_->CurrentlyScrollingNode()->id, child_layer->scroll_tree_index()); @@ -8647,13 +8706,14 @@ TEST_F(LayerTreeHostImplTest, OverscrollChildWithoutBubbling) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(5, 5), scroll_delta, - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .thread); - scroll_result = host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), scroll_delta, ScrollInputType::kTouchscreen) - .get()); + scroll_result = + host_impl_->ScrollUpdate(UpdateState(gfx::Point(), scroll_delta, + ui::ScrollInputType::kTouchscreen) + .get()); EXPECT_TRUE(scroll_result.did_scroll); EXPECT_FALSE(scroll_result.did_overscroll_root); EXPECT_EQ(host_impl_->CurrentlyScrollingNode()->id, @@ -8667,15 +8727,16 @@ TEST_F(LayerTreeHostImplTest, OverscrollChildWithoutBubbling) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(5, 5), scroll_delta, - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .thread); EXPECT_EQ(host_impl_->CurrentlyScrollingNode()->id, grand_child_layer->scroll_tree_index()); - scroll_result = host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), scroll_delta, ScrollInputType::kTouchscreen) - .get()); + scroll_result = + host_impl_->ScrollUpdate(UpdateState(gfx::Point(), scroll_delta, + ui::ScrollInputType::kTouchscreen) + .get()); EXPECT_TRUE(scroll_result.did_scroll); EXPECT_FALSE(scroll_result.did_overscroll_root); EXPECT_EQ(host_impl_->CurrentlyScrollingNode()->id, @@ -8697,22 +8758,25 @@ TEST_F(LayerTreeHostImplTest, OverscrollChildEventBubbling) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(5, 5), scroll_delta, - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); scroll_result = host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), scroll_delta, ScrollInputType::kWheel).get()); + UpdateState(gfx::Point(), scroll_delta, ui::ScrollInputType::kWheel) + .get()); EXPECT_TRUE(scroll_result.did_scroll); EXPECT_FALSE(scroll_result.did_overscroll_root); EXPECT_EQ(gfx::Vector2dF(), host_impl_->accumulated_root_overscroll()); scroll_result = host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), scroll_delta, ScrollInputType::kWheel).get()); + UpdateState(gfx::Point(), scroll_delta, ui::ScrollInputType::kWheel) + .get()); EXPECT_TRUE(scroll_result.did_scroll); EXPECT_TRUE(scroll_result.did_overscroll_root); EXPECT_EQ(gfx::Vector2dF(0, 6), host_impl_->accumulated_root_overscroll()); scroll_result = host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), scroll_delta, ScrollInputType::kWheel).get()); + UpdateState(gfx::Point(), scroll_delta, ui::ScrollInputType::kWheel) + .get()); EXPECT_FALSE(scroll_result.did_scroll); EXPECT_TRUE(scroll_result.did_overscroll_root); EXPECT_EQ(gfx::Vector2dF(0, 14), host_impl_->accumulated_root_overscroll()); @@ -8736,13 +8800,14 @@ TEST_F(LayerTreeHostImplTest, OverscrollAlways) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); - scroll_result = host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), gfx::Vector2d(0, 10), ScrollInputType::kWheel) - .get()); + scroll_result = + host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, 10), + ui::ScrollInputType::kWheel) + .get()); EXPECT_FALSE(scroll_result.did_scroll); EXPECT_TRUE(scroll_result.did_overscroll_root); EXPECT_EQ(gfx::Vector2dF(0, 10), host_impl_->accumulated_root_overscroll()); @@ -8762,13 +8827,13 @@ TEST_F(LayerTreeHostImplTest, NoOverscrollWhenNotAtEdge) { InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(0, 0), gfx::Vector2dF(0, 100), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); scroll_result = host_impl_->ScrollUpdate( UpdateState(gfx::Point(), gfx::Vector2dF(0, 100), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get()); EXPECT_TRUE(scroll_result.did_scroll); EXPECT_FALSE(scroll_result.did_overscroll_root); @@ -8776,7 +8841,7 @@ TEST_F(LayerTreeHostImplTest, NoOverscrollWhenNotAtEdge) { host_impl_->accumulated_root_overscroll().ToString()); scroll_result = host_impl_->ScrollUpdate( UpdateState(gfx::Point(), gfx::Vector2dF(0, -2.30f), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get()); EXPECT_TRUE(scroll_result.did_scroll); EXPECT_FALSE(scroll_result.did_overscroll_root); @@ -8789,13 +8854,13 @@ TEST_F(LayerTreeHostImplTest, NoOverscrollWhenNotAtEdge) { InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(0, 0), gfx::Vector2dF(0, 20), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .thread); scroll_result = host_impl_->ScrollUpdate( UpdateState(gfx::Point(), gfx::Vector2dF(0, 20), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); EXPECT_TRUE(scroll_result.did_scroll); EXPECT_TRUE(scroll_result.did_overscroll_root); @@ -8804,7 +8869,7 @@ TEST_F(LayerTreeHostImplTest, NoOverscrollWhenNotAtEdge) { scroll_result = host_impl_->ScrollUpdate( UpdateState(gfx::Point(), gfx::Vector2dF(0.02f, -0.01f), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); EXPECT_FALSE(scroll_result.did_scroll); EXPECT_FALSE(scroll_result.did_overscroll_root); @@ -8817,13 +8882,13 @@ TEST_F(LayerTreeHostImplTest, NoOverscrollWhenNotAtEdge) { host_impl_ ->ScrollBegin( BeginState(gfx::Point(0, 0), gfx::Vector2dF(-0.12f, 0.1f), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); scroll_result = host_impl_->ScrollUpdate( UpdateState(gfx::Point(), gfx::Vector2dF(-0.12f, 0.1f), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get()); EXPECT_FALSE(scroll_result.did_scroll); EXPECT_FALSE(scroll_result.did_overscroll_root); @@ -8851,12 +8916,12 @@ TEST_F(LayerTreeHostImplTest, NoOverscrollOnNonViewportLayers) { { host_impl_->ScrollBegin( BeginState(gfx::Point(0, 0), gfx::Vector2dF(100, 100), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen); + ui::ScrollInputType::kTouchscreen); host_impl_->ScrollUpdate(UpdateState(gfx::Point(0, 0), gfx::Vector2dF(100, 100), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); EXPECT_VECTOR_EQ(gfx::Vector2dF(100, 100), @@ -8870,7 +8935,7 @@ TEST_F(LayerTreeHostImplTest, NoOverscrollOnNonViewportLayers) { { InputHandlerScrollResult result = host_impl_->ScrollUpdate( UpdateState(gfx::Point(0, 0), gfx::Vector2dF(120, 140), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); EXPECT_VECTOR_EQ(gfx::Vector2dF(200, 200), @@ -8885,7 +8950,7 @@ TEST_F(LayerTreeHostImplTest, NoOverscrollOnNonViewportLayers) { { InputHandlerScrollResult result = host_impl_->ScrollUpdate( UpdateState(gfx::Point(0, 0), gfx::Vector2dF(20, 40), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); EXPECT_VECTOR_EQ(gfx::Vector2dF(200, 200), @@ -8920,9 +8985,9 @@ TEST_F(LayerTreeHostImplTest, OverscrollOnMainThread) { InputHandler::SCROLL_ON_MAIN_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(0, 60), gfx::Vector2dF(0, 10), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); // Overscroll initiated inside layers will be handled by the main thread. @@ -8932,9 +8997,9 @@ TEST_F(LayerTreeHostImplTest, OverscrollOnMainThread) { InputHandler::SCROLL_ON_MAIN_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(0, 0), gfx::Vector2dF(0, 10), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); } @@ -8968,12 +9033,12 @@ TEST_F(LayerTreeHostImplTest, ScrollFromOuterViewportSibling) { { host_impl_->ScrollBegin( BeginState(gfx::Point(0, 0), gfx::Vector2dF(1000, 1000), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen); + ui::ScrollInputType::kTouchscreen); host_impl_->ScrollUpdate(UpdateState(gfx::Point(0, 0), gfx::Vector2dF(1000, 1000), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); host_impl_->ScrollEnd(); @@ -8990,21 +9055,21 @@ TEST_F(LayerTreeHostImplTest, ScrollFromOuterViewportSibling) { { gfx::Vector2d scroll_delta(0, 10); host_impl_->ScrollBegin(BeginState(gfx::Point(0, 0), scroll_delta, - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen); - host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), scroll_delta, ScrollInputType::kTouchscreen) - .get()); + ui::ScrollInputType::kTouchscreen); + host_impl_->ScrollUpdate(UpdateState(gfx::Point(), scroll_delta, + ui::ScrollInputType::kTouchscreen) + .get()); EXPECT_EQ(0, host_impl_->active_tree()->CurrentTopControlsShownRatio()); EXPECT_VECTOR_EQ(gfx::Vector2dF(), CurrentScrollOffset(inner_scroll_layer)); - host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), scroll_delta, ScrollInputType::kTouchscreen) - .get()); - host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), scroll_delta, ScrollInputType::kTouchscreen) - .get()); + host_impl_->ScrollUpdate(UpdateState(gfx::Point(), scroll_delta, + ui::ScrollInputType::kTouchscreen) + .get()); + host_impl_->ScrollUpdate(UpdateState(gfx::Point(), scroll_delta, + ui::ScrollInputType::kTouchscreen) + .get()); EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 10), CurrentScrollOffset(inner_scroll_layer)); @@ -9047,12 +9112,12 @@ TEST_F(LayerTreeHostImplTest, ScrollChainingWithReplacedOuterViewport) { { host_impl_->ScrollBegin( BeginState(gfx::Point(0, 0), gfx::Vector2dF(200, 200), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen); + ui::ScrollInputType::kTouchscreen); host_impl_->ScrollUpdate(UpdateState(gfx::Point(0, 0), gfx::Vector2dF(200, 200), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); host_impl_->ScrollEnd(); @@ -9061,12 +9126,12 @@ TEST_F(LayerTreeHostImplTest, ScrollChainingWithReplacedOuterViewport) { host_impl_->ScrollBegin( BeginState(gfx::Point(0, 0), gfx::Vector2dF(200, 200), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen); + ui::ScrollInputType::kTouchscreen); host_impl_->ScrollUpdate(UpdateState(gfx::Point(0, 0), gfx::Vector2dF(200, 200), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); host_impl_->ScrollEnd(); @@ -9083,12 +9148,12 @@ TEST_F(LayerTreeHostImplTest, ScrollChainingWithReplacedOuterViewport) { { host_impl_->ScrollBegin( BeginState(gfx::Point(0, 0), gfx::Vector2dF(100, 100), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen); + ui::ScrollInputType::kTouchscreen); host_impl_->ScrollUpdate(UpdateState(gfx::Point(0, 0), gfx::Vector2dF(100, 100), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); host_impl_->ScrollEnd(); @@ -9113,12 +9178,12 @@ TEST_F(LayerTreeHostImplTest, ScrollChainingWithReplacedOuterViewport) { { host_impl_->ScrollBegin( BeginState(gfx::Point(0, 0), gfx::Vector2dF(100, 100), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen); + ui::ScrollInputType::kTouchscreen); host_impl_->ScrollUpdate(UpdateState(gfx::Point(0, 0), gfx::Vector2dF(100, 100), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); host_impl_->ScrollEnd(); @@ -9127,12 +9192,12 @@ TEST_F(LayerTreeHostImplTest, ScrollChainingWithReplacedOuterViewport) { host_impl_->ScrollBegin( BeginState(gfx::Point(0, 0), gfx::Vector2dF(100, 100), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen); + ui::ScrollInputType::kTouchscreen); host_impl_->ScrollUpdate(UpdateState(gfx::Point(0, 0), gfx::Vector2dF(100, 100), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); host_impl_->ScrollEnd(); @@ -9181,12 +9246,12 @@ TEST_F(LayerTreeHostImplTest, RootScrollerScrollNonDescendant) { // This should fully scroll the layer. host_impl_->ScrollBegin( BeginState(gfx::Point(0, 0), gfx::Vector2dF(1000, 1000), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen); + ui::ScrollInputType::kTouchscreen); host_impl_->ScrollUpdate(UpdateState(gfx::Point(0, 0), gfx::Vector2dF(1000, 1000), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); host_impl_->ScrollEnd(); @@ -9198,12 +9263,12 @@ TEST_F(LayerTreeHostImplTest, RootScrollerScrollNonDescendant) { // rather than an ancestor, we shouldn't chain to it. host_impl_->ScrollBegin( BeginState(gfx::Point(0, 0), gfx::Vector2dF(1000, 1000), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen); + ui::ScrollInputType::kTouchscreen); host_impl_->ScrollUpdate(UpdateState(gfx::Point(0, 0), gfx::Vector2dF(1000, 1000), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); host_impl_->ScrollEnd(); @@ -9231,9 +9296,9 @@ TEST_F(LayerTreeHostImplTest, RootScrollerScrollNonDescendant) { page_scale_factor = 2; gfx::Point anchor(viewport_size.width() / 2, viewport_size.height() / 2); host_impl_->ScrollBegin( - BeginState(anchor, gfx::Vector2dF(), ScrollInputType::kTouchscreen) + BeginState(anchor, gfx::Vector2dF(), ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen); + ui::ScrollInputType::kTouchscreen); host_impl_->PinchGestureBegin(); host_impl_->PinchGestureUpdate(page_scale_factor, anchor); host_impl_->PinchGestureEnd(anchor, true); @@ -9241,9 +9306,9 @@ TEST_F(LayerTreeHostImplTest, RootScrollerScrollNonDescendant) { EXPECT_VECTOR_EQ(gfx::Vector2dF(anchor.x() / 2, anchor.y() / 2), CurrentScrollOffset(inner_scroll_layer)); - host_impl_->ScrollUpdate( - UpdateState(anchor, viewport_size_vec, ScrollInputType::kTouchscreen) - .get()); + host_impl_->ScrollUpdate(UpdateState(anchor, viewport_size_vec, + ui::ScrollInputType::kTouchscreen) + .get()); EXPECT_VECTOR_EQ(ScaleVector2d(viewport_size_vec, 1 / page_scale_factor), CurrentScrollOffset(inner_scroll_layer)); @@ -9271,12 +9336,12 @@ TEST_F(LayerTreeHostImplTest, RootScrollerScrollNonDescendant) { // scroller, it shouldn't chain up to the inner viewport yet. host_impl_->ScrollBegin( BeginState(gfx::Point(0, 0), gfx::Vector2dF(2000, 2000), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen); + ui::ScrollInputType::kTouchscreen); host_impl_->ScrollUpdate(UpdateState(gfx::Point(0, 0), gfx::Vector2dF(2000, 2000), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); host_impl_->ScrollEnd(); @@ -9287,12 +9352,12 @@ TEST_F(LayerTreeHostImplTest, RootScrollerScrollNonDescendant) { // Scrolling now should chain up to the inner viewport. host_impl_->ScrollBegin( BeginState(gfx::Point(0, 0), gfx::Vector2dF(2000, 2000), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen); + ui::ScrollInputType::kTouchscreen); host_impl_->ScrollUpdate(UpdateState(gfx::Point(0, 0), gfx::Vector2dF(2000, 2000), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); host_impl_->ScrollEnd(); @@ -9303,12 +9368,12 @@ TEST_F(LayerTreeHostImplTest, RootScrollerScrollNonDescendant) { // No more scrolling should be possible. host_impl_->ScrollBegin( BeginState(gfx::Point(0, 0), gfx::Vector2dF(2000, 2000), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen); + ui::ScrollInputType::kTouchscreen); host_impl_->ScrollUpdate(UpdateState(gfx::Point(0, 0), gfx::Vector2dF(2000, 2000), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); host_impl_->ScrollEnd(); @@ -9339,9 +9404,9 @@ TEST_F(LayerTreeHostImplTest, OverscrollOnImplThread) { InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(0, 60), gfx::Vector2dF(0, 10), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); host_impl_->ScrollEnd(); @@ -9353,9 +9418,9 @@ TEST_F(LayerTreeHostImplTest, OverscrollOnImplThread) { InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(0, 0), gfx::Vector2dF(0, 10), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); } @@ -10793,10 +10858,11 @@ TEST_F(LayerTreeHostImplTest, ScrollUnknownNotOnAncestorChain) { DrawFrame(); - InputHandler::ScrollStatus status = host_impl_->ScrollBegin( - BeginState(gfx::Point(), gfx::Vector2dF(0, 10), ScrollInputType::kWheel) - .get(), - ScrollInputType::kWheel); + InputHandler::ScrollStatus status = + host_impl_->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10), + ui::ScrollInputType::kWheel) + .get(), + ui::ScrollInputType::kWheel); EXPECT_EQ(InputHandler::SCROLL_UNKNOWN, status.thread); EXPECT_EQ(MainThreadScrollingReason::kFailedHitTest, status.main_thread_scrolling_reasons); @@ -10828,10 +10894,11 @@ TEST_F(LayerTreeHostImplTest, ScrollUnknownScrollAncestorMismatch) { DrawFrame(); - InputHandler::ScrollStatus status = host_impl_->ScrollBegin( - BeginState(gfx::Point(), gfx::Vector2dF(0, 10), ScrollInputType::kWheel) - .get(), - ScrollInputType::kWheel); + InputHandler::ScrollStatus status = + host_impl_->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10), + ui::ScrollInputType::kWheel) + .get(), + ui::ScrollInputType::kWheel); EXPECT_EQ(InputHandler::SCROLL_UNKNOWN, status.thread); EXPECT_EQ(MainThreadScrollingReason::kFailedHitTest, status.main_thread_scrolling_reasons); @@ -10854,9 +10921,9 @@ TEST_F(LayerTreeHostImplTest, ScrollInvisibleScroller) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); EXPECT_EQ(child_scroll->scroll_tree_index(), @@ -11041,14 +11108,14 @@ TEST_F(LayerTreeHostImplTest, SimpleSwapPromiseMonitor) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .thread); EXPECT_TRUE( host_impl_ ->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, 10), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()) .did_scroll); host_impl_->ScrollEnd(); @@ -11062,14 +11129,14 @@ TEST_F(LayerTreeHostImplTest, SimpleSwapPromiseMonitor) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .thread); EXPECT_TRUE( host_impl_ ->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, 10), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()) .did_scroll); host_impl_->ScrollEnd(); @@ -11171,18 +11238,18 @@ TEST_F(LayerTreeHostImplWithBrowserControlsTest, InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, offset), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .thread); EXPECT_EQ(0, host_impl_->browser_controls_manager()->ControlsTopOffset()); EXPECT_EQ(gfx::Vector2dF().ToString(), CurrentScrollOffset(scroll_layer).ToString()); - result = host_impl_->ScrollUpdate(UpdateState(gfx::Point(), - gfx::Vector2d(0, offset), - ScrollInputType::kTouchscreen) - .get()); + result = host_impl_->ScrollUpdate( + UpdateState(gfx::Point(), gfx::Vector2d(0, offset), + ui::ScrollInputType::kTouchscreen) + .get()); EXPECT_EQ(result.unused_scroll_delta, gfx::Vector2d(0, 0)); EXPECT_TRUE(result.did_scroll); EXPECT_FLOAT_EQ(-offset, @@ -11193,10 +11260,10 @@ TEST_F(LayerTreeHostImplWithBrowserControlsTest, // Scroll across the boundary const float content_scroll = 20; offset = residue + content_scroll; - result = host_impl_->ScrollUpdate(UpdateState(gfx::Point(), - gfx::Vector2d(0, offset), - ScrollInputType::kTouchscreen) - .get()); + result = host_impl_->ScrollUpdate( + UpdateState(gfx::Point(), gfx::Vector2d(0, offset), + ui::ScrollInputType::kTouchscreen) + .get()); EXPECT_TRUE(result.did_scroll); EXPECT_EQ(result.unused_scroll_delta, gfx::Vector2d(0, 0)); EXPECT_EQ(-top_controls_height_, @@ -11206,10 +11273,10 @@ TEST_F(LayerTreeHostImplWithBrowserControlsTest, // Now scroll back to the top of the content offset = -content_scroll; - result = host_impl_->ScrollUpdate(UpdateState(gfx::Point(), - gfx::Vector2d(0, offset), - ScrollInputType::kTouchscreen) - .get()); + result = host_impl_->ScrollUpdate( + UpdateState(gfx::Point(), gfx::Vector2d(0, offset), + ui::ScrollInputType::kTouchscreen) + .get()); EXPECT_TRUE(result.did_scroll); EXPECT_EQ(result.unused_scroll_delta, gfx::Vector2d(0, 0)); EXPECT_EQ(-top_controls_height_, @@ -11219,10 +11286,10 @@ TEST_F(LayerTreeHostImplWithBrowserControlsTest, // And scroll the browser controls completely into view offset = -top_controls_height_; - result = host_impl_->ScrollUpdate(UpdateState(gfx::Point(), - gfx::Vector2d(0, offset), - ScrollInputType::kTouchscreen) - .get()); + result = host_impl_->ScrollUpdate( + UpdateState(gfx::Point(), gfx::Vector2d(0, offset), + ui::ScrollInputType::kTouchscreen) + .get()); EXPECT_TRUE(result.did_scroll); EXPECT_EQ(result.unused_scroll_delta, gfx::Vector2d(0, 0)); EXPECT_EQ(0, host_impl_->browser_controls_manager()->ControlsTopOffset()); @@ -11230,10 +11297,10 @@ TEST_F(LayerTreeHostImplWithBrowserControlsTest, CurrentScrollOffset(scroll_layer).ToString()); // And attempt to scroll past the end - result = host_impl_->ScrollUpdate(UpdateState(gfx::Point(), - gfx::Vector2d(0, offset), - ScrollInputType::kTouchscreen) - .get()); + result = host_impl_->ScrollUpdate( + UpdateState(gfx::Point(), gfx::Vector2d(0, offset), + ui::ScrollInputType::kTouchscreen) + .get()); EXPECT_FALSE(result.did_scroll); EXPECT_EQ(result.unused_scroll_delta, gfx::Vector2d(0, -50)); EXPECT_EQ(0, host_impl_->browser_controls_manager()->ControlsTopOffset()); @@ -11258,9 +11325,9 @@ TEST_F(LayerTreeHostImplWithBrowserControlsTest, EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, delta), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); EXPECT_EQ(0, host_impl_->browser_controls_manager()->ControlsTopOffset()); EXPECT_VECTOR_EQ(gfx::Vector2dF(), CurrentScrollOffset(viewport_layer)); @@ -11270,7 +11337,7 @@ TEST_F(LayerTreeHostImplWithBrowserControlsTest, EXPECT_TRUE( host_impl_ ->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, delta), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get()) .did_scroll); EXPECT_FLOAT_EQ(0, @@ -11281,7 +11348,7 @@ TEST_F(LayerTreeHostImplWithBrowserControlsTest, EXPECT_TRUE( host_impl_ ->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, delta), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get()) .did_scroll); EXPECT_FLOAT_EQ(0, @@ -11306,9 +11373,9 @@ TEST_F(LayerTreeHostImplWithBrowserControlsTest, InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, offset), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .thread); EXPECT_EQ(0, host_impl_->browser_controls_manager()->ControlsTopOffset()); EXPECT_EQ(gfx::Vector2dF().ToString(), @@ -11318,7 +11385,7 @@ TEST_F(LayerTreeHostImplWithBrowserControlsTest, EXPECT_TRUE( host_impl_ ->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, offset), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()) .did_scroll); EXPECT_FLOAT_EQ(-offset, @@ -11397,9 +11464,9 @@ TEST_F(LayerTreeHostImplWithBrowserControlsTest, InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, offset), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .thread); EXPECT_EQ(0, host_impl_->browser_controls_manager()->ControlsTopOffset()); EXPECT_EQ(gfx::Vector2dF(0, initial_scroll_offset).ToString(), @@ -11409,7 +11476,7 @@ TEST_F(LayerTreeHostImplWithBrowserControlsTest, EXPECT_TRUE( host_impl_ ->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, offset), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()) .did_scroll); EXPECT_FLOAT_EQ(-offset, @@ -11475,16 +11542,16 @@ TEST_F(LayerTreeHostImplWithBrowserControlsTest, InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, offset), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .thread); EXPECT_EQ(0, host_impl_->browser_controls_manager()->ControlsTopOffset()); EXPECT_TRUE( host_impl_ ->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, offset), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()) .did_scroll); EXPECT_EQ(-offset, @@ -11495,7 +11562,7 @@ TEST_F(LayerTreeHostImplWithBrowserControlsTest, EXPECT_TRUE( host_impl_ ->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, offset), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()) .did_scroll); EXPECT_EQ(gfx::Vector2dF(0, offset).ToString(), @@ -11504,7 +11571,7 @@ TEST_F(LayerTreeHostImplWithBrowserControlsTest, EXPECT_TRUE( host_impl_ ->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, offset), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()) .did_scroll); @@ -11519,7 +11586,7 @@ TEST_F(LayerTreeHostImplWithBrowserControlsTest, host_impl_ ->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, overscrollamount), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()) .did_scroll); EXPECT_EQ(gfx::Vector2dF(0, 2 * offset).ToString(), @@ -11530,7 +11597,7 @@ TEST_F(LayerTreeHostImplWithBrowserControlsTest, EXPECT_TRUE(host_impl_ ->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, -2 * offset), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()) .did_scroll); EXPECT_EQ(gfx::Vector2dF(0, 0).ToString(), @@ -11541,7 +11608,7 @@ TEST_F(LayerTreeHostImplWithBrowserControlsTest, EXPECT_TRUE( host_impl_ ->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, -offset), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()) .did_scroll); EXPECT_EQ(gfx::Vector2dF(0, 0).ToString(), @@ -11591,12 +11658,12 @@ TEST_F(LayerTreeHostImplBrowserControlsTest, { host_impl_->ScrollBegin( BeginState(gfx::Point(0, 0), gfx::Vector2dF(100, 100), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen); + ui::ScrollInputType::kTouchscreen); host_impl_->ScrollUpdate(UpdateState(gfx::Point(0, 0), gfx::Vector2dF(100, 100), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); host_impl_->ScrollEnd(); @@ -11666,18 +11733,16 @@ TEST_F(LayerTreeHostImplVirtualViewportTest, EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), scroll_delta, - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .thread); - EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point())); // Scroll near the edge of the outer viewport. - host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), scroll_delta, ScrollInputType::kTouchscreen) - .get()); + host_impl_->ScrollUpdate(UpdateState(gfx::Point(), scroll_delta, + ui::ScrollInputType::kTouchscreen) + .get()); inner_expected += scroll_delta; - EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point())); EXPECT_VECTOR_EQ(inner_expected, CurrentScrollOffset(inner_scroll)); EXPECT_VECTOR_EQ(outer_expected, CurrentScrollOffset(outer_scroll)); @@ -11687,13 +11752,11 @@ TEST_F(LayerTreeHostImplVirtualViewportTest, // and outer viewport layers is perfect. host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::ScaleVector2d(scroll_delta, 2), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); - EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point())); outer_expected += scroll_delta; inner_expected += scroll_delta; host_impl_->ScrollEnd(); - EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point())); EXPECT_VECTOR_EQ(inner_expected, CurrentScrollOffset(inner_scroll)); EXPECT_VECTOR_EQ(outer_expected, CurrentScrollOffset(outer_scroll)); @@ -11720,9 +11783,9 @@ TEST_F(LayerTreeHostImplVirtualViewportTest, InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->RootScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .thread); EXPECT_EQ(host_impl_->CurrentlyScrollingNode(), host_impl_->OuterViewportScrollNode()); @@ -11730,9 +11793,9 @@ TEST_F(LayerTreeHostImplVirtualViewportTest, EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .thread); EXPECT_EQ(host_impl_->CurrentlyScrollingNode()->id, child_scroll->scroll_tree_index()); @@ -11759,13 +11822,13 @@ TEST_F(LayerTreeHostImplVirtualViewportTest, EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 100), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .thread); scroll_result = host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2dF(0, 100), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get()); EXPECT_VECTOR_EQ(gfx::Vector2dF(), CurrentScrollOffset(inner_scroll)); @@ -12143,9 +12206,9 @@ TEST_F(LayerTreeHostImplTest, MacWheelIsNonAnimated) { ASSERT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(0, 50), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); host_impl_->ScrollUpdate( AnimatedUpdateState(gfx::Point(), gfx::Vector2d(0, 50)).get()); @@ -12181,9 +12244,9 @@ TEST_F(LayerTreeHostImplTest, ScrollAnimated) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(0, 50), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); host_impl_->ScrollUpdate( AnimatedUpdateState(gfx::Point(), gfx::Vector2d(0, 50)).get()); @@ -12283,8 +12346,8 @@ TEST_F(LayerTreeHostImplTest, ScrollAnimatedLatching) { { gfx::Point position(40, 40); auto begin_state = - BeginState(position, gfx::Vector2d(0, 50), ScrollInputType::kWheel); - host_impl_->ScrollBegin(begin_state.get(), ScrollInputType::kWheel); + BeginState(position, gfx::Vector2d(0, 50), ui::ScrollInputType::kWheel); + host_impl_->ScrollBegin(begin_state.get(), ui::ScrollInputType::kWheel); auto update_state = AnimatedUpdateState(position, gfx::Vector2d(0, 50)); host_impl_->ScrollUpdate(update_state.get()); EXPECT_EQ(outer_scroll->scroll_tree_index(), @@ -12329,8 +12392,8 @@ TEST_F(LayerTreeHostImplTest, ScrollAnimatedLatching) { { gfx::Point position(10, 10); auto begin_state = - BeginState(position, gfx::Vector2d(0, 50), ScrollInputType::kWheel); - host_impl_->ScrollBegin(begin_state.get(), ScrollInputType::kWheel); + BeginState(position, gfx::Vector2d(0, 50), ui::ScrollInputType::kWheel); + host_impl_->ScrollBegin(begin_state.get(), ui::ScrollInputType::kWheel); EXPECT_EQ(outer_scroll->scroll_tree_index(), host_impl_->CurrentlyScrollingNode()->id); } @@ -12388,9 +12451,9 @@ TEST_F(LayerTreeHostImplTest, ScrollAnimatedWhileZoomed) { InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(10, 10), gfx::Vector2d(0, 10), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); host_impl_->ScrollUpdate( AnimatedUpdateState(gfx::Point(10, 10), gfx::Vector2d(0, 10)).get()); @@ -12463,8 +12526,8 @@ TEST_F(LayerTreeHostImplTest, ScrollAnimatedUpdateInnerViewport) { // Start an animated scroll on the inner viewport. { auto begin_state = BeginState(gfx::Point(10, 10), gfx::Vector2d(0, 60), - ScrollInputType::kWheel); - host_impl_->ScrollBegin(begin_state.get(), ScrollInputType::kWheel); + ui::ScrollInputType::kWheel); + host_impl_->ScrollBegin(begin_state.get(), ui::ScrollInputType::kWheel); host_impl_->ScrollUpdate( AnimatedUpdateState(gfx::Point(10, 10), gfx::Vector2d(0, 60)).get()); @@ -12569,9 +12632,9 @@ TEST_F(LayerTreeHostImplTest, FadedOutPaintedOverlayScrollbarHitTest) { // Set up the scroll node and other state required for scrolling. host_impl_->ScrollBegin(BeginState(gfx::Point(350, 18), gfx::Vector2dF(), - ScrollInputType::kScrollbar) + ui::ScrollInputType::kScrollbar) .get(), - ScrollInputType::kScrollbar); + ui::ScrollInputType::kScrollbar); TestInputHandlerClient input_handler_client; host_impl_->BindToClient(&input_handler_client); @@ -12656,9 +12719,9 @@ TEST_F(LayerTreeHostImplTest, PointerMoveOutOfSequence) { begin_frame_args.frame_id.sequence_number++; host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->ScrollBegin(BeginState(gfx::Point(350, 18), gfx::Vector2dF(), - ScrollInputType::kScrollbar) + ui::ScrollInputType::kScrollbar) .get(), - ScrollInputType::kScrollbar); + ui::ScrollInputType::kScrollbar); EXPECT_TRUE(host_impl_->CurrentlyScrollingNode()); // The PointerMove(s) that follow should be handled and are expected to have a @@ -12759,9 +12822,9 @@ TEST_F(LayerTreeHostImplTest, SingleGSUForScrollbarThumbDragPerFrame) { scrollbar->SetOffsetToTransformParent(gfx::Vector2dF(345, 0)); host_impl_->ScrollBegin(BeginState(gfx::Point(350, 18), gfx::Vector2dF(), - ScrollInputType::kScrollbar) + ui::ScrollInputType::kScrollbar) .get(), - ScrollInputType::kScrollbar); + ui::ScrollInputType::kScrollbar); TestInputHandlerClient input_handler_client; host_impl_->BindToClient(&input_handler_client); @@ -12812,6 +12875,213 @@ TEST_F(LayerTreeHostImplTest, SingleGSUForScrollbarThumbDragPerFrame) { host_impl_ = nullptr; } +// Tests that an animated scrollbar scroll aborts when a different device (like +// a mousewheel) wants to animate the scroll offset. +TEST_F(LayerTreeHostImplTest, AnimatedScrollYielding) { + LayerTreeSettings settings = DefaultSettings(); + settings.compositor_threaded_scrollbar_scrolling = true; + CreateHostImpl(settings, CreateLayerTreeFrameSink()); + + // Setup the viewport. + const gfx::Size viewport_size = gfx::Size(360, 600); + const gfx::Size content_size = gfx::Size(345, 3800); + SetupViewportLayersOuterScrolls(viewport_size, content_size); + LayerImpl* scroll_layer = OuterViewportScrollLayer(); + + // Set up the scrollbar and its dimensions. + LayerTreeImpl* layer_tree_impl = host_impl_->active_tree(); + auto* scrollbar = AddLayer<PaintedScrollbarLayerImpl>( + layer_tree_impl, VERTICAL, /*is_left_side_vertical_scrollbar*/ false, + /*is_overlay*/ false); + + // TODO(arakeri): crbug.com/1070063 Setting the dimensions for scrollbar parts + // (like thumb, track etc) should be moved to SetupScrollbarLayer. + SetupScrollbarLayer(scroll_layer, scrollbar); + const gfx::Size scrollbar_size = gfx::Size(15, 600); + scrollbar->SetBounds(scrollbar_size); + host_impl_->set_force_smooth_wheel_scrolling_for_testing(true); + + // Set up the thumb dimensions. + scrollbar->SetThumbThickness(15); + scrollbar->SetThumbLength(50); + scrollbar->SetTrackRect(gfx::Rect(0, 15, 15, 575)); + + // Set up scrollbar arrows. + scrollbar->SetBackButtonRect( + gfx::Rect(gfx::Point(345, 0), gfx::Size(15, 15))); + scrollbar->SetForwardButtonRect( + gfx::Rect(gfx::Point(345, 570), gfx::Size(15, 15))); + scrollbar->SetOffsetToTransformParent(gfx::Vector2dF(345, 0)); + + TestInputHandlerClient input_handler_client; + host_impl_->BindToClient(&input_handler_client); + + { + // Set up an animated scrollbar autoscroll. + host_impl_->scrollbar_controller_for_testing()->HandlePointerDown( + gfx::PointF(350, 560), /*shift_modifier*/ false); + auto begin_state = BeginState(gfx::Point(350, 560), gfx::Vector2d(0, 40), + ui::ScrollInputType::kScrollbar); + EXPECT_EQ( + InputHandler::SCROLL_ON_IMPL_THREAD, + host_impl_ + ->ScrollBegin(begin_state.get(), ui::ScrollInputType::kScrollbar) + .thread); + auto update_state = UpdateState(gfx::Point(350, 560), gfx::Vector2dF(0, 40), + ui::ScrollInputType::kScrollbar); + update_state->data()->delta_granularity = + ui::ScrollGranularity::kScrollByPixel; + host_impl_->ScrollUpdate(update_state.get()); + + // Autoscroll animations should be active. + EXPECT_TRUE(host_impl_->scrollbar_controller_for_testing() + ->ScrollbarScrollIsActive()); + EXPECT_TRUE(GetImplAnimationHost()->ImplOnlyScrollAnimatingElement()); + } + + { + // While the cc autoscroll is in progress, a mousewheel tick takes place. + auto begin_state = BeginState(gfx::Point(), gfx::Vector2d(350, 560), + ui::ScrollInputType::kWheel); + begin_state->data()->delta_granularity = + ui::ScrollGranularity::kScrollByPixel; + + // InputHandlerProxy calls LayerTreeHostImpl::ScrollEnd to end the + // autoscroll as it has detected a device change. + host_impl_->ScrollEnd(); + + // This then leads to ScrollbarController::ResetState getting called which + // clears ScrollbarController::autoscroll_state_, + // captured_scrollbar_metadata_ etc. That means + // ScrollbarController::ScrollbarLayer should return null. + EXPECT_FALSE( + host_impl_->scrollbar_controller_for_testing()->ScrollbarLayer()); + + EXPECT_EQ( + InputHandler::SCROLL_ON_IMPL_THREAD, + host_impl_->ScrollBegin(begin_state.get(), ui::ScrollInputType::kWheel) + .thread); + + // Autoscroll animation should be aborted at this point. + EXPECT_FALSE(GetImplAnimationHost()->ImplOnlyScrollAnimatingElement()); + host_impl_->ScrollUpdate( + AnimatedUpdateState(gfx::Point(350, 560), gfx::Vector2d(0, 40)).get()); + + // Mousewheel animation should be active. + EXPECT_TRUE(GetImplAnimationHost()->ImplOnlyScrollAnimatingElement()); + + // This should not trigger any DCHECKs and will be a no-op. + host_impl_->scrollbar_controller_for_testing()->WillBeginImplFrame(); + } + + // Tear down the LayerTreeHostImpl before the InputHandlerClient. + host_impl_->ReleaseLayerTreeFrameSink(); + host_impl_ = nullptr; +} + +// Tests that changing the scroller length in the middle of a thumb drag doesn't +// cause the scroller to jump. +TEST_F(LayerTreeHostImplTest, ThumbDragScrollerLengthIncrease) { + LayerTreeSettings settings = DefaultSettings(); + settings.compositor_threaded_scrollbar_scrolling = true; + CreateHostImpl(settings, CreateLayerTreeFrameSink()); + + // Setup the viewport. + const gfx::Size viewport_size = gfx::Size(360, 600); + const gfx::Size content_size = gfx::Size(345, 3800); + SetupViewportLayersOuterScrolls(viewport_size, content_size); + LayerImpl* scroll_layer = OuterViewportScrollLayer(); + + // Set up the scrollbar and its dimensions. + LayerTreeImpl* layer_tree_impl = host_impl_->active_tree(); + auto* scrollbar = AddLayer<PaintedScrollbarLayerImpl>( + layer_tree_impl, VERTICAL, /*is_left_side_vertical_scrollbar*/ false, + /*is_overlay*/ false); + + // TODO(arakeri): crbug.com/1070063 Setting the dimensions for scrollbar parts + // (like thumb, track etc) should be moved to SetupScrollbarLayer. + SetupScrollbarLayer(scroll_layer, scrollbar); + const gfx::Size scrollbar_size = gfx::Size(15, 600); + scrollbar->SetBounds(scrollbar_size); + + // Set up the thumb dimensions. + scrollbar->SetThumbThickness(15); + scrollbar->SetThumbLength(50); + scrollbar->SetTrackRect(gfx::Rect(0, 15, 15, 575)); + + // Set up scrollbar arrows. + scrollbar->SetBackButtonRect( + gfx::Rect(gfx::Point(345, 0), gfx::Size(15, 15))); + scrollbar->SetForwardButtonRect( + gfx::Rect(gfx::Point(345, 570), gfx::Size(15, 15))); + scrollbar->SetOffsetToTransformParent(gfx::Vector2dF(345, 0)); + + TestInputHandlerClient input_handler_client; + host_impl_->BindToClient(&input_handler_client); + + // ----------------------------- Start frame 0 ----------------------------- + viz::BeginFrameArgs begin_frame_args = + viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); + base::TimeTicks start_time = + base::TimeTicks() + base::TimeDelta::FromMilliseconds(200); + begin_frame_args.frame_time = start_time; + begin_frame_args.frame_id.sequence_number++; + host_impl_->WillBeginImplFrame(begin_frame_args); + InputHandlerPointerResult result = + host_impl_->MouseDown(gfx::PointF(350, 18), /*shift_modifier*/ false); + EXPECT_EQ(result.scroll_offset.y(), 0); + EXPECT_EQ(result.type, PointerResultType::kScrollbarScroll); + host_impl_->DidFinishImplFrame(begin_frame_args); + + // ----------------------------- Start frame 1 ----------------------------- + begin_frame_args.frame_time = + start_time + base::TimeDelta::FromMilliseconds(250); + begin_frame_args.frame_id.sequence_number++; + host_impl_->WillBeginImplFrame(begin_frame_args); + + result = host_impl_->MouseMoveAt(gfx::Point(350, 20)); + EXPECT_EQ(result.scroll_offset.y(), 12); + + // This is intentional. The thumb drags that follow will test the behavior + // *after* the scroller length expansion. + scrollbar->SetScrollLayerLength(7000); + host_impl_->DidFinishImplFrame(begin_frame_args); + + // ----------------------------- Start frame 2 ----------------------------- + // The very first mousemove after the scroller length change will result in a + // zero offset. This is done to ensure that the scroller doesn't jump ahead + // when the length changes mid thumb drag. So, every time the scroller length + // changes mid thumb drag, a GSU is lost (in the worst case). + begin_frame_args.frame_time = + start_time + base::TimeDelta::FromMilliseconds(300); + begin_frame_args.frame_id.sequence_number++; + host_impl_->WillBeginImplFrame(begin_frame_args); + result = host_impl_->MouseMoveAt(gfx::Point(350, 22)); + EXPECT_EQ(result.scroll_offset.y(), 0); + host_impl_->DidFinishImplFrame(begin_frame_args); + + // ----------------------------- Start frame 3 ----------------------------- + // All subsequent mousemoves then produce deltas which are "displaced" by a + // certain amount. The previous mousemove (to y = 22) would have calculated + // the drag_state_->scroller_displacement value as 48 (based on the pointer + // movement). The current pointermove (to y = 26) calculates the delta as 97. + // Since delta -= drag_state_->scroller_displacement, the actual delta becomes + // 97 - 48 which is 49. The math that calculates the deltas (i.e 97 and 48) + // can be found in ScrollbarController::GetScrollDeltaForDragPosition. + begin_frame_args.frame_time = + start_time + base::TimeDelta::FromMilliseconds(350); + begin_frame_args.frame_id.sequence_number++; + host_impl_->WillBeginImplFrame(begin_frame_args); + result = host_impl_->MouseMoveAt(gfx::Point(350, 26)); + EXPECT_EQ(result.scroll_offset.y(), 49); + host_impl_->MouseUp(gfx::PointF(350, 26)); + host_impl_->DidFinishImplFrame(begin_frame_args); + + // Tear down the LayerTreeHostImpl before the InputHandlerClient. + host_impl_->ReleaseLayerTreeFrameSink(); + host_impl_ = nullptr; +} + TEST_F(LayerTreeHostImplTest, MainThreadFallback) { LayerTreeSettings settings = DefaultSettings(); settings.compositor_threaded_scrollbar_scrolling = true; @@ -12876,9 +13146,9 @@ TEST_F(LayerTreeHostImplTest, SecondScrollAnimatedBeginNotIgnored) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); host_impl_->ScrollEnd(); @@ -12887,9 +13157,9 @@ TEST_F(LayerTreeHostImplTest, SecondScrollAnimatedBeginNotIgnored) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); } @@ -12916,9 +13186,9 @@ TEST_F(LayerTreeHostImplTest, AnimatedScrollUpdateTargetBeforeStarting) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(0, 50), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); host_impl_->ScrollUpdate( AnimatedUpdateState(gfx::Point(), gfx::Vector2d(0, 50)).get()); @@ -12969,9 +13239,9 @@ TEST_F(LayerTreeHostImplTest, ScrollAnimatedWithDelay) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(0, 100), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); host_impl_->ScrollUpdate( AnimatedUpdateState(gfx::Point(), gfx::Vector2d(0, 100)).get(), @@ -13035,9 +13305,9 @@ TEST_F(LayerTreeHostImplTimelinesTest, ScrollAnimatedAborted) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(0, 50), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); host_impl_->ScrollUpdate( AnimatedUpdateState(gfx::Point(), gfx::Vector2d(0, 50)).get()); @@ -13073,22 +13343,20 @@ TEST_F(LayerTreeHostImplTimelinesTest, ScrollAnimatedAborted) { // Perform instant scroll. // Use "precise pixel" granularity to avoid animating. auto begin_state = BeginState(gfx::Point(0, y), gfx::Vector2dF(0, 50), - ScrollInputType::kWheel); + ui::ScrollInputType::kWheel); begin_state->data()->delta_granularity = ui::ScrollGranularity::kScrollByPrecisePixel; - EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_->ScrollBegin(begin_state.get(), ScrollInputType::kWheel) - .thread); - EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(0, y))); + EXPECT_EQ( + InputHandler::SCROLL_ON_IMPL_THREAD, + host_impl_->ScrollBegin(begin_state.get(), ui::ScrollInputType::kWheel) + .thread); auto update_state = UpdateState(gfx::Point(0, y), gfx::Vector2d(0, 50), - ScrollInputType::kWheel); + ui::ScrollInputType::kWheel); // Use "precise pixel" granularity to avoid animating. update_state->data()->delta_granularity = ui::ScrollGranularity::kScrollByPrecisePixel; host_impl_->ScrollUpdate(update_state.get()); - EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(0, y + 50))); host_impl_->ScrollEnd(); - EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point())); // The instant scroll should have marked the smooth scroll animation as // aborted. @@ -13118,9 +13386,9 @@ TEST_F(LayerTreeHostImplTimelinesTest, ScrollAnimated) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(0, 50), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); host_impl_->ScrollUpdate( AnimatedUpdateState(gfx::Point(), gfx::Vector2d(0, 50)).get()); @@ -13208,9 +13476,9 @@ TEST_F(LayerTreeHostImplTimelinesTest, ImplPinchZoomScrollAnimated) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(10, 20), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); host_impl_->ScrollUpdate( AnimatedUpdateState(gfx::Point(), gfx::Vector2d(10, 20)).get()); @@ -13304,9 +13572,9 @@ TEST_F(LayerTreeHostImplTimelinesTest, ImplPinchZoomScrollAnimatedUpdate) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(90, 90), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); host_impl_->ScrollUpdate( AnimatedUpdateState(gfx::Point(), gfx::Vector2d(90, 90)).get()); @@ -13368,9 +13636,9 @@ TEST_F(LayerTreeHostImplTimelinesTest, ScrollAnimatedNotUserScrollable) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(50, 50), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); host_impl_->ScrollUpdate( AnimatedUpdateState(gfx::Point(), gfx::Vector2d(50, 50)).get()); @@ -13452,10 +13720,10 @@ TEST_F(LayerTreeHostImplTimelinesTest, ScrollAnimatedChangingBounds) { viz::BeginFrameArgs begin_frame_args = viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); - host_impl_->ScrollBegin( - BeginState(gfx::Point(), gfx::Vector2d(500, 500), ScrollInputType::kWheel) - .get(), - ScrollInputType::kWheel); + host_impl_->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(500, 500), + ui::ScrollInputType::kWheel) + .get(), + ui::ScrollInputType::kWheel); host_impl_->ScrollUpdate( AnimatedUpdateState(gfx::Point(), gfx::Vector2d(500, 500)).get()); @@ -13571,9 +13839,9 @@ TEST_F(LayerTreeHostImplTest, WheelScrollWithPageScaleFactorOnInnerLayer) { float page_scale_delta = 2; host_impl_->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen); + ui::ScrollInputType::kTouchscreen); host_impl_->PinchGestureBegin(); host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point()); host_impl_->PinchGestureEnd(gfx::Point(), true); @@ -13583,14 +13851,15 @@ TEST_F(LayerTreeHostImplTest, WheelScrollWithPageScaleFactorOnInnerLayer) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), scroll_delta, - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); EXPECT_VECTOR_EQ(gfx::Vector2dF(), CurrentScrollOffset(scroll_layer)); host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), scroll_delta, ScrollInputType::kWheel).get()); + UpdateState(gfx::Point(), scroll_delta, ui::ScrollInputType::kWheel) + .get()); host_impl_->ScrollEnd(); EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 2.5), CurrentScrollOffset(scroll_layer)); } @@ -14649,13 +14918,13 @@ TEST_F(LayerTreeHostImplTest, RenderFrameMetadata) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .thread); - host_impl_->ScrollUpdate( - UpdateState(gfx::Point(), gfx::Vector2d(0, 10), ScrollInputType::kWheel) - .get()); + host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, 10), + ui::ScrollInputType::kWheel) + .get()); { RenderFrameMetadata metadata = StartDrawAndProduceRenderFrameMetadata(); EXPECT_EQ(gfx::Vector2dF(0, 10), metadata.root_scroll_offset); @@ -14716,10 +14985,10 @@ TEST_F(LayerTreeHostImplTest, RenderFrameMetadata) { #endif // Page scale should update metadata correctly (shrinking only the viewport). - host_impl_->ScrollBegin( - BeginState(gfx::Point(), gfx::Vector2dF(), ScrollInputType::kTouchscreen) - .get(), - ScrollInputType::kTouchscreen); + host_impl_->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(), + ui::ScrollInputType::kTouchscreen) + .get(), + ui::ScrollInputType::kTouchscreen); host_impl_->PinchGestureBegin(); host_impl_->PinchGestureUpdate(2, gfx::Point()); host_impl_->PinchGestureEnd(gfx::Point(), true); @@ -14873,11 +15142,11 @@ TEST_F(LayerTreeHostImplTest, // If the test leg contains a scroll, perform it. if (!test_leg.scroll_delta.IsZero()) { host_impl_->ScrollBegin(BeginState(gfx::Point(), test_leg.scroll_delta, - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get(), - ScrollInputType::kWheel); + ui::ScrollInputType::kWheel); host_impl_->ScrollUpdate(UpdateState(gfx::Point(), test_leg.scroll_delta, - ScrollInputType::kWheel) + ui::ScrollInputType::kWheel) .get()); } @@ -14910,9 +15179,9 @@ TEST_F(LayerTreeHostImplTest, ScrollUpdateDoesNotSetScrollingNode) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .thread); ScrollNode* scroll_node = scroll_tree.CurrentlyScrollingNode(); @@ -15336,9 +15605,9 @@ TEST_F(LayerTreeHostImplTest, TouchScrollOnAndroidScrollbar) { // scrollbar should not be hit. InputHandler::ScrollStatus status = host_impl_->ScrollBegin( BeginState(gfx::Point(350, 50), gfx::Vector2dF(0, 10), - ScrollInputType::kTouchscreen) + ui::ScrollInputType::kTouchscreen) .get(), - ScrollInputType::kTouchscreen); + ui::ScrollInputType::kTouchscreen); EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread); } @@ -15515,5 +15784,69 @@ TEST_F(ForceActivateAfterPaintWorkletPaintLayerTreeHostImplTest, EXPECT_FALSE(did_prepare_tiles_); } +// Verify that the device scale factor is not used to rescale scrollbar deltas +// in percent-based scrolling. +TEST_F(LayerTreeHostImplTest, PercentBasedScrollbarDeltasDSF3) { + LayerTreeSettings settings = DefaultSettings(); + settings.percent_based_scrolling = true; + settings.use_zoom_for_dsf = true; + settings.use_painted_device_scale_factor = true; + CreateHostImpl(settings, CreateLayerTreeFrameSink()); + + const gfx::Size viewport_size = gfx::Size(800, 800); + SetupViewportLayersOuterScrolls(viewport_size, viewport_size); + + LayerImpl* content_layer = AddContentLayer(); + LayerImpl* scroll_layer = AddScrollableLayer( + content_layer, gfx::Size(185, 200), gfx::Size(185, 3800)); + + auto* scrollbar = AddLayer<PaintedScrollbarLayerImpl>( + host_impl_->active_tree(), VERTICAL, false, true); + SetupScrollbarLayer(scroll_layer, scrollbar); + + scrollbar->SetBounds(gfx::Size(15, 200)); + + scrollbar->SetThumbThickness(15); + scrollbar->SetThumbLength(50); + scrollbar->SetTrackRect(gfx::Rect(0, 15, 15, 185)); + + scrollbar->SetBackButtonRect(gfx::Rect(gfx::Point(0, 0), gfx::Size(15, 15))); + scrollbar->SetForwardButtonRect( + gfx::Rect(gfx::Point(0, 185), gfx::Size(15, 15))); + scrollbar->SetOffsetToTransformParent(gfx::Vector2dF(185, 0)); + + DrawFrame(); + + TestInputHandlerClient input_handler_client; + host_impl_->BindToClient(&input_handler_client); + + // Test scrolling with device scale factor = 3. + const float expected_delta = kPercentDeltaForDirectionalScroll * 200u; + + host_impl_->active_tree()->set_painted_device_scale_factor(3); + + InputHandlerPointerResult scroll_result = + host_impl_->MouseDown(gfx::PointF(190, 190), false); + host_impl_->MouseUp(gfx::PointF(190, 190)); + + EXPECT_EQ(scroll_result.scroll_offset.y(), expected_delta); + EXPECT_FALSE(GetScrollNode(scroll_layer)->main_thread_scrolling_reasons); + + // Test with DSF = 1. As the scrollable layers aren't rescaled by the DSF, + // neither the scroll offset, the same result for DSF = 3 is expected. + host_impl_->active_tree()->set_painted_device_scale_factor(1); + + InputHandlerPointerResult scroll_with_dsf_1 = + host_impl_->MouseDown(gfx::PointF(190, 190), false); + host_impl_->MouseUp(gfx::PointF(190, 190)); + + EXPECT_EQ(scroll_with_dsf_1.scroll_offset.y(), expected_delta); + EXPECT_FALSE(GetScrollNode(scroll_layer)->main_thread_scrolling_reasons); + + // Tear down the LayerTreeHostImpl before the InputHandlerClient. + host_impl_->ReleaseLayerTreeFrameSink(); + host_impl_ = nullptr; +} + } // namespace } // namespace cc diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_blending.cc b/chromium/cc/trees/layer_tree_host_pixeltest_blending.cc index 4c8f1e9603c..b55a4b3b733 100644 --- a/chromium/cc/trees/layer_tree_host_pixeltest_blending.cc +++ b/chromium/cc/trees/layer_tree_host_pixeltest_blending.cc @@ -259,10 +259,8 @@ class LayerTreeHostBlendingPixelTest std::vector<PixelResourceTestCase> const kTestCases = { {LayerTreeTest::RENDERER_SOFTWARE, SOFTWARE}, -#if !defined(GL_NOT_ON_PLATFORM) {LayerTreeTest::RENDERER_GL, ZERO_COPY}, {LayerTreeTest::RENDERER_SKIA_GL, GPU}, -#endif // !defined(GL_NOT_ON_PLATFORM) #if defined(ENABLE_CC_VULKAN_TESTS) {LayerTreeTest::RENDERER_SKIA_VK, GPU}, #endif // defined(ENABLE_CC_VULKAN_TESTS) diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_filters.cc b/chromium/cc/trees/layer_tree_host_pixeltest_filters.cc index b8214db99a0..c93b84e3271 100644 --- a/chromium/cc/trees/layer_tree_host_pixeltest_filters.cc +++ b/chromium/cc/trees/layer_tree_host_pixeltest_filters.cc @@ -36,6 +36,8 @@ class LayerTreeHostFiltersPixelTest return "skia_gl"; case RENDERER_SKIA_VK: return "skia_vk"; + case RENDERER_SKIA_DAWN: + return "skia_dawn"; case RENDERER_SOFTWARE: return "sw"; } @@ -71,14 +73,14 @@ class LayerTreeHostFiltersPixelTest }; LayerTreeTest::RendererType const kRendererTypes[] = { -#if !defined(GL_NOT_ON_PLATFORM) - LayerTreeTest::RENDERER_GL, - LayerTreeTest::RENDERER_SKIA_GL, -#endif // !defined(GL_NOT_ON_PLATFORM) + LayerTreeTest::RENDERER_GL, LayerTreeTest::RENDERER_SKIA_GL, LayerTreeTest::RENDERER_SOFTWARE, #if defined(ENABLE_CC_VULKAN_TESTS) LayerTreeTest::RENDERER_SKIA_VK, #endif // defined(ENABLE_CC_VULKAN_TESTS) +#if defined(ENABLE_CC_DAWN_TESTS) + LayerTreeTest::RENDERER_SKIA_DAWN, +#endif // defined(ENABLE_CC_DAWN_TESTS) }; INSTANTIATE_TEST_SUITE_P(All, @@ -87,21 +89,20 @@ INSTANTIATE_TEST_SUITE_P(All, using LayerTreeHostFiltersPixelTestGPU = LayerTreeHostFiltersPixelTest; -#if !defined(GL_NOT_ON_PLATFORM) || defined(ENABLE_CC_VULKAN_TESTS) LayerTreeTest::RendererType const kRendererTypesGpu[] = { -#if !defined(GL_NOT_ON_PLATFORM) LayerTreeTest::RENDERER_GL, LayerTreeTest::RENDERER_SKIA_GL, -#endif // !defined(GL_NOT_ON_PLATFORM) #if defined(ENABLE_CC_VULKAN_TESTS) LayerTreeTest::RENDERER_SKIA_VK, #endif // defined(ENABLE_CC_VULKAN_TESTS) +#if defined(ENABLE_CC_DAWN_TESTS) + LayerTreeTest::RENDERER_SKIA_DAWN, +#endif // defined(ENABLE_CC_DAWN_TESTS) }; INSTANTIATE_TEST_SUITE_P(All, LayerTreeHostFiltersPixelTestGPU, ::testing::ValuesIn(kRendererTypesGpu)); -#endif // !defined(GL_NOT_ON_PLATFORM) || defined(ENABLE_CC_VULKAN_TESTS) TEST_P(LayerTreeHostFiltersPixelTest, BackdropFilterBlurRect) { scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer( @@ -196,6 +197,9 @@ TEST_P(LayerTreeHostFiltersPixelTest, BackdropFilterBlurRadius) { float average_error_allowed_in_bad_pixels = 1.f; int large_error_allowed = 1; int small_error_allowed = 0; + // Windows using Dawn D3D12 has 2982 pixels off by 1. + if (use_d3d12()) + percentage_pixels_large_error = 1.864f; // 2982px / (400*400) pixel_comparator_.reset(new FuzzyPixelComparator( true, // discard_alpha percentage_pixels_large_error, percentage_pixels_small_error, @@ -296,7 +300,7 @@ TEST_P(LayerTreeHostFiltersPixelTest, BackdropFilterBlurOutsets) { large_error_allowed, small_error_allowed)); #else - if (use_vulkan()) + if (use_skia_vulkan()) pixel_comparator_ = std::make_unique<FuzzyPixelOffByOneComparator>(true); #endif @@ -425,11 +429,19 @@ class LayerTreeHostBlurFiltersPixelTestGPULayerList } }; -#if !defined(GL_NOT_ON_PLATFORM) || defined(ENABLE_CC_VULKAN_TESTS) +// TODO(sgilhuly): Enable these tests for Skia Dawn, and switch over to using +// kRendererTypesGpu. +LayerTreeTest::RendererType const kRendererTypesGpuNonDawn[] = { + LayerTreeTest::RENDERER_GL, + LayerTreeTest::RENDERER_SKIA_GL, +#if defined(ENABLE_CC_VULKAN_TESTS) + LayerTreeTest::RENDERER_SKIA_VK, +#endif // defined(ENABLE_CC_VULKAN_TESTS) +}; + INSTANTIATE_TEST_SUITE_P(PixelResourceTest, LayerTreeHostBlurFiltersPixelTestGPULayerList, - ::testing::ValuesIn(kRendererTypesGpu)); -#endif // !defined(GL_NOT_ON_PLATFORM) || defined(ENABLE_CC_VULKAN_TESTS) + ::testing::ValuesIn(kRendererTypesGpuNonDawn)); TEST_P(LayerTreeHostBlurFiltersPixelTestGPULayerList, BackdropFilterBlurOffAxis) { @@ -453,7 +465,7 @@ TEST_P(LayerTreeHostBlurFiltersPixelTestGPULayerList, large_error_allowed, small_error_allowed)); #else - if (use_vulkan()) + if (use_skia_vulkan()) pixel_comparator_ = std::make_unique<FuzzyPixelOffByOneComparator>(true); #endif @@ -645,6 +657,11 @@ TEST_P(LayerTreeHostFiltersPixelTest, ImageFilterScaled) { // Windows has 153 pixels off by at most 2: crbug.com/225027 float percentage_pixels_large_error = 0.3825f; // 153px / (200*200) int large_error_allowed = 2; + // Windows using Dawn D3D12 has 166 pixels off by 1. + if (use_d3d12()) { + percentage_pixels_large_error = 0.415f; // 166px / (200*200) + large_error_allowed = 1; + } #elif defined(_MIPS_ARCH_LOONGSON) // Loongson has 2 pixels off by at most 2: crbug.com/819075 float percentage_pixels_large_error = 0.005f; // 2px / (200*200) @@ -755,20 +772,27 @@ TEST_P(LayerTreeHostFiltersPixelTest, ImageRenderSurfaceScaled) { background->AddChild(render_surface_layer); - // Software has some huge differences in the AA'd pixels on the different - // trybots. See crbug.com/452198. + float percentage_pixels_large_error = 0.0f; + float percentage_pixels_small_error = 0.0f; + float average_error_allowed_in_bad_pixels = 0.0f; + int large_error_allowed = 0; + int small_error_allowed = 0; if (use_software_renderer()) { - float percentage_pixels_large_error = 0.686f; - float percentage_pixels_small_error = 0.0f; - float average_error_allowed_in_bad_pixels = 16.f; - int large_error_allowed = 17; - int small_error_allowed = 0; - pixel_comparator_.reset(new FuzzyPixelComparator( - true, // discard_alpha - percentage_pixels_large_error, percentage_pixels_small_error, - average_error_allowed_in_bad_pixels, large_error_allowed, - small_error_allowed)); + // Software has some huge differences in the AA'd pixels on the different + // trybots. See crbug.com/452198. + percentage_pixels_large_error = 0.686f; + average_error_allowed_in_bad_pixels = 16.f; + large_error_allowed = 17; + } else if (use_d3d12()) { + // Windows using Dawn D3D12 has 25 pixels off by 1. + percentage_pixels_large_error = 0.028; + average_error_allowed_in_bad_pixels = 1.f; + large_error_allowed = 1; } + pixel_comparator_.reset(new FuzzyPixelComparator( + /*discard_alpha=*/true, percentage_pixels_large_error, + percentage_pixels_small_error, average_error_allowed_in_bad_pixels, + large_error_allowed, small_error_allowed)); RunPixelTest( background, @@ -776,7 +800,24 @@ TEST_P(LayerTreeHostFiltersPixelTest, ImageRenderSurfaceScaled) { .InsertBeforeExtensionASCII(GetRendererSuffix())); } -TEST_P(LayerTreeHostFiltersPixelTest, ZoomFilter) { +// TODO(sgilhuly): Enable these tests for Skia Dawn, and switch over to using +// kRendererTypes. +using LayerTreeHostFiltersPixelTestNonDawn = LayerTreeHostFiltersPixelTest; + +LayerTreeTest::RendererType const kRendererTypesNonDawn[] = { + LayerTreeTest::RENDERER_GL, + LayerTreeTest::RENDERER_SKIA_GL, + LayerTreeTest::RENDERER_SOFTWARE, +#if defined(ENABLE_CC_VULKAN_TESTS) + LayerTreeTest::RENDERER_SKIA_VK, +#endif // defined(ENABLE_CC_VULKAN_TESTS) +}; + +INSTANTIATE_TEST_SUITE_P(All, + LayerTreeHostFiltersPixelTestNonDawn, + ::testing::ValuesIn(kRendererTypesNonDawn)); + +TEST_P(LayerTreeHostFiltersPixelTestNonDawn, ZoomFilter) { scoped_refptr<SolidColorLayer> root = CreateSolidColorLayer(gfx::Rect(300, 300), SK_ColorWHITE); @@ -879,6 +920,9 @@ TEST_P(LayerTreeHostFiltersPixelTest, RotatedFilter) { float percentage_pixels_large_error = 0.00111112f; // 1px / (300*300) float average_error_allowed_in_bad_pixels = 1.f; int large_error_allowed = 1; + // Windows using Dawn D3D12 has 104 pixels off by 1. + if (use_d3d12()) + percentage_pixels_large_error = 0.115556f; // 104px / (300*300) #else float percentage_pixels_large_error = 0.0f; // 1px / (300*300) float average_error_allowed_in_bad_pixels = 0.0f; @@ -936,6 +980,9 @@ TEST_P(LayerTreeHostFiltersPixelTest, RotatedDropShadowFilter) { float percentage_pixels_large_error = 0.00333334f; // 3px / (300*300) float average_error_allowed_in_bad_pixels = 1.f; int large_error_allowed = 1; + // Windows using Dawn D3D12 has 22 pixels off by 1. + if (use_d3d12()) + percentage_pixels_large_error = 0.02445; // 22px / (300*300) #endif float percentage_pixels_small_error = 0.0f; int small_error_allowed = 0; @@ -945,7 +992,7 @@ TEST_P(LayerTreeHostFiltersPixelTest, RotatedDropShadowFilter) { average_error_allowed_in_bad_pixels, large_error_allowed, small_error_allowed)); #else - if (use_vulkan()) + if (use_skia_vulkan()) pixel_comparator_ = std::make_unique<FuzzyPixelOffByOneComparator>(true); #endif @@ -989,7 +1036,7 @@ TEST_P(LayerTreeHostFiltersPixelTest, TranslatedFilter) { parent->AddChild(child); clip->AddChild(parent); - if (use_software_renderer()) + if (use_software_renderer() || renderer_type_ == RENDERER_SKIA_DAWN) pixel_comparator_ = std::make_unique<FuzzyPixelOffByOneComparator>(true); RunPixelTest(clip, base::FilePath( @@ -1225,6 +1272,18 @@ class BackdropFilterInvertTest : public LayerTreeHostFiltersPixelTest { base::NumberToString(device_scale_factor) + "x"); if (use_software_renderer()) { expected_result = expected_result.InsertBeforeExtensionASCII("_sw"); + } else if (use_d3d12()) { + // Windows using Dawn D3D12 has 16 pixels off by 1. + float percentage_pixels_large_error = 0.04f; // 16px / (200*200) + float average_error_allowed_in_bad_pixels = 1.f; + int large_error_allowed = 1; + float percentage_pixels_small_error = 0.0f; + int small_error_allowed = 0; + pixel_comparator_.reset(new FuzzyPixelComparator( + true, // discard_alpha + percentage_pixels_large_error, percentage_pixels_small_error, + average_error_allowed_in_bad_pixels, large_error_allowed, + small_error_allowed)); } RunPixelTest(std::move(root), expected_result); } diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_masks.cc b/chromium/cc/trees/layer_tree_host_pixeltest_masks.cc index e0b98e69b30..7e39b785622 100644 --- a/chromium/cc/trees/layer_tree_host_pixeltest_masks.cc +++ b/chromium/cc/trees/layer_tree_host_pixeltest_masks.cc @@ -30,14 +30,12 @@ namespace { // https://crbug.com/979703 std::vector<PixelResourceTestCase> const kTestCases = { {LayerTreeTest::RENDERER_SOFTWARE, SOFTWARE}, -#if !defined(GL_NOT_ON_PLATFORM) {LayerTreeTest::RENDERER_GL, GPU}, {LayerTreeTest::RENDERER_GL, ONE_COPY}, {LayerTreeTest::RENDERER_GL, ZERO_COPY}, {LayerTreeTest::RENDERER_SKIA_GL, GPU}, {LayerTreeTest::RENDERER_SKIA_GL, ONE_COPY}, {LayerTreeTest::RENDERER_SKIA_GL, ZERO_COPY}, -#endif // !defined(GL_NOT_ON_PLATFORM) #if defined(ENABLE_CC_VULKAN_TESTS) {LayerTreeTest::RENDERER_SKIA_VK, GPU}, #endif // defined(ENABLE_CC_VULKAN_TESTS) @@ -585,7 +583,7 @@ TEST_P(LayerTreeHostMasksForBackdropFiltersPixelTestWithLayerList, Test) { ? base::FilePath(FILE_PATH_LITERAL("mask_of_backdrop_filter_gpu.png")) : base::FilePath(FILE_PATH_LITERAL("mask_of_backdrop_filter.png")); - if (use_vulkan() && raster_type() == GPU) { + if (use_skia_vulkan() && raster_type() == GPU) { // Vulkan with GPU raster has 4 pixels errors (the circle mask shape is // slight different). float percentage_pixels_large_error = 0.04f; // 4px / (100*100) @@ -644,7 +642,7 @@ TEST_P(LayerTreeHostMasksForBackdropFiltersPixelTestWithLayerTree, Test) { ? base::FilePath(FILE_PATH_LITERAL("mask_of_backdrop_filter_gpu.png")) : base::FilePath(FILE_PATH_LITERAL("mask_of_backdrop_filter.png")); - if (use_vulkan() && raster_type() == GPU) { + if (use_skia_vulkan() && raster_type() == GPU) { // Vulkan with GPU raster has 4 pixels errors (the circle mask shape is // slight different). float percentage_pixels_large_error = 0.04f; // 4px / (100*100) @@ -874,7 +872,6 @@ class LayerTreeHostMaskAsBlendingPixelTest MaskTestConfig const kTestConfigs[] = { MaskTestConfig{{LayerTreeTest::RENDERER_SOFTWARE, SOFTWARE}, 0}, -#if !defined(GL_NOT_ON_PLATFORM) MaskTestConfig{{LayerTreeTest::RENDERER_GL, ZERO_COPY}, 0}, MaskTestConfig{{LayerTreeTest::RENDERER_GL, ZERO_COPY}, kUseAntialiasing}, MaskTestConfig{{LayerTreeTest::RENDERER_GL, ZERO_COPY}, kForceShaders}, @@ -883,7 +880,6 @@ MaskTestConfig const kTestConfigs[] = { MaskTestConfig{{LayerTreeTest::RENDERER_SKIA_GL, ZERO_COPY}, 0}, MaskTestConfig{{LayerTreeTest::RENDERER_SKIA_GL, ZERO_COPY}, kUseAntialiasing}, -#endif // !defined(GL_NOT_ON_PLATFORM) #if defined(ENABLE_CC_VULKAN_TESTS) MaskTestConfig{{LayerTreeTest::RENDERER_SKIA_VK, ZERO_COPY}, 0}, MaskTestConfig{{LayerTreeTest::RENDERER_SKIA_VK, ZERO_COPY}, diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_mirror.cc b/chromium/cc/trees/layer_tree_host_pixeltest_mirror.cc index f34ac66a8af..82a9f66663b 100644 --- a/chromium/cc/trees/layer_tree_host_pixeltest_mirror.cc +++ b/chromium/cc/trees/layer_tree_host_pixeltest_mirror.cc @@ -25,10 +25,8 @@ class LayerTreeHostMirrorPixelTest }; const LayerTreeTest::RendererType kRendererTypes[] = { -#if !defined(GL_NOT_ON_PLATFORM) LayerTreeTest::RENDERER_GL, LayerTreeTest::RENDERER_SKIA_GL, -#endif // !defined(GL_NOT_ON_PLATFORM) LayerTreeTest::RENDERER_SOFTWARE, #if defined(ENABLE_CC_VULKAN_TESTS) LayerTreeTest::RENDERER_SKIA_VK, @@ -72,10 +70,8 @@ TEST_P(LayerTreeHostMirrorPixelTest, MirrorLayer) { max_abs_error_limit, small_error_threshold); } -#if defined(ENABLE_CC_VULKAN_TESTS) && defined(OS_LINUX) - if (use_vulkan()) + if (use_skia_vulkan()) pixel_comparator_ = std::make_unique<FuzzyPixelOffByOneComparator>(true); -#endif RunPixelTest(background, base::FilePath(FILE_PATH_LITERAL("mirror_layer.png"))); diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_readback.cc b/chromium/cc/trees/layer_tree_host_pixeltest_readback.cc index d314bdd7efb..6d7e9365f4b 100644 --- a/chromium/cc/trees/layer_tree_host_pixeltest_readback.cc +++ b/chromium/cc/trees/layer_tree_host_pixeltest_readback.cc @@ -418,7 +418,6 @@ TEST_P(LayerTreeHostReadbackPixelTest, MultipleReadbacksOnLayer) { // readback. ReadbackTestConfig const kTestConfigs[] = { ReadbackTestConfig{LayerTreeTest::RENDERER_SOFTWARE, READBACK_BITMAP}, -#if !defined(GL_NOT_ON_PLATFORM) ReadbackTestConfig{LayerTreeTest::RENDERER_GL, READBACK_TEXTURE}, ReadbackTestConfig{LayerTreeTest::RENDERER_GL, READBACK_BITMAP}, // TODO(crbug.com/1046788): The skia readback path doesn't support @@ -427,7 +426,6 @@ ReadbackTestConfig const kTestConfigs[] = { // // ReadbackTestConfig{LayerTreeTest::RENDERER_SKIA_GL, READBACK_TEXTURE}, ReadbackTestConfig{LayerTreeTest::RENDERER_SKIA_GL, READBACK_BITMAP}, -#endif // !defined(GL_NOT_ON_PLATFORM) #if defined(ENABLE_CC_VULKAN_TESTS) ReadbackTestConfig{LayerTreeTest::RENDERER_SKIA_VK, READBACK_BITMAP}, #endif // defined(ENABLE_CC_VULKAN_TESTS) @@ -441,7 +439,6 @@ INSTANTIATE_TEST_SUITE_P(All, // MSan are used. ReadbackTestConfig const kMaybeVulkanTestConfigs[] = { ReadbackTestConfig{LayerTreeTest::RENDERER_SOFTWARE, READBACK_BITMAP}, -#if !defined(GL_NOT_ON_PLATFORM) ReadbackTestConfig{LayerTreeTest::RENDERER_GL, READBACK_TEXTURE}, ReadbackTestConfig{LayerTreeTest::RENDERER_GL, READBACK_BITMAP}, // TODO(crbug.com/1046788): The skia readback path doesn't support @@ -450,7 +447,6 @@ ReadbackTestConfig const kMaybeVulkanTestConfigs[] = { // // ReadbackTestConfig{LayerTreeTest::RENDERER_SKIA_GL, READBACK_TEXTURE}, ReadbackTestConfig{LayerTreeTest::RENDERER_SKIA_GL, READBACK_BITMAP}, -#endif // !defined(GL_NOT_ON_PLATFORM) #if defined(ENABLE_CC_VULKAN_TESTS) && !defined(THREAD_SANITIZER) && \ !defined(MEMORY_SANITIZER) ReadbackTestConfig{LayerTreeTest::RENDERER_SKIA_VK, READBACK_BITMAP}, diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_scrollbars.cc b/chromium/cc/trees/layer_tree_host_pixeltest_scrollbars.cc index a04ddde31ad..5dc2efdea40 100644 --- a/chromium/cc/trees/layer_tree_host_pixeltest_scrollbars.cc +++ b/chromium/cc/trees/layer_tree_host_pixeltest_scrollbars.cc @@ -73,12 +73,9 @@ class PaintedScrollbar : public FakeScrollbar { SkColor color_ = SK_ColorGREEN; }; -#if !defined(GL_NOT_ON_PLATFORM) || defined(ENABLE_CC_VULKAN_TESTS) LayerTreeTest::RendererType const kRendererTypes[] = { -#if !defined(GL_NOT_ON_PLATFORM) LayerTreeTest::RENDERER_GL, LayerTreeTest::RENDERER_SKIA_GL, -#endif // !defined(GL_NOT_ON_PLATFORM) #if defined(ENABLE_CC_VULKAN_TESTS) LayerTreeTest::RENDERER_SKIA_VK, #endif // defined(ENABLE_CC_VULKAN_TESTS) @@ -87,7 +84,6 @@ LayerTreeTest::RendererType const kRendererTypes[] = { INSTANTIATE_TEST_SUITE_P(All, LayerTreeHostScrollbarsPixelTest, ::testing::ValuesIn(kRendererTypes)); -#endif // !defined(GL_NOT_ON_PLATFORM) || defined(ENABLE_CC_VULKAN_TESTS) TEST_P(LayerTreeHostScrollbarsPixelTest, NoScale) { scoped_refptr<SolidColorLayer> background = @@ -188,7 +184,7 @@ TEST_P(LayerTreeHostScrollbarsPixelTest, MAYBE_HugeTransformScale) { pixel_comparator_ = std::make_unique<FuzzyPixelOffByOneComparator>(true); RunPixelTest(background, - use_vulkan() + use_skia_vulkan() ? base::FilePath(FILE_PATH_LITERAL("spiral_64_scale_vk.png")) : base::FilePath(FILE_PATH_LITERAL("spiral_64_scale.png"))); } @@ -200,8 +196,7 @@ class LayerTreeHostOverlayScrollbarsPixelTest void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) override { LayerImpl* layer = host_impl->active_tree()->LayerById(scrollbar_layer_id_); - ScrollbarLayerImplBase* scrollbar = layer->ToScrollbarLayer(); - scrollbar->SetThumbThicknessScaleFactor(thickness_scale_); + ToScrollbarLayer(layer)->SetThumbThicknessScaleFactor(thickness_scale_); } int scrollbar_layer_id_; @@ -256,11 +251,9 @@ class PaintedOverlayScrollbar : public FakeScrollbar { ~PaintedOverlayScrollbar() override = default; }; -#if !defined(GL_NOT_ON_PLATFORM) || defined(ENABLE_CC_VULKAN_TESTS) INSTANTIATE_TEST_SUITE_P(All, LayerTreeHostOverlayScrollbarsPixelTest, ::testing::ValuesIn(kRendererTypes)); -#endif // !defined(GL_NOT_ON_PLATFORM) || defined(ENABLE_CC_VULKAN_TESTS) // Simulate increasing the thickness of a painted overlay scrollbar. Ensure that // the scrollbar border remains crisp. diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_synchronous.cc b/chromium/cc/trees/layer_tree_host_pixeltest_synchronous.cc index c832b869d30..2739d9880bb 100644 --- a/chromium/cc/trees/layer_tree_host_pixeltest_synchronous.cc +++ b/chromium/cc/trees/layer_tree_host_pixeltest_synchronous.cc @@ -54,12 +54,9 @@ class LayerTreeHostSynchronousPixelTest bool use_zero_copy_ = false; }; -#if !defined(GL_NOT_ON_PLATFORM) || defined(ENABLE_CC_VULKAN_TESTS) LayerTreeTest::RendererType const kRendererTypesGpu[] = { -#if !defined(GL_NOT_ON_PLATFORM) LayerTreeTest::RENDERER_GL, LayerTreeTest::RENDERER_SKIA_GL, -#endif // !defined(GL_NOT_ON_PLATFORM) #if defined(ENABLE_CC_VULKAN_TESTS) LayerTreeTest::RENDERER_SKIA_VK, #endif // defined(ENABLE_CC_VULKAN_TESTS) @@ -68,7 +65,6 @@ LayerTreeTest::RendererType const kRendererTypesGpu[] = { INSTANTIATE_TEST_SUITE_P(All, LayerTreeHostSynchronousPixelTest, ::testing::ValuesIn(kRendererTypesGpu)); -#endif // !defined(GL_NOT_ON_PLATFORM) || defined(ENABLE_CC_VULKAN_TESTS) TEST_P(LayerTreeHostSynchronousPixelTest, OneContentLayerZeroCopy) { use_zero_copy_ = true; diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_tiles.cc b/chromium/cc/trees/layer_tree_host_pixeltest_tiles.cc index 13ca041f5cd..9464beb4941 100644 --- a/chromium/cc/trees/layer_tree_host_pixeltest_tiles.cc +++ b/chromium/cc/trees/layer_tree_host_pixeltest_tiles.cc @@ -181,12 +181,10 @@ class LayerTreeHostTilesTestPartialInvalidation std::vector<TilesTestConfig> const kTestCases = { {LayerTreeTest::RENDERER_SOFTWARE, BITMAP}, -#if !defined(GL_NOT_ON_PLATFORM) {LayerTreeTest::RENDERER_GL, ONE_COPY}, {LayerTreeTest::RENDERER_GL, GPU}, {LayerTreeTest::RENDERER_SKIA_GL, ONE_COPY}, {LayerTreeTest::RENDERER_SKIA_GL, GPU}, -#endif // !defined(GL_NOT_ON_PLATFORM) #if defined(ENABLE_CC_VULKAN_TESTS) {LayerTreeTest::RENDERER_SKIA_VK, ONE_COPY}, {LayerTreeTest::RENDERER_SKIA_VK, GPU}, @@ -211,10 +209,8 @@ TEST_P(LayerTreeHostTilesTestPartialInvalidation, FullRaster) { } std::vector<TilesTestConfig> const kTestCasesMultiThread = { -#if !defined(GL_NOT_ON_PLATFORM) {LayerTreeTest::RENDERER_GL, ONE_COPY}, {LayerTreeTest::RENDERER_SKIA_GL, ONE_COPY}, -#endif // !defined(GL_NOT_ON_PLATFORM) #if defined(ENABLE_CC_VULKAN_TESTS) {LayerTreeTest::RENDERER_SKIA_VK, ONE_COPY}, #endif // defined(ENABLE_CC_VULKAN_TESTS) @@ -255,14 +251,12 @@ using LayerTreeHostTilesTestPartialInvalidationLowBitDepth = // This test doesn't work on Vulkan because on our hardware we can't render to // RGBA4444 format using either SwiftShader or native Vulkan. See // crbug.com/987278 for details -#if !defined(GL_NOT_ON_PLATFORM) INSTANTIATE_TEST_SUITE_P( All, LayerTreeHostTilesTestPartialInvalidationLowBitDepth, ::testing::Values( TilesTestConfig{LayerTreeTest::RENDERER_SKIA_GL, GPU_LOW_BIT_DEPTH}, TilesTestConfig{LayerTreeTest::RENDERER_GL, GPU_LOW_BIT_DEPTH})); -#endif // !defined(GL_NOT_ON_PLATFORM) TEST_P(LayerTreeHostTilesTestPartialInvalidationLowBitDepth, PartialRaster) { use_partial_raster_ = true; diff --git a/chromium/cc/trees/layer_tree_host_unittest.cc b/chromium/cc/trees/layer_tree_host_unittest.cc index bc6cc793d87..edbee8ea336 100644 --- a/chromium/cc/trees/layer_tree_host_unittest.cc +++ b/chromium/cc/trees/layer_tree_host_unittest.cc @@ -20,6 +20,7 @@ #include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" #include "build/build_config.h" +#include "cc/animation/animation_host.h" #include "cc/animation/timing_function.h" #include "cc/input/scroll_elasticity_helper.h" #include "cc/layers/content_layer_client.h" @@ -874,6 +875,99 @@ class LayerTreeHostTestPushPropertiesTo : public LayerTreeHostTest { SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestPushPropertiesTo); +// Verify that invisible render passes are excluded in CompositorFrame. +class LayerTreeHostTestInvisibleLayersSkipRenderPass + : public LayerTreeHostTest { + protected: + enum Step { + kAllInvisible, + kOneVisible, + kAllVisible, + kAllInvisibleAgain, + kDone, + }; + + void SetupTree() override { + SetInitialRootBounds(gfx::Size(10, 10)); + LayerTreeHostTest::SetupTree(); + auto* root = layer_tree_host()->root_layer(); + child1_ = CreateChild(root); + child2_ = CreateChild(root); + } + + scoped_refptr<Layer> CreateChild(scoped_refptr<Layer> root) { + auto child = Layer::Create(); + // Initially hidden. + child->SetHideLayerAndSubtree(true); + AddBackgroundBlurFilter(child.get()); + root->AddChild(child.get()); + return child; + } + + void AddBackgroundBlurFilter(Layer* layer) { + FilterOperations filters; + filters.Append(FilterOperation::CreateBlurFilter( + 30, SkBlurImageFilter::kClamp_TileMode)); + layer->SetBackdropFilters(filters); + } + + void BeginTest() override { + index_ = kAllInvisible; + PostSetNeedsCommitToMainThread(); + } + + void DidCommitAndDrawFrame() override { + ++index_; + switch (index_) { + case kAllInvisible: + NOTREACHED(); + break; + case kOneVisible: + child1_->SetHideLayerAndSubtree(false); + break; + case kAllVisible: + child2_->SetHideLayerAndSubtree(false); + break; + case kAllInvisibleAgain: + child1_->SetHideLayerAndSubtree(true); + child2_->SetHideLayerAndSubtree(true); + break; + case kDone: + EndTest(); + break; + } + } + + void DisplayReceivedCompositorFrameOnThread( + const viz::CompositorFrame& frame) override { + size_t num_render_passes = frame.render_pass_list.size(); + switch (index_) { + case kAllInvisible: + // There is only a root render pass. + EXPECT_EQ(1u, num_render_passes); + break; + case kOneVisible: + EXPECT_EQ(2u, num_render_passes); + break; + case kAllVisible: + EXPECT_EQ(3u, num_render_passes); + break; + case kAllInvisibleAgain: + EXPECT_EQ(1u, num_render_passes); + break; + case kDone: + EndTest(); + break; + } + } + + int index_ = kAllInvisible; + scoped_refptr<Layer> child1_; + scoped_refptr<Layer> child2_; +}; + +SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestInvisibleLayersSkipRenderPass); + class LayerTreeHostTestPushNodeOwnerToNodeIdMap : public LayerTreeHostTest { protected: void SetupTree() override { @@ -1107,7 +1201,7 @@ class LayerTreeHostTestSurfaceDamage : public LayerTreeHostTest { root_->SetBounds(gfx::Size(20, 20)); break; case 3: - child_->SetDoubleSided(false); + child_->SetOpacity(0.8f); break; } } @@ -3883,12 +3977,12 @@ class LayerTreeHostTestLCDChange : public LayerTreeHostTest { PostSetNeedsCommitToMainThread(); break; case 2: - // Change layer opacity that should trigger lcd change. - layer_tree_host()->root_layer()->SetOpacity(.5f); + // Change contents_opaque that should trigger lcd change. + layer_tree_host()->root_layer()->SetContentsOpaque(false); break; case 3: - // Change layer opacity that should not trigger lcd change. - layer_tree_host()->root_layer()->SetOpacity(1.f); + // Change contents_opaque that should trigger lcd change. + layer_tree_host()->root_layer()->SetContentsOpaque(true); break; case 4: EndTest(); @@ -3905,7 +3999,8 @@ class LayerTreeHostTestLCDChange : public LayerTreeHostTest { PictureLayerImpl* root_layer = static_cast<PictureLayerImpl*>(host_impl->active_tree()->root_layer()); bool can_use_lcd_text = - host_impl->active_tree()->root_layer()->CanUseLCDText(); + root_layer->ComputeLCDTextDisallowedReasonForTesting() == + LCDTextDisallowedReason::kNone; switch (host_impl->active_tree()->source_frame_number()) { case 0: // The first draw. @@ -8782,5 +8877,59 @@ class LayerTreeHostTopControlsDeltaTriggersViewportUpdate MULTI_THREAD_TEST_F(LayerTreeHostTopControlsDeltaTriggersViewportUpdate); +// Tests that custom sequence throughput tracking result is reported to +// LayerTreeHostClient. +constexpr MutatorHost::TrackedAnimationSequenceId kSequenceId = 1u; +class LayerTreeHostCustomThrougputTrackerTest : public LayerTreeHostTest { + public: + void BeginTest() override { PostSetNeedsCommitToMainThread(); } + + void DidCommit() override { + // FrameSequenceTracker typically sees the following sequence: + // e(2,2)b(3)B(0,3)E(3)s(3)S(3)e(3,3)P(3)b(4)B(3,4)E(4)s(4)S(4)e(4,4)P(4) + switch (layer_tree_host()->SourceFrameNumber()) { + case 1: + animation_host()->StartThroughputTracking(kSequenceId); + break; + case 3: + animation_host()->StopThroughputTracking(kSequenceId); + break; + default: + break; + } + + PostSetNeedsCommitWithForcedRedrawToMainThread(); + } + + void NotifyThroughputTrackerResults(CustomTrackerResults results) override { + ASSERT_TRUE(base::Contains(results, kSequenceId)); + const auto& throughput = results[kSequenceId]; + // Frame 3 and 4 are counted. See the sequence in DidCommit comment for + // normal case that expects 2 for both frames_expected and frames_produced. + // + // However, on slow bots, things could be different. + // - Begin frame could be skipped but still counted as expected frames, + // + // e(5,5)b(8)B(0,8)E(8)s(3)S(8)e(8,8)b(11) + // B(8,11)E(11)ts(4)S(11)e(11,11)P(3)e(14,14)P(4) + // + // B(0, 8) and B(8, 11) make frame_expected to be 4, more than 2 expected + // by test. + // + // - Finish before frame 4 is presented in multi-thread mode. + // + // e(3,3)b(4)B(0,4)E(4)s(2)e(4,4)b(6)B(4,6)E(6)s(3)S(4)e(6,6) + // P(2)e(7,7)P(3) + // + // Only P(3) is counted thus frames_produced is 1. + EXPECT_GE(throughput.frames_expected, 2u); + EXPECT_GE(throughput.frames_produced, 1u); + + EndTest(); + } +}; + +SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostCustomThrougputTrackerTest); + } // 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 80fc33f5398..a5f7b6e522a 100644 --- a/chromium/cc/trees/layer_tree_host_unittest_scroll.cc +++ b/chromium/cc/trees/layer_tree_host_unittest_scroll.cc @@ -14,7 +14,6 @@ #include "cc/base/completion_event.h" #include "cc/input/main_thread_scrolling_reason.h" #include "cc/input/scroll_elasticity_helper.h" -#include "cc/input/scroll_input_type.h" #include "cc/layers/layer.h" #include "cc/layers/layer_impl.h" #include "cc/layers/picture_layer.h" @@ -33,6 +32,7 @@ #include "cc/trees/transform_node.h" #include "components/viz/common/frame_sinks/begin_frame_source.h" #include "testing/gmock/include/gmock/gmock.h" +#include "ui/events/types/scroll_input_type.h" #include "ui/gfx/geometry/point_conversions.h" #include "ui/gfx/geometry/size_conversions.h" #include "ui/gfx/geometry/vector2d_conversions.h" @@ -686,7 +686,7 @@ class LayerTreeHostScrollTestCaseWithChild : public LayerTreeHostScrollTest { gfx::PointF(-0.5f, -0.5f) + GetTransformNode(expected_scroll_layer_impl)->post_translation); InputHandler::ScrollStatus status = impl->ScrollBegin( - BeginState(scroll_point).get(), ScrollInputType::kTouchscreen); + BeginState(scroll_point).get(), ui::ScrollInputType::kTouchscreen); EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread); impl->ScrollUpdate(UpdateState(gfx::Point(), scroll_amount_).get()); auto* scrolling_node = impl->CurrentlyScrollingNode(); @@ -709,7 +709,7 @@ class LayerTreeHostScrollTestCaseWithChild : public LayerTreeHostScrollTest { gfx::PointF(0.5f, 0.5f) + GetTransformNode(expected_scroll_layer_impl)->post_translation); InputHandler::ScrollStatus status = impl->ScrollBegin( - BeginState(scroll_point).get(), ScrollInputType::kWheel); + BeginState(scroll_point).get(), ui::ScrollInputType::kWheel); EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread); impl->ScrollUpdate(UpdateState(gfx::Point(), scroll_amount_).get()); impl->ScrollEnd(); @@ -1068,8 +1068,8 @@ void DoGestureScroll(LayerTreeHostImpl* host_impl, begin_scroll_state_data.delta_y_hint = offset.y(); std::unique_ptr<ScrollState> begin_scroll_state( new ScrollState(begin_scroll_state_data)); - auto scroll_status = host_impl->ScrollBegin(begin_scroll_state.get(), - ScrollInputType::kTouchscreen); + auto scroll_status = host_impl->ScrollBegin( + begin_scroll_state.get(), ui::ScrollInputType::kTouchscreen); EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, scroll_status.thread); auto* scrolling_node = host_impl->CurrentlyScrollingNode(); EXPECT_TRUE(scrolling_node); @@ -1453,13 +1453,13 @@ class LayerTreeHostScrollTestScrollNonDrawnLayer // checking whether the screen space point is inside the non-fast // scrollable region. InputHandler::ScrollStatus status = impl->ScrollBegin( - BeginState(gfx::Point(0, 0)).get(), ScrollInputType::kTouchscreen); + BeginState(gfx::Point(0, 0)).get(), ui::ScrollInputType::kTouchscreen); EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread); EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion, status.main_thread_scrolling_reasons); status = impl->ScrollBegin(BeginState(gfx::Point(21, 21)).get(), - ScrollInputType::kTouchscreen); + ui::ScrollInputType::kTouchscreen); EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread); EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain, status.main_thread_scrolling_reasons); @@ -2461,16 +2461,18 @@ class NonScrollingNonFastScrollableRegion : public LayerTreeHostScrollTest { // The top-left hit should immediately hit the top layer's non-fast region // which forces main-thread scrolling. - auto top_left_status = impl->ScrollBegin( - BeginState(gfx::Point(20, 20)).get(), ScrollInputType::kTouchscreen); + auto top_left_status = + impl->ScrollBegin(BeginState(gfx::Point(20, 20)).get(), + ui::ScrollInputType::kTouchscreen); EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, top_left_status.thread); EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion, top_left_status.main_thread_scrolling_reasons); // The top-right hit should hit the top layer but not the non-fast region so // the scroll should continue to scroll on the impl. - InputHandler::ScrollStatus top_right_status = impl->ScrollBegin( - BeginState(gfx::Point(80, 20)).get(), ScrollInputType::kTouchscreen); + InputHandler::ScrollStatus top_right_status = + impl->ScrollBegin(BeginState(gfx::Point(80, 20)).get(), + ui::ScrollInputType::kTouchscreen); EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, top_right_status.thread); EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain, top_right_status.main_thread_scrolling_reasons); @@ -2480,8 +2482,9 @@ class NonScrollingNonFastScrollableRegion : public LayerTreeHostScrollTest { // the middle layer is a composited scroller and is hit first, we cannot do // a fast scroll because an ancestor on the scroll chain has hit a non-fast // region. - InputHandler::ScrollStatus bottom_right_status = impl->ScrollBegin( - BeginState(gfx::Point(80, 80)).get(), ScrollInputType::kTouchscreen); + InputHandler::ScrollStatus bottom_right_status = + impl->ScrollBegin(BeginState(gfx::Point(80, 80)).get(), + ui::ScrollInputType::kTouchscreen); EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, bottom_right_status.thread); EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion, bottom_right_status.main_thread_scrolling_reasons); diff --git a/chromium/cc/trees/layer_tree_impl.cc b/chromium/cc/trees/layer_tree_impl.cc index 45f7e5707a0..600c987976c 100644 --- a/chromium/cc/trees/layer_tree_impl.cc +++ b/chromium/cc/trees/layer_tree_impl.cc @@ -1585,6 +1585,10 @@ FrameRateCounter* LayerTreeImpl::frame_rate_counter() const { return host_impl_->fps_counter(); } +base::Optional<int> LayerTreeImpl::current_universal_throughput() { + return host_impl_->current_universal_throughput(); +} + MemoryHistory* LayerTreeImpl::memory_history() const { return host_impl_->memory_history(); } @@ -1627,7 +1631,7 @@ bool LayerTreeImpl::PinchGestureActive() const { return host_impl_->pinch_gesture_active(); } -viz::BeginFrameArgs LayerTreeImpl::CurrentBeginFrameArgs() const { +const viz::BeginFrameArgs& LayerTreeImpl::CurrentBeginFrameArgs() const { return host_impl_->CurrentBeginFrameArgs(); } @@ -1945,9 +1949,9 @@ ScrollbarSet LayerTreeImpl::ScrollbarsFor(ElementId scroll_element_id) const { if (it != element_id_to_scrollbar_layer_ids_.end()) { const ScrollbarLayerIds& layer_ids = it->second; if (layer_ids.horizontal != Layer::INVALID_ID) - scrollbars.insert(LayerById(layer_ids.horizontal)->ToScrollbarLayer()); + scrollbars.insert(ToScrollbarLayer(LayerById(layer_ids.horizontal))); if (layer_ids.vertical != Layer::INVALID_ID) - scrollbars.insert(LayerById(layer_ids.vertical)->ToScrollbarLayer()); + scrollbars.insert(ToScrollbarLayer(LayerById(layer_ids.vertical))); } return scrollbars; } @@ -2158,7 +2162,7 @@ LayerImpl* LayerTreeImpl::FindFirstScrollingLayerOrScrollbarThatIsHitByPoint( auto HitTestScrollingLayerOrScrollbarFunctor = [this](const LayerImpl* layer) { return layer->HitTestable() && - (layer->is_scrollbar() || + (layer->IsScrollbarLayer() || (property_trees()->scroll_tree.FindNodeFromElementId( layer->element_id()))); }; diff --git a/chromium/cc/trees/layer_tree_impl.h b/chromium/cc/trees/layer_tree_impl.h index f87d0c3f5a5..5c2f586a0ea 100644 --- a/chromium/cc/trees/layer_tree_impl.h +++ b/chromium/cc/trees/layer_tree_impl.h @@ -128,6 +128,7 @@ class CC_EXPORT LayerTreeImpl { ImageDecodeCache* image_decode_cache() const; ImageAnimationController* image_animation_controller() const; FrameRateCounter* frame_rate_counter() const; + base::Optional<int> current_universal_throughput(); MemoryHistory* memory_history() const; DebugRectHistory* debug_rect_history() const; bool IsActiveTree() const; @@ -137,7 +138,7 @@ class CC_EXPORT LayerTreeImpl { LayerImpl* FindActiveTreeLayerById(int id); LayerImpl* FindPendingTreeLayerById(int id); bool PinchGestureActive() const; - viz::BeginFrameArgs CurrentBeginFrameArgs() const; + const viz::BeginFrameArgs& CurrentBeginFrameArgs() const; base::TimeDelta CurrentBeginFrameInterval() const; const gfx::Rect ViewportRectForTilePriority() const; std::unique_ptr<ScrollbarAnimationController> diff --git a/chromium/cc/trees/managed_memory_policy.cc b/chromium/cc/trees/managed_memory_policy.cc index 3583c8b0d2e..a8b7d9c5d3e 100644 --- a/chromium/cc/trees/managed_memory_policy.cc +++ b/chromium/cc/trees/managed_memory_policy.cc @@ -6,7 +6,7 @@ #include <stddef.h> -#include "base/logging.h" +#include "base/notreached.h" namespace cc { diff --git a/chromium/cc/trees/mutator_host.h b/chromium/cc/trees/mutator_host.h index e9c2d537c18..1292e0ec826 100644 --- a/chromium/cc/trees/mutator_host.h +++ b/chromium/cc/trees/mutator_host.h @@ -6,6 +6,7 @@ #define CC_TREES_MUTATOR_HOST_H_ #include <memory> +#include <vector> #include "base/callback_forward.h" #include "base/time/time.h" @@ -39,7 +40,7 @@ const float kNotScaled = 0; // MutatorHostClient interface. class MutatorHost { public: - virtual ~MutatorHost() {} + virtual ~MutatorHost() = default; virtual std::unique_ptr<MutatorHost> CreateImplInstance( bool supports_impl_scrolling) const = 0; @@ -164,11 +165,24 @@ class MutatorHost { virtual bool HasCustomPropertyAnimations() const = 0; virtual bool CurrentFrameHadRAF() const = 0; virtual bool NextFrameHasPendingRAF() const = 0; + + using TrackedAnimationSequenceId = size_t; + struct PendingThroughputTrackerInfo { + // Id of a tracked animation sequence. + TrackedAnimationSequenceId id = 0u; + // True means the tracking for |id| is pending to start and false means + // the tracking is pending to stop. + bool start = false; + }; + // Takes info of throughput trackers that are pending start or stop. + using PendingThroughputTrackerInfos = + std::vector<PendingThroughputTrackerInfo>; + virtual PendingThroughputTrackerInfos TakePendingThroughputTrackerInfos() = 0; }; class MutatorEvents { public: - virtual ~MutatorEvents() {} + virtual ~MutatorEvents() = default; virtual bool IsEmpty() const = 0; }; diff --git a/chromium/cc/trees/occlusion.cc b/chromium/cc/trees/occlusion.cc index d0f5f1f5525..7edb53303b6 100644 --- a/chromium/cc/trees/occlusion.cc +++ b/chromium/cc/trees/occlusion.cc @@ -4,6 +4,7 @@ #include "cc/trees/occlusion.h" +#include "base/check_op.h" #include "cc/base/math_util.h" #include "ui/gfx/geometry/rect.h" diff --git a/chromium/cc/trees/occlusion_tracker.cc b/chromium/cc/trees/occlusion_tracker.cc index ad2da282dcc..7a6bf2068f6 100644 --- a/chromium/cc/trees/occlusion_tracker.cc +++ b/chromium/cc/trees/occlusion_tracker.cc @@ -179,6 +179,12 @@ void OcclusionTracker::EnterRenderTarget( gfx::Rect(), old_target_to_new_target_transform)); } +// A blend mode is occluding if a fully opaque source can fully occlude the +// destination and the result is also fully opaque. +static bool IsOccludingBlendMode(SkBlendMode blend_mode) { + return blend_mode == SkBlendMode::kSrc || blend_mode == SkBlendMode::kSrcOver; +} + void OcclusionTracker::FinishedRenderTarget( const RenderSurfaceImpl* finished_target_surface) { // Make sure we know about the target surface. @@ -198,7 +204,7 @@ void OcclusionTracker::FinishedRenderTarget( // the surface's subtree, then clear the occlusion here so it won't be used. if (finished_target_surface->HasMaskingContributingSurface() || finished_target_surface->draw_opacity() < 1 || - !finished_target_surface->UsesDefaultBlendMode() || + !IsOccludingBlendMode(finished_target_surface->BlendMode()) || target_is_only_for_copy_request_or_force_render_surface || finished_target_surface->Filters().HasFilterThatAffectsOpacity()) { stack_.back().occlusion_from_outside_target.Clear(); @@ -351,7 +357,7 @@ void OcclusionTracker::MarkOccludedBehindLayer(const LayerImpl* layer) { if (opaque_layer_region.IsEmpty()) return; - // If the blend mode is not kSrcOver and the effect doesn't have a render + // If the blend mode is not occluding and the effect doesn't have a render // surface, then the layer should not occlude. An example of this would // otherwise be wrong is that this layer is a non-render-surface mask layer // with kDstIn blend mode. @@ -359,7 +365,7 @@ void OcclusionTracker::MarkOccludedBehindLayer(const LayerImpl* layer) { layer->layer_tree_impl()->property_trees()->effect_tree.Node( layer->effect_tree_index()); if (!effect_node->HasRenderSurface() && - effect_node->blend_mode != SkBlendMode::kSrcOver) + !IsOccludingBlendMode(effect_node->blend_mode)) return; DCHECK(layer->visible_layer_rect().Contains(opaque_layer_region.bounds())); diff --git a/chromium/cc/trees/occlusion_tracker_unittest.cc b/chromium/cc/trees/occlusion_tracker_unittest.cc index 5ab0b6552fc..920c746638d 100644 --- a/chromium/cc/trees/occlusion_tracker_unittest.cc +++ b/chromium/cc/trees/occlusion_tracker_unittest.cc @@ -1830,10 +1830,82 @@ class OcclusionTrackerTestReduceOcclusionWhenBkgdFilterIsPartiallyOccluded ALL_OCCLUSIONTRACKER_TEST( OcclusionTrackerTestReduceOcclusionWhenBkgdFilterIsPartiallyOccluded) -class OcclusionTrackerTestRenderSurfaceBlendModeDoesNotOcclude +class OcclusionTrackerTestRenderSurfaceOccludingBlendMode : public OcclusionTrackerTest { protected: - explicit OcclusionTrackerTestRenderSurfaceBlendModeDoesNotOcclude( + explicit OcclusionTrackerTestRenderSurfaceOccludingBlendMode( + bool opaque_layers, + SkBlendMode blend_mode) + : OcclusionTrackerTest(opaque_layers), blend_mode_(blend_mode) {} + + void RunMyTest() override { + TestContentLayerImpl* parent = CreateRoot(gfx::Size(100, 100)); + LayerImpl* blend_mode_layer = + CreateDrawingSurface(parent, identity_matrix, gfx::PointF(0.f, 0.f), + gfx::Size(100, 100), true); + LayerImpl* top_layer = + CreateDrawingLayer(parent, identity_matrix, gfx::PointF(10.f, 12.f), + gfx::Size(20, 22), true); + + GetEffectNode(blend_mode_layer)->render_surface_reason = + RenderSurfaceReason::kTest; + GetEffectNode(blend_mode_layer)->blend_mode = blend_mode_; + + this->CalcDrawEtc(); + + TestOcclusionTrackerWithClip occlusion(gfx::Rect(0, 0, 1000, 1000)); + + ASSERT_NO_FATAL_FAILURE(VisitLayer(top_layer, &occlusion)); + // |top_layer| occludes. + EXPECT_EQ(gfx::Rect(10, 12, 20, 22).ToString(), + occlusion.occlusion_from_inside_target().ToString()); + EXPECT_TRUE(occlusion.occlusion_from_outside_target().IsEmpty()); + + ASSERT_NO_FATAL_FAILURE(VisitLayer(blend_mode_layer, &occlusion)); + // |top_layer| and |blend_mode_layer| both occlude. + EXPECT_EQ(gfx::Rect(100, 100).ToString(), + occlusion.occlusion_from_inside_target().ToString()); + EXPECT_EQ(gfx::Rect(10, 12, 20, 22).ToString(), + occlusion.occlusion_from_outside_target().ToString()); + + ASSERT_NO_FATAL_FAILURE( + this->VisitContributingSurface(blend_mode_layer, &occlusion)); + // |top_layer| and |blend_mode_layer| still both occlude. + EXPECT_EQ(gfx::Rect(100, 100).ToString(), + occlusion.occlusion_from_inside_target().ToString()); + EXPECT_TRUE(occlusion.occlusion_from_outside_target().IsEmpty()); + } + + private: + SkBlendMode blend_mode_; +}; + +class OcclusionTrackerTestRenderSurfaceBlendModeSrcOver + : public OcclusionTrackerTestRenderSurfaceOccludingBlendMode { + protected: + explicit OcclusionTrackerTestRenderSurfaceBlendModeSrcOver(bool opaque_layers) + : OcclusionTrackerTestRenderSurfaceOccludingBlendMode( + opaque_layers, + SkBlendMode::kSrcOver) {} +}; + +ALL_OCCLUSIONTRACKER_TEST(OcclusionTrackerTestRenderSurfaceBlendModeSrcOver) + +class OcclusionTrackerTestRenderSurfaceBlendModeSrc + : public OcclusionTrackerTestRenderSurfaceOccludingBlendMode { + protected: + explicit OcclusionTrackerTestRenderSurfaceBlendModeSrc(bool opaque_layers) + : OcclusionTrackerTestRenderSurfaceOccludingBlendMode(opaque_layers, + SkBlendMode::kSrc) { + } +}; + +ALL_OCCLUSIONTRACKER_TEST(OcclusionTrackerTestRenderSurfaceBlendModeSrc) + +class OcclusionTrackerTestRenderSurfaceNonOccludingBlendMode + : public OcclusionTrackerTest { + protected: + explicit OcclusionTrackerTestRenderSurfaceNonOccludingBlendMode( bool opaque_layers) : OcclusionTrackerTest(opaque_layers) {} void RunMyTest() override { @@ -1877,12 +1949,16 @@ class OcclusionTrackerTestRenderSurfaceBlendModeDoesNotOcclude }; ALL_OCCLUSIONTRACKER_TEST( - OcclusionTrackerTestRenderSurfaceBlendModeDoesNotOcclude) + OcclusionTrackerTestRenderSurfaceNonOccludingBlendMode) + +// No OcclusionTrackerTestNonRenderSurfaceOccludingBlendMode because kSrcOver is +// default and is tested in many other tests, and kSrc always creates a render +// surface. -class OcclusionTrackerTestNonRenderSurfaceBlendModeDoesNotOcclude +class OcclusionTrackerTestNonRenderSurfaceNonOccludingBlendMode : public OcclusionTrackerTest { protected: - explicit OcclusionTrackerTestNonRenderSurfaceBlendModeDoesNotOcclude( + explicit OcclusionTrackerTestNonRenderSurfaceNonOccludingBlendMode( bool opaque_layers) : OcclusionTrackerTest(opaque_layers) {} void RunMyTest() override { @@ -1908,7 +1984,7 @@ class OcclusionTrackerTestNonRenderSurfaceBlendModeDoesNotOcclude }; ALL_OCCLUSIONTRACKER_TEST( - OcclusionTrackerTestNonRenderSurfaceBlendModeDoesNotOcclude) + OcclusionTrackerTestNonRenderSurfaceNonOccludingBlendMode) class OcclusionTrackerTestMinimumTrackingSize : public OcclusionTrackerTest { protected: diff --git a/chromium/cc/trees/property_animation_state.cc b/chromium/cc/trees/property_animation_state.cc index b82efc75ec4..6c785231ac0 100644 --- a/chromium/cc/trees/property_animation_state.cc +++ b/chromium/cc/trees/property_animation_state.cc @@ -4,7 +4,6 @@ #include "cc/trees/property_animation_state.h" -#include "base/logging.h" namespace cc { diff --git a/chromium/cc/trees/property_tree.cc b/chromium/cc/trees/property_tree.cc index 63eed878cb5..cbdb9d43e73 100644 --- a/chromium/cc/trees/property_tree.cc +++ b/chromium/cc/trees/property_tree.cc @@ -7,7 +7,7 @@ #include <set> #include <vector> -#include "base/logging.h" +#include "base/check_op.h" #include "base/memory/ptr_util.h" #include "base/numerics/checked_math.h" #include "base/trace_event/traced_value.h" @@ -1202,6 +1202,10 @@ const ScrollNode* ScrollTree::FindNodeFromElementId(ElementId id) const { return Node(iterator->second); } +bool ScrollTree::IsComposited(const ScrollNode& node) const { + return node.is_composited; +} + void ScrollTree::clear() { PropertyTree<ScrollNode>::clear(); @@ -1387,8 +1391,14 @@ const gfx::ScrollOffset ScrollTree::GetPixelSnappedScrollOffset( if (transform_node->needs_local_transform_update) property_trees()->transform_tree.UpdateTransforms(transform_node->id); - offset.set_x(offset.x() - transform_node->snap_amount.x()); - offset.set_y(offset.y() - transform_node->snap_amount.y()); + // The calculated pixel snap amount can be slightly larger than the actual + // snapping needed, due to floating point precision errors. In general this + // is fine, but we never want to report a negative scroll offset so avoid + // that case here. + // TODO(crbug.com/1076878): Remove the clamping when scroll timeline effects + // always match the snapping. + offset = ClampScrollOffsetToLimits( + offset - gfx::ScrollOffset(transform_node->snap_amount), *scroll_node); } return offset; diff --git a/chromium/cc/trees/property_tree.h b/chromium/cc/trees/property_tree.h index add08e3b897..4dcf7d7a0bd 100644 --- a/chromium/cc/trees/property_tree.h +++ b/chromium/cc/trees/property_tree.h @@ -489,6 +489,11 @@ class CC_EXPORT ScrollTree final : public PropertyTree<ScrollNode> { void NotifyDidChangeScrollbarsHidden(ElementId scroll_element_id, bool hidden); + // A composited scroll node is a scroll node that has an associated composited + // layer, otherwise the scroll node corresponds to a scroller that requires + // repainting. + bool IsComposited(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 6350605baca..b036be21cbd 100644 --- a/chromium/cc/trees/property_tree_builder.cc +++ b/chromium/cc/trees/property_tree_builder.cc @@ -470,7 +470,6 @@ bool PropertyTreeBuilderContext::AddEffectNodeIfNeeded( node->trilinear_filtering = layer->trilinear_filtering(); node->has_potential_opacity_animation = has_potential_opacity_animation; node->has_potential_filter_animation = has_potential_filter_animation; - node->double_sided = layer->double_sided(); node->subtree_hidden = layer->hide_layer_and_subtree(); node->is_currently_animating_opacity = OpacityIsAnimating(mutator_host_, layer); @@ -633,15 +632,6 @@ void PropertyTreeBuilderContext::AddScrollNodeIfNeeded( layer->SetScrollTreeIndex(node_id); } -void SetBackfaceVisibilityTransform(Layer* layer, bool created_transform_node) { - // A double-sided layer's backface can been shown when its visible. - // In addition, we need to check if (1) there might be a local 3D transform - // on the layer that might turn it to the backface, or (2) it is not drawn - // into a flattened space. - layer->SetShouldCheckBackfaceVisibility(!layer->double_sided() && - created_transform_node); -} - void SetSafeOpaqueBackgroundColor(const DataForRecursion& data_from_ancestor, Layer* layer, DataForRecursion* data_for_children) { @@ -673,7 +663,6 @@ void PropertyTreeBuilderContext::BuildPropertyTreesInternal( AddScrollNodeIfNeeded(data_from_parent, layer, &data_for_children); - SetBackfaceVisibilityTransform(layer, created_transform_node); SetSafeOpaqueBackgroundColor(data_from_parent, layer, &data_for_children); bool not_axis_aligned_since_last_clip = diff --git a/chromium/cc/trees/property_tree_builder_unittest.cc b/chromium/cc/trees/property_tree_builder_unittest.cc index f46440c6793..af760bc828a 100644 --- a/chromium/cc/trees/property_tree_builder_unittest.cc +++ b/chromium/cc/trees/property_tree_builder_unittest.cc @@ -453,185 +453,6 @@ TEST_F(PropertyTreeBuilderTest, TextureLayerSnapping) { EXPECT_EQ(layer_bounds_in_screen_space, gfx::RectF(11.f, 20.f, 100.f, 100.f)); } -// Verify the behavior of back-face culling when there are no preserve-3d -// layers. Note that 3d transforms still apply in this case, but they are -// "flattened" to each parent layer according to current W3C spec. -TEST_F(PropertyTreeBuilderTest, BackFaceCullingWithoutPreserves3d) { - auto root = Layer::Create(); - host()->SetRootLayer(root); - auto front_facing_child = Layer::Create(); - root->AddChild(front_facing_child); - auto back_facing_child = Layer::Create(); - root->AddChild(back_facing_child); - auto front_facing_surface = Layer::Create(); - root->AddChild(front_facing_surface); - auto back_facing_surface = Layer::Create(); - root->AddChild(back_facing_surface); - auto front_facing_child_of_front_facing_surface = Layer::Create(); - front_facing_surface->AddChild(front_facing_child_of_front_facing_surface); - auto back_facing_child_of_front_facing_surface = Layer::Create(); - front_facing_surface->AddChild(back_facing_child_of_front_facing_surface); - auto front_facing_child_of_back_facing_surface = Layer::Create(); - back_facing_surface->AddChild(front_facing_child_of_back_facing_surface); - auto back_facing_child_of_back_facing_surface = Layer::Create(); - back_facing_surface->AddChild(back_facing_child_of_back_facing_surface); - - // Nothing is double-sided - front_facing_child->SetDoubleSided(false); - back_facing_child->SetDoubleSided(false); - front_facing_surface->SetDoubleSided(false); - back_facing_surface->SetDoubleSided(false); - front_facing_child_of_front_facing_surface->SetDoubleSided(false); - back_facing_child_of_front_facing_surface->SetDoubleSided(false); - front_facing_child_of_back_facing_surface->SetDoubleSided(false); - back_facing_child_of_back_facing_surface->SetDoubleSided(false); - - // Everything draws content. - front_facing_child->SetIsDrawable(true); - back_facing_child->SetIsDrawable(true); - front_facing_surface->SetIsDrawable(true); - back_facing_surface->SetIsDrawable(true); - front_facing_child_of_front_facing_surface->SetIsDrawable(true); - back_facing_child_of_front_facing_surface->SetIsDrawable(true); - front_facing_child_of_back_facing_surface->SetIsDrawable(true); - back_facing_child_of_back_facing_surface->SetIsDrawable(true); - - gfx::Transform backface_matrix; - backface_matrix.Translate(50.0, 50.0); - backface_matrix.RotateAboutYAxis(180.0); - backface_matrix.Translate(-50.0, -50.0); - - root->SetBounds(gfx::Size(100, 100)); - front_facing_child->SetBounds(gfx::Size(100, 100)); - back_facing_child->SetBounds(gfx::Size(100, 100)); - front_facing_surface->SetBounds(gfx::Size(100, 100)); - back_facing_surface->SetBounds(gfx::Size(100, 100)); - front_facing_child_of_front_facing_surface->SetBounds(gfx::Size(100, 100)); - back_facing_child_of_front_facing_surface->SetBounds(gfx::Size(100, 100)); - front_facing_child_of_back_facing_surface->SetBounds(gfx::Size(100, 100)); - back_facing_child_of_back_facing_surface->SetBounds(gfx::Size(100, 100)); - - front_facing_surface->SetForceRenderSurfaceForTesting(true); - back_facing_surface->SetForceRenderSurfaceForTesting(true); - - back_facing_child->SetTransform(backface_matrix); - back_facing_surface->SetTransform(backface_matrix); - back_facing_child_of_front_facing_surface->SetTransform(backface_matrix); - back_facing_child_of_back_facing_surface->SetTransform(backface_matrix); - - // Note: No layers preserve 3d. According to current W3C CSS gfx::Transforms - // spec, these layers should blindly use their own local transforms to - // determine back-face culling. - CommitAndActivate(); - - // Verify which render surfaces were created. - EXPECT_EQ(GetRenderSurfaceImpl(front_facing_child), - GetRenderSurfaceImpl(root)); - EXPECT_EQ(GetRenderSurfaceImpl(back_facing_child), - GetRenderSurfaceImpl(root)); - EXPECT_NE(GetRenderSurfaceImpl(front_facing_surface), - GetRenderSurfaceImpl(root)); - EXPECT_NE(GetRenderSurfaceImpl(back_facing_surface), - GetRenderSurfaceImpl(root)); - EXPECT_NE(GetRenderSurfaceImpl(back_facing_surface), - GetRenderSurfaceImpl(front_facing_surface)); - EXPECT_EQ(GetRenderSurfaceImpl(front_facing_child_of_front_facing_surface), - GetRenderSurfaceImpl(front_facing_surface)); - EXPECT_EQ(GetRenderSurfaceImpl(back_facing_child_of_front_facing_surface), - GetRenderSurfaceImpl(front_facing_surface)); - EXPECT_EQ(GetRenderSurfaceImpl(front_facing_child_of_back_facing_surface), - GetRenderSurfaceImpl(back_facing_surface)); - EXPECT_EQ(GetRenderSurfaceImpl(back_facing_child_of_back_facing_surface), - GetRenderSurfaceImpl(back_facing_surface)); - - EXPECT_EQ(3u, update_layer_impl_list().size()); - EXPECT_TRUE(UpdateLayerImplListContains(front_facing_child->id())); - EXPECT_TRUE(UpdateLayerImplListContains(front_facing_surface->id())); - EXPECT_TRUE(UpdateLayerImplListContains( - front_facing_child_of_front_facing_surface->id())); -} - -// Verify that layers are appropriately culled when their back face is showing -// and they are not double sided, while animations are going on. -// Even layers that are animating get culled if their back face is showing and -// they are not double sided. -TEST_F(PropertyTreeBuilderTest, BackFaceCullingWithAnimatingTransforms) { - auto root = Layer::Create(); - host()->SetRootLayer(root); - auto child = Layer::Create(); - root->AddChild(child); - auto animating_surface = Layer::Create(); - root->AddChild(animating_surface); - auto child_of_animating_surface = Layer::Create(); - animating_surface->AddChild(child_of_animating_surface); - auto animating_child = Layer::Create(); - root->AddChild(animating_child); - auto child2 = Layer::Create(); - root->AddChild(child2); - - // Nothing is double-sided - child->SetDoubleSided(false); - child2->SetDoubleSided(false); - animating_surface->SetDoubleSided(false); - child_of_animating_surface->SetDoubleSided(false); - animating_child->SetDoubleSided(false); - - // Everything draws content. - child->SetIsDrawable(true); - child2->SetIsDrawable(true); - animating_surface->SetIsDrawable(true); - child_of_animating_surface->SetIsDrawable(true); - animating_child->SetIsDrawable(true); - - gfx::Transform backface_matrix; - backface_matrix.Translate(50.0, 50.0); - backface_matrix.RotateAboutYAxis(180.0); - backface_matrix.Translate(-50.0, -50.0); - - host()->SetElementIdsForTesting(); - - // Animate the transform on the render surface. - AddAnimatedTransformToElementWithAnimation(animating_surface->element_id(), - timeline(), 10.0, 30, 0); - // This is just an animating layer, not a surface. - AddAnimatedTransformToElementWithAnimation(animating_child->element_id(), - timeline(), 10.0, 30, 0); - - root->SetBounds(gfx::Size(100, 100)); - child->SetBounds(gfx::Size(100, 100)); - child->SetTransform(backface_matrix); - animating_surface->SetBounds(gfx::Size(100, 100)); - animating_surface->SetTransform(backface_matrix); - animating_surface->SetForceRenderSurfaceForTesting(true); - child_of_animating_surface->SetBounds(gfx::Size(100, 100)); - child_of_animating_surface->SetTransform(backface_matrix); - animating_child->SetBounds(gfx::Size(100, 100)); - animating_child->SetTransform(backface_matrix); - child2->SetBounds(gfx::Size(100, 100)); - - CommitAndActivate(); - - EXPECT_EQ(GetRenderSurfaceImpl(child), GetRenderSurfaceImpl(root)); - EXPECT_TRUE(GetRenderSurfaceImpl(animating_surface)); - EXPECT_EQ(GetRenderSurfaceImpl(child_of_animating_surface), - GetRenderSurfaceImpl(animating_surface)); - EXPECT_EQ(GetRenderSurfaceImpl(animating_child), GetRenderSurfaceImpl(root)); - EXPECT_EQ(GetRenderSurfaceImpl(child2), GetRenderSurfaceImpl(root)); - - EXPECT_EQ(1u, update_layer_impl_list().size()); - - // The back facing layers are culled from the layer list, and have an empty - // visible rect. - EXPECT_TRUE(UpdateLayerImplListContains(child2->id())); - EXPECT_TRUE(ImplOf(child)->visible_layer_rect().IsEmpty()); - EXPECT_TRUE(ImplOf(animating_surface)->visible_layer_rect().IsEmpty()); - EXPECT_TRUE( - ImplOf(child_of_animating_surface)->visible_layer_rect().IsEmpty()); - EXPECT_TRUE(ImplOf(animating_child)->visible_layer_rect().IsEmpty()); - - EXPECT_EQ(gfx::Rect(100, 100), ImplOf(child2)->visible_layer_rect()); -} - // Verify that having animated opacity but current opacity 1 still creates // a render surface. TEST_F(PropertyTreeBuilderTest, AnimatedOpacityCreatesRenderSurface) { diff --git a/chromium/cc/trees/property_tree_unittest.cc b/chromium/cc/trees/property_tree_unittest.cc index 890e9032ba6..8ac81691db8 100644 --- a/chromium/cc/trees/property_tree_unittest.cc +++ b/chromium/cc/trees/property_tree_unittest.cc @@ -634,5 +634,37 @@ TEST(EffectTreeTest, CopyOutputRequestsThatBecomeIllegalAreDropped) { EXPECT_TRUE(requests_out.empty()); } +// Tests that GetPixelSnappedScrollOffset cannot return a negative offset, even +// when the snap amount is larger than the scroll offset. The snap amount can be +// (fractionally) larger due to floating point precision errors, and if the +// scroll offset is near zero that can naively lead to a negative offset being +// returned which is not desirable. +TEST(ScrollTreeTest, GetPixelSnappedScrollOffsetNegativeOffset) { + PropertyTrees property_trees; + ScrollTree& scroll_tree = property_trees.scroll_tree; + TransformTree& transform_tree = property_trees.transform_tree; + + ElementId element_id(5); + int transform_node_id = transform_tree.Insert(TransformNode(), 0); + int scroll_node_id = scroll_tree.Insert(ScrollNode(), 0); + scroll_tree.Node(scroll_node_id)->transform_id = transform_node_id; + scroll_tree.Node(scroll_node_id)->element_id = element_id; + + // Set a scroll value close to 0. + scroll_tree.SetScrollOffset(element_id, gfx::ScrollOffset(0, 0.1)); + transform_tree.Node(transform_node_id)->scrolls = true; + transform_tree.Node(transform_node_id)->scroll_offset = + gfx::ScrollOffset(0, 0.1); + + // Pretend that the snap amount was slightly larger than 0.1. + transform_tree.Node(transform_node_id)->snap_amount = gfx::Vector2dF(0, 0.2); + transform_tree.Node(transform_node_id)->needs_local_transform_update = false; + + // The returned offset should be clamped at a minimum of 0. + gfx::ScrollOffset offset = + scroll_tree.GetPixelSnappedScrollOffset(scroll_node_id); + EXPECT_EQ(offset.y(), 0); +} + } // namespace } // namespace cc diff --git a/chromium/cc/trees/proxy_common.h b/chromium/cc/trees/proxy_common.h index bc45efa8b8a..8dacb9c39d2 100644 --- a/chromium/cc/trees/proxy_common.h +++ b/chromium/cc/trees/proxy_common.h @@ -9,7 +9,7 @@ #include "base/callback_forward.h" #include "cc/cc_export.h" -#include "cc/metrics/frame_sequence_tracker.h" +#include "cc/metrics/frame_sequence_tracker_collection.h" #include "components/viz/common/frame_sinks/begin_frame_args.h" namespace cc { diff --git a/chromium/cc/trees/proxy_impl.cc b/chromium/cc/trees/proxy_impl.cc index ba19daeb353..544ad6392e3 100644 --- a/chromium/cc/trees/proxy_impl.cc +++ b/chromium/cc/trees/proxy_impl.cc @@ -535,6 +535,13 @@ void ProxyImpl::NotifyPaintWorkletStateChange( scheduler_->NotifyPaintWorkletStateChange(state); } +void ProxyImpl::NotifyThroughputTrackerResults(CustomTrackerResults results) { + DCHECK(IsImplThread()); + MainThreadTaskRunner()->PostTask( + FROM_HERE, base::BindOnce(&ProxyMain::NotifyThroughputTrackerResults, + proxy_main_weak_ptr_, std::move(results))); +} + bool ProxyImpl::WillBeginImplFrame(const viz::BeginFrameArgs& args) { DCHECK(IsImplThread()); return host_impl_->WillBeginImplFrame(args); diff --git a/chromium/cc/trees/proxy_impl.h b/chromium/cc/trees/proxy_impl.h index 5457428faf4..3050199e43d 100644 --- a/chromium/cc/trees/proxy_impl.h +++ b/chromium/cc/trees/proxy_impl.h @@ -118,6 +118,7 @@ class CC_EXPORT ProxyImpl : public LayerTreeHostImplClient, ElementListType element_list_type) override; void NotifyPaintWorkletStateChange( Scheduler::PaintWorkletState state) override; + void NotifyThroughputTrackerResults(CustomTrackerResults results) override; // SchedulerClient implementation bool WillBeginImplFrame(const viz::BeginFrameArgs& args) override; diff --git a/chromium/cc/trees/proxy_main.cc b/chromium/cc/trees/proxy_main.cc index 5fb3d5ce309..22976f62b02 100644 --- a/chromium/cc/trees/proxy_main.cc +++ b/chromium/cc/trees/proxy_main.cc @@ -374,6 +374,10 @@ void ProxyMain::DidPresentCompositorFrame( feedback); } +void ProxyMain::NotifyThroughputTrackerResults(CustomTrackerResults results) { + layer_tree_host_->NotifyThroughputTrackerResults(std::move(results)); +} + bool ProxyMain::IsStarted() const { DCHECK(IsMainThread()); return started_; diff --git a/chromium/cc/trees/proxy_main.h b/chromium/cc/trees/proxy_main.h index 27d960bf8a2..8738cb1f38f 100644 --- a/chromium/cc/trees/proxy_main.h +++ b/chromium/cc/trees/proxy_main.h @@ -57,6 +57,7 @@ class CC_EXPORT ProxyMain : public Proxy { uint32_t frame_token, std::vector<LayerTreeHost::PresentationTimeCallback> callbacks, const gfx::PresentationFeedback& feedback); + void NotifyThroughputTrackerResults(CustomTrackerResults results); CommitPipelineStage max_requested_pipeline_stage() const { return max_requested_pipeline_stage_; diff --git a/chromium/cc/trees/scroll_node.cc b/chromium/cc/trees/scroll_node.cc index ab20c16bcda..5a83875a7ed 100644 --- a/chromium/cc/trees/scroll_node.cc +++ b/chromium/cc/trees/scroll_node.cc @@ -28,7 +28,8 @@ ScrollNode::ScrollNode() user_scrollable_horizontal(false), user_scrollable_vertical(false), transform_id(0), - overscroll_behavior(OverscrollBehavior::kOverscrollBehaviorTypeAuto) {} + overscroll_behavior(OverscrollBehavior::kOverscrollBehaviorTypeAuto), + is_composited(false) {} ScrollNode::ScrollNode(const ScrollNode& other) = default; @@ -51,7 +52,8 @@ bool ScrollNode::operator==(const ScrollNode& other) const { user_scrollable_vertical == other.user_scrollable_vertical && element_id == other.element_id && transform_id == other.transform_id && overscroll_behavior == other.overscroll_behavior && - snap_container_data == other.snap_container_data; + snap_container_data == other.snap_container_data && + is_composited == other.is_composited; } void ScrollNode::AsValueInto(base::trace_event::TracedValue* value) const { @@ -87,6 +89,8 @@ void ScrollNode::AsValueInto(base::trace_event::TracedValue* value) const { value->EndArray(); } } + + value->SetBoolean("is_composited", is_composited); } } // namespace cc diff --git a/chromium/cc/trees/scroll_node.h b/chromium/cc/trees/scroll_node.h index 57357bdf2e9..c822b5ab711 100644 --- a/chromium/cc/trees/scroll_node.h +++ b/chromium/cc/trees/scroll_node.h @@ -63,6 +63,8 @@ struct CC_EXPORT ScrollNode { base::Optional<SnapContainerData> snap_container_data; + bool is_composited : 1; + bool operator==(const ScrollNode& other) const; void AsValueInto(base::trace_event::TracedValue* value) const; }; diff --git a/chromium/cc/trees/single_thread_proxy.cc b/chromium/cc/trees/single_thread_proxy.cc index 7d6ac6449d0..6f469a42c4e 100644 --- a/chromium/cc/trees/single_thread_proxy.cc +++ b/chromium/cc/trees/single_thread_proxy.cc @@ -550,6 +550,11 @@ void SingleThreadProxy::NotifyPaintWorkletStateChange( NOTREACHED(); } +void SingleThreadProxy::NotifyThroughputTrackerResults( + CustomTrackerResults results) { + layer_tree_host_->NotifyThroughputTrackerResults(std::move(results)); +} + void SingleThreadProxy::RequestBeginMainFrameNotExpected(bool new_state) { if (scheduler_on_impl_thread_) { scheduler_on_impl_thread_->SetMainThreadWantsBeginMainFrameNotExpected( diff --git a/chromium/cc/trees/single_thread_proxy.h b/chromium/cc/trees/single_thread_proxy.h index 1d24289df85..96228af593c 100644 --- a/chromium/cc/trees/single_thread_proxy.h +++ b/chromium/cc/trees/single_thread_proxy.h @@ -136,6 +136,7 @@ class CC_EXPORT SingleThreadProxy : public Proxy, ElementListType element_list_type) override; void NotifyPaintWorkletStateChange( Scheduler::PaintWorkletState state) override; + void NotifyThroughputTrackerResults(CustomTrackerResults results) override; void RequestNewLayerTreeFrameSink(); diff --git a/chromium/cc/trees/swap_promise_monitor.cc b/chromium/cc/trees/swap_promise_monitor.cc index 635f17ab3c3..0cb85ca54d0 100644 --- a/chromium/cc/trees/swap_promise_monitor.cc +++ b/chromium/cc/trees/swap_promise_monitor.cc @@ -2,10 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/logging.h" +#include "cc/trees/swap_promise_monitor.h" +#include "base/check.h" #include "cc/trees/layer_tree_host_impl.h" #include "cc/trees/swap_promise_manager.h" -#include "cc/trees/swap_promise_monitor.h" namespace cc { diff --git a/chromium/cc/trees/tree_synchronizer.cc b/chromium/cc/trees/tree_synchronizer.cc index 711a7ea1b49..5c5a70d2020 100644 --- a/chromium/cc/trees/tree_synchronizer.cc +++ b/chromium/cc/trees/tree_synchronizer.cc @@ -8,10 +8,10 @@ #include <set> +#include "base/check_op.h" #include "base/containers/flat_set.h" #include "base/debug/crash_logging.h" #include "base/debug/dump_without_crashing.h" -#include "base/logging.h" #include "base/stl_util.h" #include "base/trace_event/trace_event.h" #include "cc/layers/layer.h" diff --git a/chromium/cc/trees/ukm_manager.cc b/chromium/cc/trees/ukm_manager.cc index 5a9e68cf8f2..2141cf1a21c 100644 --- a/chromium/cc/trees/ukm_manager.cc +++ b/chromium/cc/trees/ukm_manager.cc @@ -190,7 +190,7 @@ void UkmManager::RecordAggregateThroughput(AggregationType aggregation_type, void UkmManager::RecordLatencyUKM( CompositorFrameReporter::FrameReportType report_type, const std::vector<CompositorFrameReporter::StageData>& stage_history, - const base::flat_set<FrameSequenceTrackerType>* active_trackers, + const CompositorFrameReporter::ActiveTrackers& active_trackers, const viz::FrameTimingDetails& viz_breakdown) const { ukm::builders::Graphics_Smoothness_Latency builder(source_id_); @@ -258,7 +258,11 @@ void UkmManager::RecordLatencyUKM( } // Record the active trackers - for (const auto& frame_sequence_tracker_type : *active_trackers) { + for (size_t type = 0; type < active_trackers.size(); ++type) { + if (!active_trackers.test(type)) + continue; + const auto frame_sequence_tracker_type = + static_cast<FrameSequenceTrackerType>(type); if (frame_sequence_tracker_type == FrameSequenceTrackerType::kUniversal) continue; switch (frame_sequence_tracker_type) { diff --git a/chromium/cc/trees/ukm_manager.h b/chromium/cc/trees/ukm_manager.h index 08cce51e755..50653814f85 100644 --- a/chromium/cc/trees/ukm_manager.h +++ b/chromium/cc/trees/ukm_manager.h @@ -7,7 +7,7 @@ #include "cc/cc_export.h" #include "cc/metrics/compositor_frame_reporter.h" -#include "cc/metrics/frame_sequence_tracker.h" +#include "cc/metrics/frame_sequence_metrics.h" #include "services/metrics/public/cpp/ukm_source_id.h" #include "url/gurl.h" @@ -51,7 +51,7 @@ class CC_EXPORT UkmManager { void RecordLatencyUKM( CompositorFrameReporter::FrameReportType report_type, const std::vector<CompositorFrameReporter::StageData>& stage_history, - const base::flat_set<FrameSequenceTrackerType>* active_trackers, + const CompositorFrameReporter::ActiveTrackers& active_trackers, const viz::FrameTimingDetails& viz_breakdown) const; ukm::UkmRecorder* recorder_for_testing() { return recorder_.get(); } |