diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2021-09-01 11:08:40 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2021-10-01 12:16:21 +0000 |
commit | 03c549e0392f92c02536d3f86d5e1d8dfa3435ac (patch) | |
tree | fe49d170a929b34ba82cd10db1a0bd8e3760fa4b /chromium/cc | |
parent | 5d013f5804a0d91fcf6c626b2d6fb6eca5c845b0 (diff) | |
download | qtwebengine-chromium-03c549e0392f92c02536d3f86d5e1d8dfa3435ac.tar.gz |
BASELINE: Update Chromium to 91.0.4472.160
Change-Id: I0def1f08a2412aeed79a9ab95dd50eb5c3f65f31
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/cc')
114 files changed, 2641 insertions, 1359 deletions
diff --git a/chromium/cc/BUILD.gn b/chromium/cc/BUILD.gn index 3465bf70b6c..13098ad0074 100644 --- a/chromium/cc/BUILD.gn +++ b/chromium/cc/BUILD.gn @@ -35,6 +35,7 @@ cc_component("cc") { "benchmarks/unittest_only_benchmark_impl.h", "document_transition/document_transition_request.cc", "document_transition/document_transition_request.h", + "document_transition/document_transition_shared_element_id.h", "input/actively_scrolling_type.h", "input/browser_controls_offset_manager.cc", "input/browser_controls_offset_manager.h", @@ -860,7 +861,14 @@ cc_test("cc_unittests") { ] if (is_fuchsia) { - manifest = "//build/config/fuchsia/gfx_tests.cmx" + additional_manifest_fragments = [ + "//build/config/fuchsia/test/font_capabilities.test-cmx", + + # TODO(crbug.com/1185811): Figure out why jit_capabilities is needed. + "//build/config/fuchsia/test/jit_capabilities.test-cmx", + + "//build/config/fuchsia/test/vulkan_capabilities.test-cmx", + ] } if (enable_vulkan) { diff --git a/chromium/cc/OWNERS b/chromium/cc/OWNERS index 6b427ed86d7..57a11c57c6f 100644 --- a/chromium/cc/OWNERS +++ b/chromium/cc/OWNERS @@ -7,7 +7,6 @@ set noparent # layers -danakj@chromium.org pdr@chromium.org wangxianzhu@chromium.org @@ -57,5 +56,4 @@ sunnyps@chromium.org vasilyt@chromium.org # general -danakj@chromium.org vmpstr@chromium.org diff --git a/chromium/cc/animation/animation.cc b/chromium/cc/animation/animation.cc index 0b8d96cd812..220347a9220 100644 --- a/chromium/cc/animation/animation.cc +++ b/chromium/cc/animation/animation.cc @@ -6,6 +6,7 @@ #include <inttypes.h> #include <algorithm> +#include <memory> #include <string> #include <utility> @@ -32,7 +33,7 @@ Animation::Animation(int id, std::unique_ptr<KeyframeEffect> keyframe_effect) : animation_host_(), animation_timeline_(), animation_delegate_(), id_(id) { DCHECK(id_); if (!keyframe_effect) - keyframe_effect.reset(new KeyframeEffect(this)); + keyframe_effect = std::make_unique<KeyframeEffect>(this); keyframe_effect_ = std::move(keyframe_effect); } @@ -238,7 +239,8 @@ void Animation::ActivateKeyframeModels() { KeyframeModel* Animation::GetKeyframeModel( TargetProperty::Type target_property) const { - return keyframe_effect_->GetKeyframeModel(target_property); + return KeyframeModel::ToCcKeyframeModel( + keyframe_effect_->GetKeyframeModel(target_property)); } std::string Animation::ToString() const { diff --git a/chromium/cc/animation/element_animations.cc b/chromium/cc/animation/element_animations.cc index c8e8637523c..a5e64cac011 100644 --- a/chromium/cc/animation/element_animations.cc +++ b/chromium/cc/animation/element_animations.cc @@ -570,7 +570,8 @@ PropertyToElementIdMap ElementAnimations::GetPropertyToElementIdMap() const { static_cast<TargetProperty::Type>(property_index); ElementId element_id_for_property; for (auto& keyframe_effect : keyframe_effects_list_) { - KeyframeModel* model = keyframe_effect.GetKeyframeModel(property); + KeyframeModel* model = KeyframeModel::ToCcKeyframeModel( + keyframe_effect.GetKeyframeModel(property)); if (model) { // We deliberately use two branches here so that the DCHECK can // differentiate between models with different element ids, and the case diff --git a/chromium/cc/animation/element_animations_unittest.cc b/chromium/cc/animation/element_animations_unittest.cc index e5fecd65354..e151dc3db00 100644 --- a/chromium/cc/animation/element_animations_unittest.cc +++ b/chromium/cc/animation/element_animations_unittest.cc @@ -2539,11 +2539,13 @@ TEST_F(ElementAnimationsTest, NewlyPushedAnimationWaitsForActivation) { animation_impl_->keyframe_effect() ->GetKeyframeModelById(keyframe_model_id) ->run_state()); - EXPECT_TRUE(animation_impl_->keyframe_effect() - ->GetKeyframeModelById(keyframe_model_id) + EXPECT_TRUE(KeyframeModel::ToCcKeyframeModel( + animation_impl_->keyframe_effect()->GetKeyframeModelById( + keyframe_model_id)) ->affects_pending_elements()); - EXPECT_FALSE(animation_impl_->keyframe_effect() - ->GetKeyframeModelById(keyframe_model_id) + EXPECT_FALSE(KeyframeModel::ToCcKeyframeModel( + animation_impl_->keyframe_effect()->GetKeyframeModelById( + keyframe_model_id)) ->affects_active_elements()); animation_impl_->Tick(kInitialTickTime); @@ -2567,11 +2569,13 @@ TEST_F(ElementAnimationsTest, NewlyPushedAnimationWaitsForActivation) { EXPECT_EQ(0.f, client_impl_.GetOpacity(element_id_, ElementListType::ACTIVE)); animation_impl_->ActivateKeyframeModels(); - EXPECT_TRUE(animation_impl_->keyframe_effect() - ->GetKeyframeModelById(keyframe_model_id) + EXPECT_TRUE(KeyframeModel::ToCcKeyframeModel( + animation_impl_->keyframe_effect()->GetKeyframeModelById( + keyframe_model_id)) ->affects_pending_elements()); - EXPECT_TRUE(animation_impl_->keyframe_effect() - ->GetKeyframeModelById(keyframe_model_id) + EXPECT_TRUE(KeyframeModel::ToCcKeyframeModel( + animation_impl_->keyframe_effect()->GetKeyframeModelById( + keyframe_model_id)) ->affects_active_elements()); animation_impl_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(1000)); @@ -2607,11 +2611,13 @@ TEST_F(ElementAnimationsTest, ActivationBetweenAnimateAndUpdateState) { animation_impl_->keyframe_effect() ->GetKeyframeModelById(keyframe_model_id) ->run_state()); - EXPECT_TRUE(animation_impl_->keyframe_effect() - ->GetKeyframeModelById(keyframe_model_id) + EXPECT_TRUE(KeyframeModel::ToCcKeyframeModel( + animation_impl_->keyframe_effect()->GetKeyframeModelById( + keyframe_model_id)) ->affects_pending_elements()); - EXPECT_FALSE(animation_impl_->keyframe_effect() - ->GetKeyframeModelById(keyframe_model_id) + EXPECT_FALSE(KeyframeModel::ToCcKeyframeModel( + animation_impl_->keyframe_effect()->GetKeyframeModelById( + keyframe_model_id)) ->affects_active_elements()); animation_impl_->Tick(kInitialTickTime); @@ -2623,11 +2629,13 @@ TEST_F(ElementAnimationsTest, ActivationBetweenAnimateAndUpdateState) { EXPECT_EQ(0.f, client_impl_.GetOpacity(element_id_, ElementListType::ACTIVE)); animation_impl_->ActivateKeyframeModels(); - EXPECT_TRUE(animation_impl_->keyframe_effect() - ->GetKeyframeModelById(keyframe_model_id) + EXPECT_TRUE(KeyframeModel::ToCcKeyframeModel( + animation_impl_->keyframe_effect()->GetKeyframeModelById( + keyframe_model_id)) ->affects_pending_elements()); - EXPECT_TRUE(animation_impl_->keyframe_effect() - ->GetKeyframeModelById(keyframe_model_id) + EXPECT_TRUE(KeyframeModel::ToCcKeyframeModel( + animation_impl_->keyframe_effect()->GetKeyframeModelById( + keyframe_model_id)) ->affects_active_elements()); animation_impl_->UpdateState(true, events.get()); @@ -3568,11 +3576,13 @@ TEST_F(ElementAnimationsTest, PushedDeletedAnimationWaitsForActivation) { EXPECT_EQ(0.5f, client_impl_.GetOpacity(element_id_, ElementListType::ACTIVE)); - EXPECT_TRUE(animation_impl_->keyframe_effect() - ->GetKeyframeModelById(keyframe_model_id) + EXPECT_TRUE(KeyframeModel::ToCcKeyframeModel( + animation_impl_->keyframe_effect()->GetKeyframeModelById( + keyframe_model_id)) ->affects_pending_elements()); - EXPECT_TRUE(animation_impl_->keyframe_effect() - ->GetKeyframeModelById(keyframe_model_id) + EXPECT_TRUE(KeyframeModel::ToCcKeyframeModel( + animation_impl_->keyframe_effect()->GetKeyframeModelById( + keyframe_model_id)) ->affects_active_elements()); // Delete the animation on the main-thread animations. @@ -3581,11 +3591,13 @@ TEST_F(ElementAnimationsTest, PushedDeletedAnimationWaitsForActivation) { PushProperties(); // The animation should no longer affect pending elements. - EXPECT_FALSE(animation_impl_->keyframe_effect() - ->GetKeyframeModelById(keyframe_model_id) + EXPECT_FALSE(KeyframeModel::ToCcKeyframeModel( + animation_impl_->keyframe_effect()->GetKeyframeModelById( + keyframe_model_id)) ->affects_pending_elements()); - EXPECT_TRUE(animation_impl_->keyframe_effect() - ->GetKeyframeModelById(keyframe_model_id) + EXPECT_TRUE(KeyframeModel::ToCcKeyframeModel( + animation_impl_->keyframe_effect()->GetKeyframeModelById( + keyframe_model_id)) ->affects_active_elements()); animation_impl_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(500)); @@ -3644,17 +3656,21 @@ TEST_F(ElementAnimationsTest, StartAnimationsAffectingDifferentObservers) { // The original animation should only affect active elements, and the new // animation should only affect pending elements. - EXPECT_FALSE(animation_impl_->keyframe_effect() - ->GetKeyframeModelById(first_keyframe_model_id) + EXPECT_FALSE(KeyframeModel::ToCcKeyframeModel( + animation_impl_->keyframe_effect()->GetKeyframeModelById( + first_keyframe_model_id)) ->affects_pending_elements()); - EXPECT_TRUE(animation_impl_->keyframe_effect() - ->GetKeyframeModelById(first_keyframe_model_id) + EXPECT_TRUE(KeyframeModel::ToCcKeyframeModel( + animation_impl_->keyframe_effect()->GetKeyframeModelById( + first_keyframe_model_id)) ->affects_active_elements()); - EXPECT_TRUE(animation_impl_->keyframe_effect() - ->GetKeyframeModelById(second_keyframe_model_id) + EXPECT_TRUE(KeyframeModel::ToCcKeyframeModel( + animation_impl_->keyframe_effect()->GetKeyframeModelById( + second_keyframe_model_id)) ->affects_pending_elements()); - EXPECT_FALSE(animation_impl_->keyframe_effect() - ->GetKeyframeModelById(second_keyframe_model_id) + EXPECT_FALSE(KeyframeModel::ToCcKeyframeModel( + animation_impl_->keyframe_effect()->GetKeyframeModelById( + second_keyframe_model_id)) ->affects_active_elements()); animation_impl_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(500)); @@ -3682,17 +3698,21 @@ TEST_F(ElementAnimationsTest, StartAnimationsAffectingDifferentObservers) { // The original animation no longer affect either elements, and the new // animation should now affect both elements. - EXPECT_FALSE(animation_impl_->keyframe_effect() - ->GetKeyframeModelById(first_keyframe_model_id) + EXPECT_FALSE(KeyframeModel::ToCcKeyframeModel( + animation_impl_->keyframe_effect()->GetKeyframeModelById( + first_keyframe_model_id)) ->affects_pending_elements()); - EXPECT_FALSE(animation_impl_->keyframe_effect() - ->GetKeyframeModelById(first_keyframe_model_id) + EXPECT_FALSE(KeyframeModel::ToCcKeyframeModel( + animation_impl_->keyframe_effect()->GetKeyframeModelById( + first_keyframe_model_id)) ->affects_active_elements()); - EXPECT_TRUE(animation_impl_->keyframe_effect() - ->GetKeyframeModelById(second_keyframe_model_id) + EXPECT_TRUE(KeyframeModel::ToCcKeyframeModel( + animation_impl_->keyframe_effect()->GetKeyframeModelById( + second_keyframe_model_id)) ->affects_pending_elements()); - EXPECT_TRUE(animation_impl_->keyframe_effect() - ->GetKeyframeModelById(second_keyframe_model_id) + EXPECT_TRUE(KeyframeModel::ToCcKeyframeModel( + animation_impl_->keyframe_effect()->GetKeyframeModelById( + second_keyframe_model_id)) ->affects_active_elements()); animation_impl_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(1000)); diff --git a/chromium/cc/animation/filter_animation_curve.cc b/chromium/cc/animation/filter_animation_curve.cc index 83d991c0662..38ee1e8d1b6 100644 --- a/chromium/cc/animation/filter_animation_curve.cc +++ b/chromium/cc/animation/filter_animation_curve.cc @@ -5,97 +5,10 @@ #include "cc/animation/filter_animation_curve.h" #include "base/memory/ptr_util.h" +#include "ui/gfx/animation/keyframe/keyframed_animation_curve-inl.h" namespace cc { -// TODO(crbug.com/747185): All code in this namespace duplicates code from -// ui/gfx/keyframe/animation/ unnecessarily. -namespace { - -template <class KeyframeType> -void InsertKeyframe(std::unique_ptr<KeyframeType> keyframe, - std::vector<std::unique_ptr<KeyframeType>>* keyframes) { - // Usually, the keyframes will be added in order, so this loop would be - // unnecessary and we should skip it if possible. - if (!keyframes->empty() && keyframe->Time() < keyframes->back()->Time()) { - for (size_t i = 0; i < keyframes->size(); ++i) { - if (keyframe->Time() < keyframes->at(i)->Time()) { - keyframes->insert(keyframes->begin() + i, std::move(keyframe)); - return; - } - } - } - - keyframes->push_back(std::move(keyframe)); -} - -struct TimeValues { - base::TimeDelta start_time; - base::TimeDelta duration; - double progress; -}; - -template <typename KeyframeType> -TimeValues GetTimeValues(const KeyframeType& start_frame, - const KeyframeType& end_frame, - double scaled_duration, - base::TimeDelta time) { - TimeValues values; - values.start_time = start_frame.Time() * scaled_duration; - values.duration = (end_frame.Time() * scaled_duration) - values.start_time; - const base::TimeDelta elapsed = time - values.start_time; - values.progress = (elapsed.is_inf() || values.duration.is_zero()) - ? 1.0 - : (elapsed / values.duration); - return values; -} - -template <typename KeyframeType> -base::TimeDelta TransformedAnimationTime( - const std::vector<std::unique_ptr<KeyframeType>>& keyframes, - const std::unique_ptr<gfx::TimingFunction>& timing_function, - double scaled_duration, - base::TimeDelta time) { - if (timing_function) { - const auto values = GetTimeValues(*keyframes.front(), *keyframes.back(), - scaled_duration, time); - time = (values.duration * timing_function->GetValue(values.progress)) + - values.start_time; - } - - return time; -} - -template <typename KeyframeType> -size_t GetActiveKeyframe( - const std::vector<std::unique_ptr<KeyframeType>>& keyframes, - double scaled_duration, - base::TimeDelta time) { - DCHECK_GE(keyframes.size(), 2ul); - size_t i = 0; - while ((i < keyframes.size() - 2) && // Last keyframe is never active. - (time >= (keyframes[i + 1]->Time() * scaled_duration))) - ++i; - - return i; -} - -template <typename KeyframeType> -double TransformedKeyframeProgress( - const std::vector<std::unique_ptr<KeyframeType>>& keyframes, - double scaled_duration, - base::TimeDelta time, - size_t i) { - const double progress = - GetTimeValues(*keyframes[i], *keyframes[i + 1], scaled_duration, time) - .progress; - return keyframes[i]->timing_function() - ? keyframes[i]->timing_function()->GetValue(progress) - : progress; -} - -} // namespace - void FilterAnimationCurve::Tick(base::TimeDelta t, int property_id, gfx::KeyframeModel* keyframe_model) const { @@ -151,6 +64,10 @@ std::unique_ptr<FilterKeyframe> FilterKeyframe::Clone() const { return FilterKeyframe::Create(Time(), Value(), std::move(func)); } +base::TimeDelta KeyframedFilterAnimationCurve::TickInterval() const { + return ComputeTickInterval(timing_function_, scaled_duration(), keyframes_); +} + void KeyframedFilterAnimationCurve::AddKeyframe( std::unique_ptr<FilterKeyframe> keyframe) { InsertKeyframe(std::move(keyframe), &keyframes_); diff --git a/chromium/cc/animation/filter_animation_curve.h b/chromium/cc/animation/filter_animation_curve.h index 0455d67df19..af8d7355a47 100644 --- a/chromium/cc/animation/filter_animation_curve.h +++ b/chromium/cc/animation/filter_animation_curve.h @@ -64,6 +64,7 @@ class CC_ANIMATION_EXPORT KeyframedFilterAnimationCurve // AnimationCurve implementation base::TimeDelta Duration() const override; std::unique_ptr<gfx::AnimationCurve> Clone() const override; + base::TimeDelta TickInterval() const override; // FilterAnimationCurve implementation FilterOperations GetValue(base::TimeDelta t) const override; diff --git a/chromium/cc/animation/filter_animation_curve_unittest.cc b/chromium/cc/animation/filter_animation_curve_unittest.cc index e7401a78a11..81ef211d841 100644 --- a/chromium/cc/animation/filter_animation_curve_unittest.cc +++ b/chromium/cc/animation/filter_animation_curve_unittest.cc @@ -24,7 +24,7 @@ void ExpectBrightness(double brightness, const FilterOperations& filter) { } // Tests that a filter animation with one keyframe works as expected. -TEST(KeyframedAnimationCurveTest, OneFilterKeyframe) { +TEST(FilterAnimationCurveTest, OneFilterKeyframe) { std::unique_ptr<KeyframedFilterAnimationCurve> curve( KeyframedFilterAnimationCurve::Create()); FilterOperations operations; @@ -40,7 +40,7 @@ TEST(KeyframedAnimationCurveTest, OneFilterKeyframe) { } // Tests that a filter animation with two keyframes works as expected. -TEST(KeyframedAnimationCurveTest, TwoFilterKeyframe) { +TEST(FilterAnimationCurveTest, TwoFilterKeyframe) { std::unique_ptr<KeyframedFilterAnimationCurve> curve( KeyframedFilterAnimationCurve::Create()); FilterOperations operations1; @@ -60,7 +60,7 @@ TEST(KeyframedAnimationCurveTest, TwoFilterKeyframe) { } // Tests that a filter animation with three keyframes works as expected. -TEST(KeyframedAnimationCurveTest, ThreeFilterKeyframe) { +TEST(FilterAnimationCurveTest, ThreeFilterKeyframe) { std::unique_ptr<KeyframedFilterAnimationCurve> curve( KeyframedFilterAnimationCurve::Create()); FilterOperations operations1; @@ -86,7 +86,7 @@ TEST(KeyframedAnimationCurveTest, ThreeFilterKeyframe) { // Tests that a filter animation with multiple keys at a given time works // sanely. -TEST(KeyframedAnimationCurveTest, RepeatedFilterKeyTimes) { +TEST(FilterAnimationCurveTest, RepeatedFilterKeyTimes) { std::unique_ptr<KeyframedFilterAnimationCurve> curve( KeyframedFilterAnimationCurve::Create()); // A step function. diff --git a/chromium/cc/animation/keyframe_effect.cc b/chromium/cc/animation/keyframe_effect.cc index ef4ec75cb45..9b34b0c2adb 100644 --- a/chromium/cc/animation/keyframe_effect.cc +++ b/chromium/cc/animation/keyframe_effect.cc @@ -35,13 +35,14 @@ bool NeedsFinishedEvent(KeyframeModel* keyframe_model) { // Returns indices for keyframe_models that have matching group id. std::vector<size_t> FindAnimationsWithSameGroupId( - const std::vector<std::unique_ptr<KeyframeModel>>& keyframe_models, + const std::vector<std::unique_ptr<gfx::KeyframeModel>>& keyframe_models, int group_id) { std::vector<size_t> group; for (size_t i = 0; i < keyframe_models.size(); ++i) { - if (keyframe_models[i]->group() != group_id) + auto* cc_keyframe_model = + KeyframeModel::ToCcKeyframeModel(keyframe_models[i].get()); + if (cc_keyframe_model->group() != group_id) continue; - group.push_back(i); } return group; @@ -110,7 +111,7 @@ void KeyframeEffect::Tick(base::TimeTicks monotonic_time) { if (needs_to_start_keyframe_models_) StartKeyframeModels(monotonic_time); - for (auto& keyframe_model : keyframe_models_) { + for (auto& keyframe_model : keyframe_models()) { TickKeyframeModel(monotonic_time, keyframe_model.get()); } @@ -118,21 +119,6 @@ void KeyframeEffect::Tick(base::TimeTicks monotonic_time) { element_animations_->UpdateClientAnimationState(); } -void KeyframeEffect::TickKeyframeModel(base::TimeTicks monotonic_time, - KeyframeModel* keyframe_model) { - if ((keyframe_model->run_state() != gfx::KeyframeModel::STARTING && - keyframe_model->run_state() != gfx::KeyframeModel::RUNNING && - keyframe_model->run_state() != gfx::KeyframeModel::PAUSED) || - !keyframe_model->InEffect(monotonic_time)) { - return; - } - - gfx::AnimationCurve* curve = keyframe_model->curve(); - base::TimeDelta trimmed = - keyframe_model->TrimTimeToCurrentIteration(monotonic_time); - curve->Tick(trimmed, keyframe_model->TargetProperty(), keyframe_model); -} - void KeyframeEffect::RemoveFromTicking() { is_ticking_ = false; // Resetting last_tick_time_ here ensures that calling ::UpdateState @@ -186,7 +172,7 @@ void KeyframeEffect::UpdateTickingState() { void KeyframeEffect::Pause(base::TimeDelta pause_offset, PauseCondition pause_condition) { bool did_pause = false; - for (auto& keyframe_model : keyframe_models_) { + for (auto& keyframe_model : keyframe_models()) { // TODO(crbug.com/1076012): KeyframeEffect is paused with local time for // scroll-linked animations. To make sure the start event of a keyframe // model is sent to blink, we should not set its run state to PAUSED until @@ -208,17 +194,22 @@ void KeyframeEffect::Pause(base::TimeDelta pause_offset, } void KeyframeEffect::AddKeyframeModel( - std::unique_ptr<KeyframeModel> keyframe_model) { - DCHECK(!keyframe_model->is_impl_only() || + std::unique_ptr<gfx::KeyframeModel> keyframe_model) { + KeyframeModel* cc_keyframe_model = + KeyframeModel::ToCcKeyframeModel(keyframe_model.get()); + DCHECK(!cc_keyframe_model->is_impl_only() || keyframe_model->TargetProperty() == TargetProperty::SCROLL_OFFSET); // This is to make sure that keyframe models in the same group, i.e., start // together, don't animate the same property. - DCHECK(std::none_of(keyframe_models_.begin(), keyframe_models_.end(), + DCHECK(std::none_of(keyframe_models().begin(), keyframe_models().end(), [&](const auto& existing_keyframe_model) { + auto* cc_existing_keyframe_model = + KeyframeModel::ToCcKeyframeModel( + existing_keyframe_model.get()); return keyframe_model->TargetProperty() == existing_keyframe_model->TargetProperty() && - keyframe_model->group() == - existing_keyframe_model->group(); + cc_keyframe_model->group() == + cc_existing_keyframe_model->group(); })); if (keyframe_model->TargetProperty() == TargetProperty::SCROLL_OFFSET) { @@ -226,17 +217,19 @@ void KeyframeEffect::AddKeyframeModel( // same scrolling element as this would result in multiple automated // scrolls. DCHECK(std::none_of( - keyframe_models_.begin(), keyframe_models_.end(), + keyframe_models().begin(), keyframe_models().end(), [&](const auto& existing_keyframe_model) { - return existing_keyframe_model->TargetProperty() == + auto* cc_existing_keyframe_model = + KeyframeModel::ToCcKeyframeModel(existing_keyframe_model.get()); + return cc_existing_keyframe_model->TargetProperty() == TargetProperty::SCROLL_OFFSET && - !existing_keyframe_model->is_finished() && - (!existing_keyframe_model->is_controlling_instance() || - existing_keyframe_model->affects_pending_elements()); + !cc_existing_keyframe_model->is_finished() && + (!cc_existing_keyframe_model->is_controlling_instance() || + cc_existing_keyframe_model->affects_pending_elements()); })); } - keyframe_models_.push_back(std::move(keyframe_model)); + gfx::KeyframeEffect::AddKeyframeModel(std::move(keyframe_model)); if (has_bound_element_animations()) { KeyframeModelAdded(); @@ -246,7 +239,7 @@ void KeyframeEffect::AddKeyframeModel( void KeyframeEffect::PauseKeyframeModel(int keyframe_model_id, base::TimeDelta time_offset) { - for (auto& keyframe_model : keyframe_models_) { + for (auto& keyframe_model : keyframe_models()) { if (keyframe_model->id() == keyframe_model_id) { keyframe_model->Pause(time_offset); } @@ -258,41 +251,9 @@ void KeyframeEffect::PauseKeyframeModel(int keyframe_model_id, } } -void KeyframeEffect::RemoveKeyframeModel(int keyframe_model_id) { - bool keyframe_model_removed = false; - - // Since we want to use the KeyframeModels that we're going to remove, we - // need to use a stable_partition here instead of remove_if. remove_if leaves - // the removed items in an unspecified state. - auto keyframe_models_to_remove = std::stable_partition( - keyframe_models_.begin(), keyframe_models_.end(), - [keyframe_model_id]( - const std::unique_ptr<KeyframeModel>& keyframe_model) { - return keyframe_model->id() != keyframe_model_id; - }); - for (auto it = keyframe_models_to_remove; it != keyframe_models_.end(); - ++it) { - if ((*it)->TargetProperty() == TargetProperty::SCROLL_OFFSET) { - if (has_bound_element_animations()) - scroll_offset_animation_was_interrupted_ = true; - } else if (!(*it)->is_finished()) { - keyframe_model_removed = true; - } - } - - keyframe_models_.erase(keyframe_models_to_remove, keyframe_models_.end()); - - if (has_bound_element_animations()) { - UpdateTickingState(); - if (keyframe_model_removed) - element_animations_->UpdateClientAnimationState(); - animation_->SetNeedsCommit(); - SetNeedsPushProperties(); - } -} - void KeyframeEffect::AbortKeyframeModel(int keyframe_model_id) { - if (KeyframeModel* keyframe_model = GetKeyframeModelById(keyframe_model_id)) { + if (gfx::KeyframeModel* keyframe_model = + GetKeyframeModelById(keyframe_model_id)) { if (!keyframe_model->is_finished()) { keyframe_model->SetRunState(gfx::KeyframeModel::ABORTED, last_tick_time_.value_or(base::TimeTicks())); @@ -314,12 +275,14 @@ void KeyframeEffect::AbortKeyframeModelsWithProperty( DCHECK(target_property == TargetProperty::SCROLL_OFFSET); bool aborted_keyframe_model = false; - for (auto& keyframe_model : keyframe_models_) { + for (auto& keyframe_model : keyframe_models()) { if (keyframe_model->TargetProperty() == target_property && !keyframe_model->is_finished()) { // Currently only impl-only scroll offset KeyframeModels can be completed // on the main thread. - if (needs_completion && keyframe_model->is_impl_only()) { + if (needs_completion && + KeyframeModel::ToCcKeyframeModel(keyframe_model.get()) + ->is_impl_only()) { keyframe_model->SetRunState( gfx::KeyframeModel::ABORTED_BUT_NEEDS_COMPLETION, last_tick_time_.value_or(base::TimeTicks())); @@ -344,13 +307,15 @@ void KeyframeEffect::ActivateKeyframeModels() { DCHECK(has_bound_element_animations()); bool keyframe_model_activated = false; - for (auto& keyframe_model : keyframe_models_) { - if (keyframe_model->affects_active_elements() != - keyframe_model->affects_pending_elements()) { + for (auto& keyframe_model : keyframe_models()) { + auto* cc_keyframe_model = + KeyframeModel::ToCcKeyframeModel(keyframe_model.get()); + if (cc_keyframe_model->affects_active_elements() != + cc_keyframe_model->affects_pending_elements()) { keyframe_model_activated = true; } - keyframe_model->set_affects_active_elements( - keyframe_model->affects_pending_elements()); + cc_keyframe_model->set_affects_active_elements( + cc_keyframe_model->affects_pending_elements()); } if (keyframe_model_activated) @@ -366,7 +331,7 @@ void KeyframeEffect::KeyframeModelAdded() { needs_to_start_keyframe_models_ = true; UpdateTickingState(); - for (auto& keyframe_model : keyframe_models_) { + for (auto& keyframe_model : keyframe_models()) { element_animations_->AttachToCurve(keyframe_model->curve()); } element_animations_->UpdateClientAnimationState(); @@ -375,7 +340,8 @@ void KeyframeEffect::KeyframeModelAdded() { bool KeyframeEffect::DispatchAnimationEventToKeyframeModel( const AnimationEvent& event) { DCHECK(!event.is_impl_only); - KeyframeModel* keyframe_model = GetKeyframeModelById(event.uid.model_id); + KeyframeModel* keyframe_model = KeyframeModel::ToCcKeyframeModel( + GetKeyframeModelById(event.uid.model_id)); bool dispatched = false; switch (event.type) { case AnimationEvent::STARTED: @@ -434,7 +400,7 @@ bool KeyframeEffect::DispatchAnimationEventToKeyframeModel( } bool KeyframeEffect::HasTickingKeyframeModel() const { - for (const auto& keyframe_model : keyframe_models_) { + for (const auto& keyframe_model : keyframe_models()) { if (!keyframe_model->is_finished()) return true; } @@ -442,14 +408,14 @@ bool KeyframeEffect::HasTickingKeyframeModel() const { } bool KeyframeEffect::AffectsCustomProperty() const { - for (const auto& it : keyframe_models_) + for (const auto& it : keyframe_models()) if (it->TargetProperty() == TargetProperty::CSS_CUSTOM_PROPERTY) return true; return false; } bool KeyframeEffect::HasNonDeletedKeyframeModel() const { - for (const auto& keyframe_model : keyframe_models_) { + for (const auto& keyframe_model : keyframe_models()) { if (keyframe_model->run_state() != gfx::KeyframeModel::WAITING_FOR_DELETION) return true; } @@ -457,7 +423,7 @@ bool KeyframeEffect::HasNonDeletedKeyframeModel() const { } bool KeyframeEffect::AnimationsPreserveAxisAlignment() const { - for (const auto& keyframe_model : keyframe_models_) { + for (const auto& keyframe_model : keyframe_models()) { if (keyframe_model->is_finished()) continue; @@ -469,14 +435,17 @@ bool KeyframeEffect::AnimationsPreserveAxisAlignment() const { float KeyframeEffect::MaximumScale(ElementListType list_type) const { float maximum_scale = kInvalidScale; - for (const auto& keyframe_model : keyframe_models_) { + for (const auto& keyframe_model : keyframe_models()) { if (keyframe_model->is_finished()) continue; + auto* cc_keyframe_model = + KeyframeModel::ToCcKeyframeModel(keyframe_model.get()); + if ((list_type == ElementListType::ACTIVE && - !keyframe_model->affects_active_elements()) || + !cc_keyframe_model->affects_active_elements()) || (list_type == ElementListType::PENDING && - !keyframe_model->affects_pending_elements())) + !cc_keyframe_model->affects_pending_elements())) continue; float curve_maximum_scale = kInvalidScale; @@ -489,13 +458,15 @@ float KeyframeEffect::MaximumScale(ElementListType list_type) const { bool KeyframeEffect::IsPotentiallyAnimatingProperty( TargetProperty::Type target_property, ElementListType list_type) const { - for (const auto& keyframe_model : keyframe_models_) { + for (const auto& keyframe_model : keyframe_models()) { if (!keyframe_model->is_finished() && keyframe_model->TargetProperty() == target_property) { + auto* cc_keyframe_model = + KeyframeModel::ToCcKeyframeModel(keyframe_model.get()); if ((list_type == ElementListType::ACTIVE && - keyframe_model->affects_active_elements()) || + cc_keyframe_model->affects_active_elements()) || (list_type == ElementListType::PENDING && - keyframe_model->affects_pending_elements())) + cc_keyframe_model->affects_pending_elements())) return true; } } @@ -505,50 +476,37 @@ bool KeyframeEffect::IsPotentiallyAnimatingProperty( bool KeyframeEffect::IsCurrentlyAnimatingProperty( TargetProperty::Type target_property, ElementListType list_type) const { - for (const auto& keyframe_model : keyframe_models_) { + for (const auto& keyframe_model : keyframe_models()) { + auto* cc_keyframe_model = + KeyframeModel::ToCcKeyframeModel(keyframe_model.get()); if (!keyframe_model->is_finished() && - keyframe_model->InEffect(last_tick_time_.value_or(base::TimeTicks())) && + cc_keyframe_model->InEffect( + last_tick_time_.value_or(base::TimeTicks())) && keyframe_model->TargetProperty() == target_property) { if ((list_type == ElementListType::ACTIVE && - keyframe_model->affects_active_elements()) || + cc_keyframe_model->affects_active_elements()) || (list_type == ElementListType::PENDING && - keyframe_model->affects_pending_elements())) + cc_keyframe_model->affects_pending_elements())) return true; } } return false; } -KeyframeModel* KeyframeEffect::GetKeyframeModel( - TargetProperty::Type target_property) const { - for (size_t i = 0; i < keyframe_models_.size(); ++i) { - size_t index = keyframe_models_.size() - i - 1; - if (keyframe_models_[index]->TargetProperty() == target_property) - return keyframe_models_[index].get(); - } - return nullptr; -} - -KeyframeModel* KeyframeEffect::GetKeyframeModelById( - int keyframe_model_id) const { - for (auto& keyframe_model : keyframe_models_) - if (keyframe_model->id() == keyframe_model_id) - return keyframe_model.get(); - return nullptr; -} - void KeyframeEffect::GetPropertyAnimationState( PropertyAnimationState* pending_state, PropertyAnimationState* active_state) const { pending_state->Clear(); active_state->Clear(); - for (const auto& keyframe_model : keyframe_models_) { + for (const auto& keyframe_model : keyframe_models()) { if (!keyframe_model->is_finished()) { - 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(); + auto* cc_keyframe_model = + KeyframeModel::ToCcKeyframeModel(keyframe_model.get()); + bool in_effect = cc_keyframe_model->InEffect( + last_tick_time_.value_or(base::TimeTicks())); + bool active = cc_keyframe_model->affects_active_elements(); + bool pending = cc_keyframe_model->affects_pending_elements(); int property = keyframe_model->TargetProperty(); if (pending) @@ -568,11 +526,11 @@ void KeyframeEffect::MarkAbortedKeyframeModelsForDeletion( KeyframeEffect* keyframe_effect_impl) { bool keyframe_model_aborted = false; - auto& keyframe_models_impl = keyframe_effect_impl->keyframe_models_; + auto& keyframe_models_impl = keyframe_effect_impl->keyframe_models(); for (const auto& keyframe_model_impl : keyframe_models_impl) { // If the keyframe_model has been aborted on the main thread, mark it for // deletion. - if (KeyframeModel* keyframe_model = + if (gfx::KeyframeModel* keyframe_model = GetKeyframeModelById(keyframe_model_impl->id())) { if (keyframe_model->run_state() == gfx::KeyframeModel::ABORTED) { keyframe_model_impl->SetRunState( @@ -591,29 +549,28 @@ void KeyframeEffect::MarkAbortedKeyframeModelsForDeletion( } void KeyframeEffect::PurgeKeyframeModelsMarkedForDeletion(bool impl_only) { - base::EraseIf( - keyframe_models_, - [impl_only](const std::unique_ptr<KeyframeModel>& keyframe_model) { - return keyframe_model->run_state() == - gfx::KeyframeModel::WAITING_FOR_DELETION && - (!impl_only || keyframe_model->is_impl_only()); - }); + base::EraseIf(keyframe_models(), [impl_only](const auto& keyframe_model) { + return keyframe_model->run_state() == + gfx::KeyframeModel::WAITING_FOR_DELETION && + (!impl_only || KeyframeModel::ToCcKeyframeModel(keyframe_model.get()) + ->is_impl_only()); + }); } void KeyframeEffect::PurgeDeletedKeyframeModels() { - base::EraseIf(keyframe_models_, - [](const std::unique_ptr<KeyframeModel>& keyframe_model) { - return keyframe_model->run_state() == - gfx::KeyframeModel::WAITING_FOR_DELETION && - !keyframe_model->affects_pending_elements(); - }); + base::EraseIf(keyframe_models(), [](const auto& keyframe_model) { + return keyframe_model->run_state() == + gfx::KeyframeModel::WAITING_FOR_DELETION && + !KeyframeModel::ToCcKeyframeModel(keyframe_model.get()) + ->affects_pending_elements(); + }); } void KeyframeEffect::PushNewKeyframeModelsToImplThread( KeyframeEffect* keyframe_effect_impl) const { // Any new KeyframeModels owned by the main thread's Animation are // cloned and added to the impl thread's Animation. - for (const auto& keyframe_model : keyframe_models_) { + for (const auto& keyframe_model : keyframe_models()) { // If the keyframe_model is finished, do not copy it over to impl since the // impl instance, if there was one, was just removed in // |RemoveKeyframeModelsCompletedOnMainThread|. @@ -647,7 +604,8 @@ void KeyframeEffect::PushNewKeyframeModelsToImplThread( gfx::KeyframeModel::RunState initial_run_state = gfx::KeyframeModel::WAITING_FOR_TARGET_AVAILABILITY; std::unique_ptr<KeyframeModel> to_add( - keyframe_model->CreateImplInstance(initial_run_state)); + KeyframeModel::ToCcKeyframeModel(keyframe_model.get()) + ->CreateImplInstance(initial_run_state)); DCHECK(!to_add->needs_synchronized_start_time()); to_add->set_affects_active_elements(false); keyframe_effect_impl->AddKeyframeModel(std::move(to_add)); @@ -655,13 +613,13 @@ void KeyframeEffect::PushNewKeyframeModelsToImplThread( } namespace { -bool IsCompleted(KeyframeModel* keyframe_model, +bool IsCompleted(gfx::KeyframeModel* keyframe_model, const KeyframeEffect* main_thread_keyframe_effect) { - if (keyframe_model->is_impl_only()) { + if (KeyframeModel::ToCcKeyframeModel(keyframe_model)->is_impl_only()) { return (keyframe_model->run_state() == gfx::KeyframeModel::WAITING_FOR_DELETION); } else { - KeyframeModel* main_thread_keyframe_model = + gfx::KeyframeModel* main_thread_keyframe_model = main_thread_keyframe_effect->GetKeyframeModelById(keyframe_model->id()); return !main_thread_keyframe_model || main_thread_keyframe_model->is_finished(); @@ -677,10 +635,10 @@ void KeyframeEffect::RemoveKeyframeModelsCompletedOnMainThread( // elements, and should stop affecting active elements after the next call // to ActivateKeyframeEffects. If already WAITING_FOR_DELETION, they can be // removed immediately. - for (const std::unique_ptr<KeyframeModel>& keyframe_model : - keyframe_effect_impl->keyframe_models_) { + for (auto& keyframe_model : keyframe_effect_impl->keyframe_models()) { if (IsCompleted(keyframe_model.get(), this)) { - keyframe_model->set_affects_pending_elements(false); + KeyframeModel::ToCcKeyframeModel(keyframe_model.get()) + ->set_affects_pending_elements(false); keyframe_model_completed = true; } } @@ -728,11 +686,12 @@ void KeyframeEffect::PushPropertiesTo(KeyframeEffect* keyframe_effect_impl) { // Now that the keyframe model lists are synchronized, push the properties for // the individual KeyframeModels. - for (const auto& keyframe_model : keyframe_models_) { - KeyframeModel* current_impl = - keyframe_effect_impl->GetKeyframeModelById(keyframe_model->id()); + for (const auto& keyframe_model : keyframe_models()) { + KeyframeModel* current_impl = KeyframeModel::ToCcKeyframeModel( + keyframe_effect_impl->GetKeyframeModelById(keyframe_model->id())); if (current_impl) - keyframe_model->PushPropertiesTo(current_impl); + KeyframeModel::ToCcKeyframeModel(keyframe_model.get()) + ->PushPropertiesTo(current_impl); } keyframe_effect_impl->UpdateTickingState(); @@ -740,17 +699,18 @@ void KeyframeEffect::PushPropertiesTo(KeyframeEffect* keyframe_effect_impl) { std::string KeyframeEffect::KeyframeModelsToString() const { std::string str; - for (size_t i = 0; i < keyframe_models_.size(); i++) { + for (size_t i = 0; i < keyframe_models().size(); i++) { if (i > 0) str.append(", "); - str.append(keyframe_models_[i]->ToString()); + str.append(KeyframeModel::ToCcKeyframeModel(keyframe_models()[i].get()) + ->ToString()); } return str; } base::TimeDelta KeyframeEffect::MinimumTickInterval() const { base::TimeDelta min_interval = base::TimeDelta::Max(); - for (const auto& model : keyframe_models_) { + for (const auto& model : keyframe_models()) { base::TimeDelta interval = model->curve()->TickInterval(); if (interval.is_zero()) return interval; @@ -760,6 +720,30 @@ base::TimeDelta KeyframeEffect::MinimumTickInterval() const { return min_interval; } +void KeyframeEffect::RemoveKeyframeModelRange( + typename KeyframeModels::iterator to_remove_begin, + typename KeyframeModels::iterator to_remove_end) { + bool keyframe_model_removed = false; + for (auto it = to_remove_begin; it != to_remove_end; ++it) { + if ((*it)->TargetProperty() == TargetProperty::SCROLL_OFFSET) { + if (has_bound_element_animations()) + scroll_offset_animation_was_interrupted_ = true; + } else if (!(*it)->is_finished()) { + keyframe_model_removed = true; + } + } + + gfx::KeyframeEffect::RemoveKeyframeModelRange(to_remove_begin, to_remove_end); + + if (has_bound_element_animations()) { + UpdateTickingState(); + if (keyframe_model_removed) + element_animations_->UpdateClientAnimationState(); + animation_->SetNeedsCommit(); + SetNeedsPushProperties(); + } +} + void KeyframeEffect::StartKeyframeModels(base::TimeTicks monotonic_time) { DCHECK(needs_to_start_keyframe_models_); needs_to_start_keyframe_models_ = false; @@ -769,19 +753,20 @@ void KeyframeEffect::StartKeyframeModels(base::TimeTicks monotonic_time) { gfx::TargetProperties blocked_properties_for_pending_elements; std::vector<size_t> keyframe_models_waiting_for_target; - keyframe_models_waiting_for_target.reserve(keyframe_models_.size()); - for (size_t i = 0; i < keyframe_models_.size(); ++i) { - auto& keyframe_model = keyframe_models_[i]; - if (keyframe_model->run_state() == gfx::KeyframeModel::STARTING || - keyframe_model->run_state() == gfx::KeyframeModel::RUNNING) { - int property = keyframe_model->TargetProperty(); - if (keyframe_model->affects_active_elements()) { + keyframe_models_waiting_for_target.reserve(keyframe_models().size()); + for (size_t i = 0; i < keyframe_models().size(); ++i) { + auto* cc_keyframe_model = + KeyframeModel::ToCcKeyframeModel(keyframe_models()[i].get()); + if (cc_keyframe_model->run_state() == gfx::KeyframeModel::STARTING || + cc_keyframe_model->run_state() == gfx::KeyframeModel::RUNNING) { + int property = cc_keyframe_model->TargetProperty(); + if (cc_keyframe_model->affects_active_elements()) { blocked_properties_for_active_elements[property] = true; } - if (keyframe_model->affects_pending_elements()) { + if (cc_keyframe_model->affects_pending_elements()) { blocked_properties_for_pending_elements[property] = true; } - } else if (keyframe_model->run_state() == + } else if (cc_keyframe_model->run_state() == gfx::KeyframeModel::WAITING_FOR_TARGET_AVAILABILITY) { keyframe_models_waiting_for_target.push_back(i); } @@ -792,7 +777,8 @@ void KeyframeEffect::StartKeyframeModels(base::TimeTicks monotonic_time) { // should all also be in the list of KeyframeModels). size_t keyframe_model_index = keyframe_models_waiting_for_target[i]; KeyframeModel* keyframe_model_waiting_for_target = - keyframe_models_[keyframe_model_index].get(); + KeyframeModel::ToCcKeyframeModel( + keyframe_models()[keyframe_model_index].get()); // Check for the run state again even though the keyframe_model was waiting // for target because it might have changed the run state while handling // previous keyframe_model in this loop (if they belong to same group). @@ -805,15 +791,17 @@ void KeyframeEffect::StartKeyframeModels(base::TimeTicks monotonic_time) { keyframe_model_waiting_for_target->affects_pending_elements(); enqueued_properties[keyframe_model_waiting_for_target->TargetProperty()] = true; - for (size_t j = keyframe_model_index + 1; j < keyframe_models_.size(); + for (size_t j = keyframe_model_index + 1; j < keyframe_models().size(); ++j) { + auto* cc_keyframe_model = + KeyframeModel::ToCcKeyframeModel(keyframe_models()[j].get()); if (keyframe_model_waiting_for_target->group() == - keyframe_models_[j]->group()) { - enqueued_properties[keyframe_models_[j]->TargetProperty()] = true; + cc_keyframe_model->group()) { + enqueued_properties[cc_keyframe_model->TargetProperty()] = true; affects_active_elements |= - keyframe_models_[j]->affects_active_elements(); + cc_keyframe_model->affects_active_elements(); affects_pending_elements |= - keyframe_models_[j]->affects_pending_elements(); + cc_keyframe_model->affects_pending_elements(); } } @@ -846,12 +834,14 @@ void KeyframeEffect::StartKeyframeModels(base::TimeTicks monotonic_time) { if (null_intersection) { keyframe_model_waiting_for_target->SetRunState( gfx::KeyframeModel::STARTING, monotonic_time); - for (size_t j = keyframe_model_index + 1; j < keyframe_models_.size(); + for (size_t j = keyframe_model_index + 1; j < keyframe_models().size(); ++j) { + auto* cc_keyframe_model = + KeyframeModel::ToCcKeyframeModel(keyframe_models()[j].get()); if (keyframe_model_waiting_for_target->group() == - keyframe_models_[j]->group()) { - keyframe_models_[j]->SetRunState(gfx::KeyframeModel::STARTING, - monotonic_time); + cc_keyframe_model->group()) { + cc_keyframe_model->SetRunState(gfx::KeyframeModel::STARTING, + monotonic_time); } } } else { @@ -862,23 +852,27 @@ void KeyframeEffect::StartKeyframeModels(base::TimeTicks monotonic_time) { } void KeyframeEffect::PromoteStartedKeyframeModels(AnimationEvents* events) { - for (auto& keyframe_model : keyframe_models_) { + for (auto& keyframe_model : keyframe_models()) { if (keyframe_model->run_state() == gfx::KeyframeModel::STARTING && - keyframe_model->affects_active_elements()) { - keyframe_model->SetRunState(gfx::KeyframeModel::RUNNING, - last_tick_time_.value_or(base::TimeTicks())); - if (!keyframe_model->has_set_start_time() && - !keyframe_model->needs_synchronized_start_time()) - keyframe_model->set_start_time( + KeyframeModel::ToCcKeyframeModel(keyframe_model.get()) + ->affects_active_elements()) { + auto* cc_keyframe_model = + KeyframeModel::ToCcKeyframeModel(keyframe_model.get()); + cc_keyframe_model->SetRunState( + gfx::KeyframeModel::RUNNING, + last_tick_time_.value_or(base::TimeTicks())); + if (!cc_keyframe_model->has_set_start_time() && + !cc_keyframe_model->needs_synchronized_start_time()) + cc_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(); + if (cc_keyframe_model->has_set_start_time()) + start_time = cc_keyframe_model->start_time(); else start_time = last_tick_time_.value_or(base::TimeTicks()); - GenerateEvent(events, *keyframe_model, AnimationEvent::STARTED, + GenerateEvent(events, *cc_keyframe_model, AnimationEvent::STARTED, start_time); } } @@ -899,50 +893,52 @@ void KeyframeEffect::MarkKeyframeModelsForDeletion( // we don't have an events vector, we must ensure that non-aborted // KeyframeModels have received a finished event before marking them for // deletion. - for (size_t i = 0; i < keyframe_models_.size(); i++) { - KeyframeModel* keyframe_model = keyframe_models_[i].get(); - if (keyframe_model->run_state() == gfx::KeyframeModel::ABORTED) { - GenerateEvent(events, *keyframe_model, AnimationEvent::ABORTED, + for (auto& keyframe_model : keyframe_models()) { + KeyframeModel* cc_keyframe_model = + KeyframeModel::ToCcKeyframeModel(keyframe_model.get()); + if (cc_keyframe_model->run_state() == gfx::KeyframeModel::ABORTED) { + GenerateEvent(events, *cc_keyframe_model, AnimationEvent::ABORTED, monotonic_time); // If this is the controlling instance or it has already received finish // event, keyframe model can be marked for deletion. - if (!NeedsFinishedEvent(keyframe_model)) - MarkForDeletion(keyframe_model); + if (!NeedsFinishedEvent(cc_keyframe_model)) + MarkForDeletion(cc_keyframe_model); continue; } // If this is an aborted controlling instance that need completion on the // main thread, generate takeover event. - if (keyframe_model->is_controlling_instance() && - keyframe_model->run_state() == + if (cc_keyframe_model->is_controlling_instance() && + cc_keyframe_model->run_state() == gfx::KeyframeModel::ABORTED_BUT_NEEDS_COMPLETION) { - GenerateTakeoverEventForScrollAnimation(events, *keyframe_model, + GenerateTakeoverEventForScrollAnimation(events, *cc_keyframe_model, monotonic_time); // Remove the keyframe model from the impl thread. - MarkForDeletion(keyframe_model); + MarkForDeletion(cc_keyframe_model); continue; } - if (keyframe_model->run_state() != gfx::KeyframeModel::FINISHED) + if (cc_keyframe_model->run_state() != gfx::KeyframeModel::FINISHED) continue; // Since deleting an animation on the main thread leads to its deletion // on the impl thread, we only mark a FINISHED main thread animation for // deletion once it has received a FINISHED event from the impl thread. - if (NeedsFinishedEvent(keyframe_model)) + if (NeedsFinishedEvent(cc_keyframe_model)) continue; // If a keyframe model is finished, and not already marked for deletion, // find out if all other keyframe models in the same group are also // finished. std::vector<size_t> keyframe_models_in_same_group = - FindAnimationsWithSameGroupId(keyframe_models_, - keyframe_model->group()); + FindAnimationsWithSameGroupId(keyframe_models(), + cc_keyframe_model->group()); bool a_keyframe_model_in_same_group_is_not_finished = std::any_of( keyframe_models_in_same_group.cbegin(), keyframe_models_in_same_group.cend(), [&](size_t index) { - KeyframeModel* keyframe_model = keyframe_models_[index].get(); + auto* keyframe_model = + KeyframeModel::ToCcKeyframeModel(keyframe_models()[index].get()); return !keyframe_model->is_finished() || (keyframe_model->run_state() == gfx::KeyframeModel::FINISHED && NeedsFinishedEvent(keyframe_model)); @@ -956,7 +952,8 @@ void KeyframeEffect::MarkKeyframeModelsForDeletion( // ensures we don't try to delete them again. for (size_t j = 0; j < keyframe_models_in_same_group.size(); ++j) { KeyframeModel* same_group_keyframe_model = - keyframe_models_[keyframe_models_in_same_group[j]].get(); + KeyframeModel::ToCcKeyframeModel( + keyframe_models()[keyframe_models_in_same_group[j]].get()); // Skip any keyframe model in this group which is already processed. if (same_group_keyframe_model->run_state() == @@ -981,15 +978,18 @@ void KeyframeEffect::MarkFinishedKeyframeModels( DCHECK(has_bound_element_animations()); bool keyframe_model_finished = false; - for (auto& keyframe_model : keyframe_models_) { + for (auto& keyframe_model : keyframe_models()) { if (!keyframe_model->is_finished() && keyframe_model->IsFinishedAt(monotonic_time)) { keyframe_model->SetRunState(gfx::KeyframeModel::FINISHED, monotonic_time); keyframe_model_finished = true; SetNeedsPushProperties(); } - if (!keyframe_model->affects_active_elements() && - !keyframe_model->affects_pending_elements()) { + auto* cc_keyframe_model = + KeyframeModel::ToCcKeyframeModel(keyframe_model.get()); + + if (!cc_keyframe_model->affects_active_elements() && + !cc_keyframe_model->affects_pending_elements()) { switch (keyframe_model->run_state()) { case gfx::KeyframeModel::WAITING_FOR_TARGET_AVAILABILITY: case gfx::KeyframeModel::STARTING: @@ -1030,7 +1030,8 @@ void KeyframeEffect::GenerateEvent(AnimationEvents* events, animation_->id(), keyframe_model.id()}, keyframe_model.group(), keyframe_model.TargetProperty(), monotonic_time); - event.is_impl_only = keyframe_model.is_impl_only(); + event.is_impl_only = + KeyframeModel::ToCcKeyframeModel(&keyframe_model)->is_impl_only(); if (!event.is_impl_only) { events->events_.push_back(event); return; diff --git a/chromium/cc/animation/keyframe_effect.h b/chromium/cc/animation/keyframe_effect.h index 9e511add1f8..2cf4079c392 100644 --- a/chromium/cc/animation/keyframe_effect.h +++ b/chromium/cc/animation/keyframe_effect.h @@ -18,6 +18,7 @@ #include "cc/paint/element_id.h" #include "cc/trees/mutator_host_client.h" #include "cc/trees/target_property.h" +#include "ui/gfx/animation/keyframe/keyframe_effect.h" #include "ui/gfx/geometry/box_f.h" #include "ui/gfx/geometry/scroll_offset.h" @@ -38,7 +39,7 @@ struct PropertyAnimationState; // KeyframeModels. The commonality between keyframe models on the same target // is found via ElementAnimations - there is only one ElementAnimations for a // given target. -class CC_ANIMATION_EXPORT KeyframeEffect { +class CC_ANIMATION_EXPORT KeyframeEffect : public gfx::KeyframeEffect { public: explicit KeyframeEffect(Animation* animation); KeyframeEffect(const KeyframeEffect&) = delete; @@ -58,7 +59,7 @@ class CC_ANIMATION_EXPORT KeyframeEffect { ElementId element_id() const { return element_id_; } // Returns true if there are any KeyframeModels at all to process. - bool has_any_keyframe_model() const { return !keyframe_models_.empty(); } + bool has_any_keyframe_model() const { return !keyframe_models().empty(); } // When a scroll animation is removed on the main thread, its compositor // thread counterpart continues producing scroll deltas until activation. @@ -80,9 +81,7 @@ class CC_ANIMATION_EXPORT KeyframeEffect { void AttachElement(ElementId element_id); void DetachElement(); - virtual void Tick(base::TimeTicks monotonic_time); - static void TickKeyframeModel(base::TimeTicks monotonic_time, - KeyframeModel* keyframe_model); + void Tick(base::TimeTicks monotonic_time) override; void RemoveFromTicking(); void UpdateState(bool start_ready_keyframe_models, AnimationEvents* events); @@ -91,9 +90,9 @@ class CC_ANIMATION_EXPORT KeyframeEffect { void Pause(base::TimeDelta pause_offset, PauseCondition = PauseCondition::kUnconditional); - void AddKeyframeModel(std::unique_ptr<KeyframeModel> keyframe_model); + void AddKeyframeModel( + std::unique_ptr<gfx::KeyframeModel> keyframe_model) override; void PauseKeyframeModel(int keyframe_model_id, base::TimeDelta time_offset); - void RemoveKeyframeModel(int keyframe_model_id); void AbortKeyframeModel(int keyframe_model_id); void AbortKeyframeModelsWithProperty(TargetProperty::Type target_property, bool needs_completion); @@ -132,9 +131,6 @@ class CC_ANIMATION_EXPORT KeyframeEffect { bool IsCurrentlyAnimatingProperty(TargetProperty::Type target_property, ElementListType list_type) const; - KeyframeModel* GetKeyframeModel(TargetProperty::Type target_property) const; - KeyframeModel* GetKeyframeModelById(int keyframe_model_id) const; - void GetPropertyAnimationState(PropertyAnimationState* pending_state, PropertyAnimationState* active_state) const; @@ -155,6 +151,14 @@ class CC_ANIMATION_EXPORT KeyframeEffect { // fast as possible. base::TimeDelta MinimumTickInterval() const; + protected: + // We override this because we have additional bookkeeping (eg, noting if + // we've aborted a scroll animation, updating ticking state, sending updates + // to the impl instance, informing |element_animations_|). + void RemoveKeyframeModelRange( + typename KeyframeModels::iterator to_remove_begin, + typename KeyframeModels::iterator to_remove_end) override; + private: void StartKeyframeModels(base::TimeTicks monotonic_time); void PromoteStartedKeyframeModels(AnimationEvents* events); @@ -174,7 +178,6 @@ class CC_ANIMATION_EXPORT KeyframeEffect { const KeyframeModel& keyframe_model, base::TimeTicks monotonic_time); - std::vector<std::unique_ptr<KeyframeModel>> keyframe_models_; Animation* animation_; ElementId element_id_; diff --git a/chromium/cc/animation/worklet_animation_unittest.cc b/chromium/cc/animation/worklet_animation_unittest.cc index b286e57d14e..7edc3f2e15f 100644 --- a/chromium/cc/animation/worklet_animation_unittest.cc +++ b/chromium/cc/animation/worklet_animation_unittest.cc @@ -215,13 +215,13 @@ TEST_F(WorkletAnimationTest, std::unique_ptr<AnimationWorkletInput> input = state->TakeWorkletState(worklet_animation_id_.worklet_id); EXPECT_EQ(0, input->added_and_updated_animations[0].current_time); - state.reset(new MutatorInputState); + state = std::make_unique<MutatorInputState>(); worklet_animation->UpdateInputState(state.get(), second_ticks, scroll_tree, true); input = state->TakeWorkletState(worklet_animation_id_.worklet_id); EXPECT_EQ(123.4, input->updated_animations[0].current_time); // Should always offset from start time. - state.reset(new MutatorInputState()); + state = std::make_unique<MutatorInputState>(); worklet_animation->UpdateInputState(state.get(), third_ticks, scroll_tree, true); input = state->TakeWorkletState(worklet_animation_id_.worklet_id); @@ -255,7 +255,7 @@ TEST_F(WorkletAnimationTest, DocumentTimelineSetPlaybackRate) { // Start the animation. worklet_animation->UpdateInputState(state.get(), first_ticks, scroll_tree, true); - state.reset(new MutatorInputState); + state = std::make_unique<MutatorInputState>(); // Play until second_ticks. worklet_animation->UpdateInputState(state.get(), second_ticks, scroll_tree, @@ -270,7 +270,7 @@ TEST_F(WorkletAnimationTest, DocumentTimelineSetPlaybackRate) { // Update the playback rate. worklet_animation->SetPlaybackRateForTesting(playback_rate_half); - state.reset(new MutatorInputState()); + state = std::make_unique<MutatorInputState>(); // Play until third_ticks. worklet_animation->UpdateInputState(state.get(), third_ticks, scroll_tree, @@ -319,7 +319,7 @@ TEST_F(WorkletAnimationTest, ScrollTimelineSetPlaybackRate) { // Update the playback rate. worklet_animation->SetPlaybackRateForTesting(playback_rate_half); - state.reset(new MutatorInputState()); + state = std::make_unique<MutatorInputState>(); // Continue playing the animation. EXPECT_CALL(*mock_timeline, IsActive(_, _)).WillRepeatedly(Return(true)); @@ -362,7 +362,7 @@ TEST_F(WorkletAnimationTest, InactiveScrollTimeline) { std::unique_ptr<AnimationWorkletInput> input = state->TakeWorkletState(worklet_animation_id_.worklet_id); EXPECT_FALSE(input); - state.reset(new MutatorInputState()); + state = std::make_unique<MutatorInputState>(); // Now the timeline is active. EXPECT_CALL(*mock_timeline, IsActive(_, _)).WillRepeatedly(Return(true)); @@ -376,7 +376,7 @@ TEST_F(WorkletAnimationTest, InactiveScrollTimeline) { // Verify that the current time is updated when the timeline becomes newly // active. EXPECT_EQ(100, input->added_and_updated_animations[0].current_time); - state.reset(new MutatorInputState()); + state = std::make_unique<MutatorInputState>(); // Now the timeline is inactive. EXPECT_CALL(*mock_timeline, IsActive(_, _)).WillRepeatedly(Return(false)); @@ -421,7 +421,7 @@ TEST_F(WorkletAnimationTest, UpdateInputStateProducesCorrectState) { // The state of WorkletAnimation is updated to RUNNING after calling // UpdateInputState above. - state.reset(new MutatorInputState()); + state = std::make_unique<MutatorInputState>(); time += base::TimeDelta::FromSecondsD(0.1); worklet_animation_->UpdateInputState(state.get(), time, scroll_tree, true); input = state->TakeWorkletState(worklet_animation_id_.worklet_id); @@ -432,7 +432,7 @@ TEST_F(WorkletAnimationTest, UpdateInputStateProducesCorrectState) { // Operating on individual KeyframeModel doesn't affect the state of // WorkletAnimation. keyframe_model->SetRunState(KeyframeModel::FINISHED, time); - state.reset(new MutatorInputState()); + state = std::make_unique<MutatorInputState>(); time += base::TimeDelta::FromSecondsD(0.1); worklet_animation_->UpdateInputState(state.get(), time, scroll_tree, true); input = state->TakeWorkletState(worklet_animation_id_.worklet_id); @@ -444,7 +444,7 @@ TEST_F(WorkletAnimationTest, UpdateInputStateProducesCorrectState) { // leads to RemoveKeyframeModel. worklet_animation_->RemoveKeyframeModel(keyframe_model_id); worklet_animation_->UpdateState(true, nullptr); - state.reset(new MutatorInputState()); + state = std::make_unique<MutatorInputState>(); worklet_animation_->UpdateInputState(state.get(), time, scroll_tree, true); input = state->TakeWorkletState(worklet_animation_id_.worklet_id); EXPECT_EQ(input->added_and_updated_animations.size(), 0u); @@ -475,20 +475,20 @@ TEST_F(WorkletAnimationTest, SkipUnchangedAnimations) { EXPECT_EQ(input->added_and_updated_animations.size(), 1u); EXPECT_EQ(input->updated_animations.size(), 0u); - state.reset(new MutatorInputState()); + state = std::make_unique<MutatorInputState>(); // No update on the input state if input time stays the same. worklet_animation_->UpdateInputState(state.get(), time, scroll_tree, true); input = state->TakeWorkletState(worklet_animation_id_.worklet_id); EXPECT_FALSE(input); - state.reset(new MutatorInputState()); + state = std::make_unique<MutatorInputState>(); // Different input time causes the input state to be updated. time += base::TimeDelta::FromSecondsD(0.1); worklet_animation_->UpdateInputState(state.get(), time, scroll_tree, true); input = state->TakeWorkletState(worklet_animation_id_.worklet_id); EXPECT_EQ(input->updated_animations.size(), 1u); - state.reset(new MutatorInputState()); + state = std::make_unique<MutatorInputState>(); // Input state gets updated when the worklet animation is to be removed even // the input time doesn't change. worklet_animation_->RemoveKeyframeModel(keyframe_model_id); @@ -528,20 +528,20 @@ TEST_F(WorkletAnimationTest, SkipLockedAnimations) { EXPECT_EQ(input->added_and_updated_animations.size(), 1u); EXPECT_EQ(input->updated_animations.size(), 0u); - state.reset(new MutatorInputState()); + state = std::make_unique<MutatorInputState>(); // Different scroll time causes the input state to be updated. worklet_animation->UpdateInputState(state.get(), time, scroll_tree, true); input = state->TakeWorkletState(worklet_animation_id_.worklet_id); EXPECT_EQ(input->updated_animations.size(), 1u); - state.reset(new MutatorInputState()); + state = std::make_unique<MutatorInputState>(); // Different scroll time causes the input state to be updated. Pending // mutation will grab a lock. worklet_animation->UpdateInputState(state.get(), time, scroll_tree, false); input = state->TakeWorkletState(worklet_animation_id_.worklet_id); EXPECT_EQ(input->updated_animations.size(), 1u); - state.reset(new MutatorInputState()); + state = std::make_unique<MutatorInputState>(); // Pending lock has not been released. worklet_animation->UpdateInputState(state.get(), time, scroll_tree, true); input = state->TakeWorkletState(worklet_animation_id_.worklet_id); @@ -549,7 +549,7 @@ TEST_F(WorkletAnimationTest, SkipLockedAnimations) { worklet_animation->ReleasePendingTreeLock(); - state.reset(new MutatorInputState()); + state = std::make_unique<MutatorInputState>(); // Pending lock has been released. worklet_animation->UpdateInputState(state.get(), time, scroll_tree, true); input = state->TakeWorkletState(worklet_animation_id_.worklet_id); diff --git a/chromium/cc/base/devtools_instrumentation.cc b/chromium/cc/base/devtools_instrumentation.cc index b65c1546655..d0210ca1a5d 100644 --- a/chromium/cc/base/devtools_instrumentation.cc +++ b/chromium/cc/base/devtools_instrumentation.cc @@ -4,6 +4,8 @@ #include "cc/base/devtools_instrumentation.h" +#include <string> + namespace cc { namespace devtools_instrumentation { namespace { @@ -36,6 +38,7 @@ const char kFrameId[] = "frameId"; const char kLayerId[] = "layerId"; const char kLayerTreeId[] = "layerTreeId"; const char kPixelRefId[] = "pixelRefId"; +const char kPresentationTimestamp[] = "presentationTimestamp"; const char kImageUploadTask[] = "ImageUploadTask"; const char kImageDecodeTask[] = "ImageDecodeTask"; @@ -69,6 +72,9 @@ ScopedImageUploadTask::~ScopedImageUploadTask() { auto duration = base::TimeTicks::Now() - start_time_; const char* histogram_name = nullptr; switch (image_type_) { + case ImageType::kJxl: + histogram_name = "Renderer4.ImageUploadTaskDurationUs.Jxl"; + break; case ImageType::kAvif: histogram_name = "Renderer4.ImageUploadTaskDurationUs.Avif"; break; @@ -118,6 +124,9 @@ ScopedImageDecodeTask::~ScopedImageDecodeTask() { auto duration = base::TimeTicks::Now() - start_time_; const char* histogram_name = nullptr; switch (image_type_) { + case ImageType::kJxl: + histogram_name = "Renderer4.ImageUploadTaskDurationUs.Jxl"; + break; case ImageType::kAvif: histogram_name = "Renderer4.ImageDecodeTaskDurationUs.Avif"; break; diff --git a/chromium/cc/base/devtools_instrumentation.h b/chromium/cc/base/devtools_instrumentation.h index 02f0254e25f..46327e9aff8 100644 --- a/chromium/cc/base/devtools_instrumentation.h +++ b/chromium/cc/base/devtools_instrumentation.h @@ -8,6 +8,7 @@ #include <stdint.h> #include <memory> +#include <utility> #include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" @@ -32,6 +33,7 @@ CC_BASE_EXPORT extern const char kFrameId[]; CC_BASE_EXPORT extern const char kLayerId[]; CC_BASE_EXPORT extern const char kLayerTreeId[]; CC_BASE_EXPORT extern const char kPixelRefId[]; +CC_BASE_EXPORT extern const char kPresentationTimestamp[]; CC_BASE_EXPORT extern const char kImageDecodeTask[]; CC_BASE_EXPORT extern const char kBeginFrame[]; @@ -67,7 +69,7 @@ class CC_BASE_EXPORT ScopedLayerTask { class CC_BASE_EXPORT ScopedImageTask { public: - enum ImageType { kAvif, kBmp, kGif, kIco, kJpeg, kPng, kWebP, kOther }; + enum ImageType { kJxl, kAvif, kBmp, kGif, kIco, kJpeg, kPng, kWebP, kOther }; explicit ScopedImageTask(ImageType image_type) : image_type_(image_type), start_time_(base::TimeTicks::Now()) {} @@ -181,10 +183,31 @@ inline void CC_BASE_EXPORT DidBeginFrame(int layer_tree_host_id) { internal::kLayerTreeId, layer_tree_host_id); } -inline void CC_BASE_EXPORT DidDrawFrame(int layer_tree_host_id) { - TRACE_EVENT_INSTANT1(internal::CategoryName::kTimelineFrame, - internal::kDrawFrame, TRACE_EVENT_SCOPE_THREAD, - internal::kLayerTreeId, layer_tree_host_id); +constexpr uint64_t GetUniqueIDFromLayerTreeHostIdAndFrameToken( + int layer_tree_host_id, + uint32_t frame_token) { + return static_cast<uint64_t>(layer_tree_host_id) << 32 | + static_cast<uint64_t>(frame_token); +} + +inline void CC_BASE_EXPORT DidDrawFrame(int layer_tree_host_id, + uint32_t frame_token) { + TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(internal::CategoryName::kTimelineFrame, + internal::kDrawFrame, + GetUniqueIDFromLayerTreeHostIdAndFrameToken( + layer_tree_host_id, frame_token), + internal::kLayerTreeId, layer_tree_host_id); +} + +inline void CC_BASE_EXPORT +DidPresentFrame(int layer_tree_host_id, + uint32_t frame_token, + base::TimeTicks presentation_timestamp) { + TRACE_EVENT_NESTABLE_ASYNC_END1( + internal::CategoryName::kTimelineFrame, internal::kDrawFrame, + GetUniqueIDFromLayerTreeHostIdAndFrameToken(layer_tree_host_id, + frame_token), + internal::kPresentationTimestamp, presentation_timestamp); } inline void CC_BASE_EXPORT DidRequestMainThreadFrame(int layer_tree_host_id) { diff --git a/chromium/cc/base/features.cc b/chromium/cc/base/features.cc index 5e95d7db995..9b1496fbb33 100644 --- a/chromium/cc/base/features.cc +++ b/chromium/cc/base/features.cc @@ -4,6 +4,7 @@ #include "cc/base/features.h" +#include "base/feature_list.h" #include "build/build_config.h" namespace features { @@ -34,36 +35,23 @@ const base::Feature kSynchronizedScrolling = { base::FEATURE_ENABLED_BY_DEFAULT}; #endif -#if !defined(OS_ANDROID) -// Enables latency recovery on the impl thread. -const base::Feature kImplLatencyRecovery = {"ImplLatencyRecovery", - base::FEATURE_DISABLED_BY_DEFAULT}; - -// Enables latency recovery on the main thread. -const base::Feature kMainLatencyRecovery = {"MainLatencyRecovery", - base::FEATURE_DISABLED_BY_DEFAULT}; -#endif // !defined(OS_ANDROID) - bool IsImplLatencyRecoveryEnabled() { -#if defined(OS_ANDROID) - // TODO(crbug.com/933846): LatencyRecovery is causing jank on Android. Disable - // for now, with plan to disable more widely on all platforms. + // TODO(crbug.com/1142598): Latency recovery has been disabled by default + // since M87. For now, only the flag is removed. If all goes well, remove the + // code supporting latency recovery. return false; -#else - return base::FeatureList::IsEnabled(kImplLatencyRecovery); -#endif } bool IsMainLatencyRecoveryEnabled() { -#if defined(OS_ANDROID) - // TODO(crbug.com/933846): LatencyRecovery is causing jank on Android. Disable - // for now, with plan to disable more widely on all platforms. + // TODO(crbug.com/1142598): Latency recovery has been disabled by default + // since M87. For now, only the flag is removed. If all goes well, remove the + // code supporting latency recovery. return false; -#else - return base::FeatureList::IsEnabled(kMainLatencyRecovery); -#endif } +const base::Feature kRemoveMobileViewportDoubleTap{ + "RemoveMobileViewportDoubleTap", base::FEATURE_ENABLED_BY_DEFAULT}; + const base::Feature kScrollUnification{"ScrollUnification", base::FEATURE_DISABLED_BY_DEFAULT}; @@ -71,11 +59,19 @@ const base::Feature kSchedulerSmoothnessForAnimatedScrolls{ "SmoothnessModeForAnimatedScrolls", base::FEATURE_DISABLED_BY_DEFAULT}; const base::Feature kWheelEventRegions{"WheelEventRegions", - base::FEATURE_DISABLED_BY_DEFAULT}; + base::FEATURE_ENABLED_BY_DEFAULT}; const base::Feature kHudDisplayForPerformanceMetrics{ "HudDisplayForPerformanceMetrics", base::FEATURE_DISABLED_BY_DEFAULT}; const base::Feature kJankInjectionAblationFeature{ "JankInjectionAblation", base::FEATURE_DISABLED_BY_DEFAULT}; + +const base::Feature kDocumentTransition{"DocumentTransition", + base::FEATURE_DISABLED_BY_DEFAULT}; + +bool IsDocumentTransitionEnabled() { + return base::FeatureList::IsEnabled(kDocumentTransition); +} + } // namespace features diff --git a/chromium/cc/base/features.h b/chromium/cc/base/features.h index 8fef93721ec..6068e0b46fa 100644 --- a/chromium/cc/base/features.h +++ b/chromium/cc/base/features.h @@ -15,14 +15,14 @@ CC_BASE_EXPORT extern const base::Feature kAnimatedImageResume; CC_BASE_EXPORT extern const base::Feature kImpulseScrollAnimations; CC_BASE_EXPORT extern const base::Feature kSynchronizedScrolling; -#if !defined(OS_ANDROID) -CC_BASE_EXPORT extern const base::Feature kImplLatencyRecovery; -CC_BASE_EXPORT extern const base::Feature kMainLatencyRecovery; -#endif // !defined(OS_ANDROID) - CC_BASE_EXPORT bool IsImplLatencyRecoveryEnabled(); CC_BASE_EXPORT bool IsMainLatencyRecoveryEnabled(); +// When enabled, the double tap to zoom will be disabled when the viewport +// meta tag is properly set for mobile using content=width=device-width +// or content=initial-scale=1.0 +CC_BASE_EXPORT extern const base::Feature kRemoveMobileViewportDoubleTap; + // 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 @@ -50,6 +50,13 @@ CC_BASE_EXPORT extern const base::Feature kHudDisplayForPerformanceMetrics; // When enabled, some jank is injected to the animation/scrolling pipeline. CC_BASE_EXPORT extern const base::Feature kJankInjectionAblationFeature; +// Controls the DocumentTransition feature. More information at +// third_party/blink/renderer/core/document_transition/README.md +CC_BASE_EXPORT extern const base::Feature kDocumentTransition; + +// Helper for DocumentTransition feature. +CC_BASE_EXPORT bool IsDocumentTransitionEnabled(); + } // namespace features #endif // CC_BASE_FEATURES_H_ diff --git a/chromium/cc/base/math_util.cc b/chromium/cc/base/math_util.cc index 6af60ed1cf4..26a7156d9b4 100644 --- a/chromium/cc/base/math_util.cc +++ b/chromium/cc/base/math_util.cc @@ -280,6 +280,7 @@ gfx::QuadF MathUtil::InverseMapQuadToLocalSpace( const gfx::QuadF& device_quad) { gfx::Transform inverse_device_transform(gfx::Transform::kSkipInitialization); DCHECK(device_transform.IsInvertible()); + DCHECK(device_transform.IsFlat()); bool did_invert = device_transform.GetInverse(&inverse_device_transform); DCHECK(did_invert); bool clipped = false; diff --git a/chromium/cc/document_transition/document_transition_request.cc b/chromium/cc/document_transition/document_transition_request.cc index 220d097f1ff..408d1112210 100644 --- a/chromium/cc/document_transition/document_transition_request.cc +++ b/chromium/cc/document_transition/document_transition_request.cc @@ -4,13 +4,17 @@ #include "cc/document_transition/document_transition_request.h" +#include <map> #include <memory> #include <sstream> #include <utility> +#include <vector> #include "base/callback.h" #include "base/memory/ptr_util.h" +#include "cc/document_transition/document_transition_shared_element_id.h" #include "components/viz/common/quads/compositor_frame_transition_directive.h" +#include "components/viz/common/quads/compositor_render_pass.h" namespace cc { namespace { @@ -63,51 +67,68 @@ uint32_t DocumentTransitionRequest::s_next_sequence_id_ = 1; // static std::unique_ptr<DocumentTransitionRequest> DocumentTransitionRequest::CreatePrepare(Effect effect, - base::TimeDelta duration, + uint32_t document_tag, + uint32_t shared_element_count, base::OnceClosure commit_callback) { return base::WrapUnique(new DocumentTransitionRequest( - effect, duration, std::move(commit_callback))); + effect, document_tag, shared_element_count, std::move(commit_callback))); } // static std::unique_ptr<DocumentTransitionRequest> -DocumentTransitionRequest::CreateStart(base::OnceClosure commit_callback) { - return base::WrapUnique( - new DocumentTransitionRequest(std::move(commit_callback))); +DocumentTransitionRequest::CreateStart(uint32_t document_tag, + uint32_t shared_element_count, + base::OnceClosure commit_callback) { + return base::WrapUnique(new DocumentTransitionRequest( + document_tag, shared_element_count, std::move(commit_callback))); } DocumentTransitionRequest::DocumentTransitionRequest( Effect effect, - base::TimeDelta duration, + uint32_t document_tag, + uint32_t shared_element_count, base::OnceClosure commit_callback) : type_(Type::kSave), effect_(effect), - duration_(duration), - commit_callback_(std::move(commit_callback)) {} + document_tag_(document_tag), + shared_element_count_(shared_element_count), + commit_callback_(std::move(commit_callback)), + sequence_id_(s_next_sequence_id_++) {} DocumentTransitionRequest::DocumentTransitionRequest( + uint32_t document_tag, + uint32_t shared_element_count, base::OnceClosure commit_callback) - : type_(Type::kAnimate), commit_callback_(std::move(commit_callback)) {} + : type_(Type::kAnimate), + document_tag_(document_tag), + shared_element_count_(shared_element_count), + commit_callback_(std::move(commit_callback)), + sequence_id_(s_next_sequence_id_++) {} DocumentTransitionRequest::~DocumentTransitionRequest() = default; viz::CompositorFrameTransitionDirective -DocumentTransitionRequest::ConstructDirective() const { - // Note that the clamped_duration is also verified at - // CompositorFrameTransitionDirective deserialization time. - auto clamped_duration = - duration_ < viz::CompositorFrameTransitionDirective::kMaxDuration - ? duration_ - : viz::CompositorFrameTransitionDirective::kMaxDuration; - return viz::CompositorFrameTransitionDirective(s_next_sequence_id_++, type_, - effect_, clamped_duration); +DocumentTransitionRequest::ConstructDirective( + const std::map<DocumentTransitionSharedElementId, + viz::CompositorRenderPassId>& + shared_element_render_pass_id_map) const { + std::vector<viz::CompositorRenderPassId> shared_passes(shared_element_count_); + for (uint32_t i = 0; i < shared_passes.size(); ++i) { + auto it = shared_element_render_pass_id_map.find( + DocumentTransitionSharedElementId{document_tag_, i}); + if (it == shared_element_render_pass_id_map.end()) + continue; + shared_passes[i] = it->second; + } + return viz::CompositorFrameTransitionDirective(sequence_id_, type_, effect_, + std::move(shared_passes)); } std::string DocumentTransitionRequest::ToString() const { std::ostringstream str; str << "[type: " << TypeToString(type_) << " effect: " << EffectToString(effect_) - << " duration: " << duration_.InMillisecondsF() << "ms]"; + << " sequence_id: " << sequence_id_ << "]"; return str.str(); } diff --git a/chromium/cc/document_transition/document_transition_request.h b/chromium/cc/document_transition/document_transition_request.h index dddfd4a9fd9..2f2ecdd2012 100644 --- a/chromium/cc/document_transition/document_transition_request.h +++ b/chromium/cc/document_transition/document_transition_request.h @@ -5,13 +5,16 @@ #ifndef CC_DOCUMENT_TRANSITION_DOCUMENT_TRANSITION_REQUEST_H_ #define CC_DOCUMENT_TRANSITION_DOCUMENT_TRANSITION_REQUEST_H_ +#include <map> #include <memory> #include <string> #include <utility> #include "base/callback.h" #include "cc/cc_export.h" +#include "cc/document_transition/document_transition_shared_element_id.h" #include "components/viz/common/quads/compositor_frame_transition_directive.h" +#include "components/viz/common/quads/compositor_render_pass.h" namespace cc { @@ -25,11 +28,14 @@ class CC_EXPORT DocumentTransitionRequest { // Creates a Type::kPrepare type of request. static std::unique_ptr<DocumentTransitionRequest> CreatePrepare( Effect effect, - base::TimeDelta duration, + uint32_t document_tag, + uint32_t shared_element_count, base::OnceClosure commit_callback); // Creates a Type::kSave type of request. static std::unique_ptr<DocumentTransitionRequest> CreateStart( + uint32_t document_tag, + uint32_t shared_element_count, base::OnceClosure commit_callback); DocumentTransitionRequest(DocumentTransitionRequest&) = delete; @@ -37,17 +43,24 @@ class CC_EXPORT DocumentTransitionRequest { DocumentTransitionRequest& operator=(DocumentTransitionRequest&) = delete; - // The callback is run when the request is committed from the main thread onto - // the compositor thread. This is used to indicate that the request has been - // submitted for processing and that script may now change the page in some - // way. In other words, this callback would resolve the prepare promise that - // script may be waiting for. - base::OnceClosure TakeCommitCallback() { return std::move(commit_callback_); } + // The callback is run when the request is sufficiently processed for us to be + // able to begin the next step in the animation. In other words, when this + // callback is invoked it can resolve a script promise that is gating this + // step. + base::OnceClosure TakeFinishedCallback() { + return std::move(commit_callback_); + } // This constructs a viz directive. Note that repeated calls to this function // would create a new sequence id for the directive, which means it would be // processed again by viz. - viz::CompositorFrameTransitionDirective ConstructDirective() const; + viz::CompositorFrameTransitionDirective ConstructDirective( + const std::map<DocumentTransitionSharedElementId, + viz::CompositorRenderPassId>& + shared_element_render_pass_id_map) const; + + // Returns the sequence id for this request. + uint32_t sequence_id() const { return sequence_id_; } // Testing / debugging functionality. std::string ToString() const; @@ -56,14 +69,19 @@ class CC_EXPORT DocumentTransitionRequest { using Type = viz::CompositorFrameTransitionDirective::Type; DocumentTransitionRequest(Effect effect, - base::TimeDelta duration, + uint32_t document_tag, + uint32_t shared_element_count, base::OnceClosure commit_callback); - explicit DocumentTransitionRequest(base::OnceClosure commit_callback); + explicit DocumentTransitionRequest(uint32_t document_tag, + uint32_t shared_element_count, + base::OnceClosure commit_callback); const Type type_; const Effect effect_ = Effect::kNone; - const base::TimeDelta duration_; + const uint32_t document_tag_; + const uint32_t shared_element_count_; base::OnceClosure commit_callback_; + const uint32_t sequence_id_; static uint32_t s_next_sequence_id_; }; diff --git a/chromium/cc/document_transition/document_transition_request_unittest.cc b/chromium/cc/document_transition/document_transition_request_unittest.cc index e5001d1f2df..bd686565d50 100644 --- a/chromium/cc/document_transition/document_transition_request_unittest.cc +++ b/chromium/cc/document_transition/document_transition_request_unittest.cc @@ -17,54 +17,39 @@ TEST(DocumentTransitionRequestTest, PrepareRequest) { auto request = DocumentTransitionRequest::CreatePrepare( DocumentTransitionRequest::Effect::kRevealLeft, - base::TimeDelta::FromMilliseconds(123), std::move(callback)); + /*document_tag=*/0, + /*shared_element_count=*/0, std::move(callback)); EXPECT_FALSE(called); - request->TakeCommitCallback().Run(); + request->TakeFinishedCallback().Run(); EXPECT_TRUE(called); - EXPECT_TRUE(request->TakeCommitCallback().is_null()); + EXPECT_TRUE(request->TakeFinishedCallback().is_null()); - auto directive = request->ConstructDirective(); + auto directive = request->ConstructDirective({}); EXPECT_GT(directive.sequence_id(), 0u); EXPECT_EQ(DocumentTransitionRequest::Effect::kRevealLeft, directive.effect()); - EXPECT_EQ(base::TimeDelta::FromMilliseconds(123), directive.duration()); EXPECT_EQ(viz::CompositorFrameTransitionDirective::Type::kSave, directive.type()); - auto duplicate = request->ConstructDirective(); - EXPECT_GT(duplicate.sequence_id(), directive.sequence_id()); + auto duplicate = request->ConstructDirective({}); + EXPECT_EQ(duplicate.sequence_id(), directive.sequence_id()); EXPECT_EQ(duplicate.effect(), directive.effect()); - EXPECT_EQ(duplicate.duration(), directive.duration()); EXPECT_EQ(duplicate.type(), directive.type()); } -TEST(DocumentTransitionRequestTest, PrepareRequestLongDurationIsCapped) { - auto long_duration = base::TimeDelta::FromSeconds(1); - - ASSERT_GT(long_duration, - viz::CompositorFrameTransitionDirective::kMaxDuration); - - auto request = DocumentTransitionRequest::CreatePrepare( - DocumentTransitionRequest::Effect::kRevealLeft, long_duration, - base::OnceCallback<void()>()); - - auto directive = request->ConstructDirective(); - EXPECT_EQ(viz::CompositorFrameTransitionDirective::kMaxDuration, - directive.duration()); -} - TEST(DocumentTransitionRequestTest, StartRequest) { bool called = false; auto callback = base::BindLambdaForTesting([&called]() { called = true; }); - auto request = DocumentTransitionRequest::CreateStart(std::move(callback)); + auto request = DocumentTransitionRequest::CreateStart( + /*document_tag=*/0, /*shared_element_transition=*/0, std::move(callback)); EXPECT_FALSE(called); - request->TakeCommitCallback().Run(); + request->TakeFinishedCallback().Run(); EXPECT_TRUE(called); - EXPECT_TRUE(request->TakeCommitCallback().is_null()); + EXPECT_TRUE(request->TakeFinishedCallback().is_null()); - auto directive = request->ConstructDirective(); + auto directive = request->ConstructDirective({}); EXPECT_GT(directive.sequence_id(), 0u); EXPECT_EQ(viz::CompositorFrameTransitionDirective::Type::kAnimate, directive.type()); diff --git a/chromium/cc/document_transition/document_transition_shared_element_id.h b/chromium/cc/document_transition/document_transition_shared_element_id.h new file mode 100644 index 00000000000..8a94cad1c81 --- /dev/null +++ b/chromium/cc/document_transition/document_transition_shared_element_id.h @@ -0,0 +1,37 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CC_DOCUMENT_TRANSITION_DOCUMENT_TRANSITION_SHARED_ELEMENT_ID_H_ +#define CC_DOCUMENT_TRANSITION_DOCUMENT_TRANSITION_SHARED_ELEMENT_ID_H_ + +#include <stdint.h> + +#include <tuple> + +namespace cc { + +struct DocumentTransitionSharedElementId { + uint32_t document_tag = 0u; + uint32_t element_index = 0u; + + bool operator==(const DocumentTransitionSharedElementId& other) const { + return element_index == other.element_index && + document_tag == other.document_tag; + } + + bool operator!=(const DocumentTransitionSharedElementId& other) const { + return !(*this == other); + } + + bool operator<(const DocumentTransitionSharedElementId& other) const { + return std::tie(document_tag, element_index) < + std::tie(other.document_tag, other.element_index); + } + + bool valid() const { return document_tag != 0u; } +}; + +} // namespace cc + +#endif // CC_DOCUMENT_TRANSITION_DOCUMENT_TRANSITION_SHARED_ELEMENT_ID_H_ diff --git a/chromium/cc/input/browser_controls_offset_manager.cc b/chromium/cc/input/browser_controls_offset_manager.cc index 052f621a775..2f2878adc59 100644 --- a/chromium/cc/input/browser_controls_offset_manager.cc +++ b/chromium/cc/input/browser_controls_offset_manager.cc @@ -350,6 +350,8 @@ void BrowserControlsOffsetManager::OnBrowserControlsParamsChanged( top_controls_min_height_offset_ = old_browser_controls_params_.top_controls_min_height; top_min_height_change_in_progress_ = true; + SetTopMinHeightOffsetAnimationRange(top_controls_min_height_offset_, + TopControlsMinHeight()); } } else { top_controls_min_height_offset_ = TopControlsMinHeight(); @@ -363,6 +365,8 @@ void BrowserControlsOffsetManager::OnBrowserControlsParamsChanged( bottom_controls_min_height_offset_ = old_browser_controls_params_.bottom_controls_min_height; bottom_min_height_change_in_progress_ = true; + SetBottomMinHeightOffsetAnimationRange(bottom_controls_min_height_offset_, + BottomControlsMinHeight()); } } else { bottom_controls_min_height_offset_ = BottomControlsMinHeight(); @@ -509,14 +513,26 @@ gfx::Vector2dF BrowserControlsOffsetManager::Animate( float bottom_offset_delta = ContentBottomOffset() - old_bottom_offset; if (top_min_height_change_in_progress_) { - top_controls_min_height_offset_ += top_offset_delta; + // The change in top offset may be larger than the min-height, resulting in + // too low or too high |top_controls_min_height_offset_| values. So, we + // should clamp it to a valid range. + top_controls_min_height_offset_ = + base::ClampToRange(top_controls_min_height_offset_ + top_offset_delta, + top_min_height_offset_animation_range_->first, + top_min_height_offset_animation_range_->second); // Ticking the animation might reset it if it's at the final value. top_min_height_change_in_progress_ = top_controls_animation_.IsInitialized(); } if (bottom_min_height_change_in_progress_) { + // The change in bottom offset may be larger than the min-height, resulting + // in too low or too high |bottom_controls_min_height_offset_| values. So, + // we should clamp it to a valid range. + bottom_controls_min_height_offset_ = base::ClampToRange( + bottom_controls_min_height_offset_ + bottom_offset_delta, + bottom_min_height_offset_animation_range_->first, + bottom_min_height_offset_animation_range_->second); // Ticking the animation might reset it if it's at the final value. - bottom_controls_min_height_offset_ += bottom_offset_delta; bottom_min_height_change_in_progress_ = bottom_controls_animation_.IsInitialized(); } @@ -552,6 +568,8 @@ void BrowserControlsOffsetManager::ResetAnimations() { } top_min_height_change_in_progress_ = false; bottom_min_height_change_in_progress_ = false; + top_min_height_offset_animation_range_.reset(); + bottom_min_height_offset_animation_range_.reset(); } void BrowserControlsOffsetManager::SetupAnimation( @@ -663,6 +681,20 @@ void BrowserControlsOffsetManager::UpdateOldBrowserControlsParams() { BottomControlsMinHeight(); } +void BrowserControlsOffsetManager::SetTopMinHeightOffsetAnimationRange( + float from, + float to) { + top_min_height_offset_animation_range_ = + std::make_pair(std::min(from, to), std::max(from, to)); +} + +void BrowserControlsOffsetManager::SetBottomMinHeightOffsetAnimationRange( + float from, + float to) { + bottom_min_height_offset_animation_range_ = + std::make_pair(std::min(from, to), std::max(from, to)); +} + // class Animation BrowserControlsOffsetManager::Animation::Animation() {} diff --git a/chromium/cc/input/browser_controls_offset_manager.h b/chromium/cc/input/browser_controls_offset_manager.h index bd6f0438aa6..a2ce20981ba 100644 --- a/chromium/cc/input/browser_controls_offset_manager.h +++ b/chromium/cc/input/browser_controls_offset_manager.h @@ -6,6 +6,7 @@ #define CC_INPUT_BROWSER_CONTROLS_OFFSET_MANAGER_H_ #include <memory> +#include <utility> #include "base/time/time.h" #include "cc/input/browser_controls_state.h" @@ -117,6 +118,8 @@ class CC_EXPORT BrowserControlsOffsetManager { void InitAnimationForHeightChange(Animation* animation, float start_ratio, float stop_ratio); + void SetTopMinHeightOffsetAnimationRange(float from, float to); + void SetBottomMinHeightOffsetAnimationRange(float from, float to); // The client manages the lifecycle of this. BrowserControlsOffsetManagerClient* client_; @@ -156,6 +159,15 @@ class CC_EXPORT BrowserControlsOffsetManager { float top_controls_min_height_offset_; float bottom_controls_min_height_offset_; + // Minimum and maximum values |top_controls_min_height_offset_| can take + // during the current min-height change animation. + base::Optional<std::pair<float, float>> + top_min_height_offset_animation_range_; + // Minimum and maximum values |bottom_controls_min_height_offset_| can take + // during the current min-height change animation. + base::Optional<std::pair<float, float>> + bottom_min_height_offset_animation_range_; + // Class that holds and manages the state of the controls animations. class Animation { public: diff --git a/chromium/cc/input/browser_controls_offset_manager_unittest.cc b/chromium/cc/input/browser_controls_offset_manager_unittest.cc index 7c64038668f..69a6e62d17f 100644 --- a/chromium/cc/input/browser_controls_offset_manager_unittest.cc +++ b/chromium/cc/input/browser_controls_offset_manager_unittest.cc @@ -1234,5 +1234,70 @@ TEST(BrowserControlsOffsetManagerTest, MinHeightChangeUpdatesAnimation) { EXPECT_FLOAT_EQ(0.1f, manager->TopControlsShownRatio()); } +// Tests that setting a top height and min-height with animation when both were +// 0 doesn't cause invalid |TopControlsMinHeightOffset| values. +// See: https://crbug.com/1184902. +TEST(BrowserControlsOffsetManagerTest, + ChangingTopMinHeightFromInitialZeroAnimatesCorrectly) { + MockBrowserControlsOffsetManagerClient client(0, 0.5f, 0.5f); + BrowserControlsOffsetManager* manager = client.manager(); + + client.SetBrowserControlsParams({100, 30, 0, 0, true, false}); + EXPECT_TRUE(manager->HasAnimation()); + EXPECT_FLOAT_EQ(0.f, client.CurrentTopControlsShownRatio()); + + base::TimeTicks time = base::TimeTicks::Now(); + + // First animate will establish the animation. + float previous_min_height_offset = 0.f; + manager->Animate(time); + EXPECT_EQ(manager->TopControlsMinHeightOffset(), previous_min_height_offset); + + while (manager->HasAnimation()) { + previous_min_height_offset = manager->TopControlsMinHeightOffset(); + time = base::TimeDelta::FromMicroseconds(100) + time; + manager->Animate(time); + EXPECT_GE(manager->TopControlsMinHeightOffset(), + previous_min_height_offset); + EXPECT_LE(manager->TopControlsMinHeightOffset(), + manager->TopControlsMinHeight()); + } + + EXPECT_FLOAT_EQ(30.f, manager->TopControlsMinHeightOffset()); +} + +// Tests that reducing both height and min-height with animation doesn't cause +// invalid |TopControlsMinHeightOffset| values. +TEST(BrowserControlsOffsetManagerTest, + ReducingTopHeightAndMinHeightAnimatesCorrectly) { + MockBrowserControlsOffsetManagerClient client(0, 0.5f, 0.5f); + BrowserControlsOffsetManager* manager = client.manager(); + + client.SetBrowserControlsParams({100, 30, 0, 0, false, false}); + EXPECT_FALSE(manager->HasAnimation()); + EXPECT_EQ(30, manager->TopControlsMinHeightOffset()); + client.SetBrowserControlsParams({50, 20, 0, 0, true, false}); + EXPECT_TRUE(manager->HasAnimation()); + + base::TimeTicks time = base::TimeTicks::Now(); + + // First animate will establish the animation. + float previous_min_height_offset = 30.f; + manager->Animate(time); + EXPECT_EQ(manager->TopControlsMinHeightOffset(), previous_min_height_offset); + + while (manager->HasAnimation()) { + previous_min_height_offset = manager->TopControlsMinHeightOffset(); + time = base::TimeDelta::FromMicroseconds(100) + time; + manager->Animate(time); + EXPECT_LE(manager->TopControlsMinHeightOffset(), + previous_min_height_offset); + EXPECT_GE(manager->TopControlsMinHeightOffset(), + manager->TopControlsMinHeight()); + } + + EXPECT_FLOAT_EQ(20.f, manager->TopControlsMinHeightOffset()); +} + } // namespace } // namespace cc diff --git a/chromium/cc/input/scroll_snap_data.cc b/chromium/cc/input/scroll_snap_data.cc index f2586f2b976..7633c4f4336 100644 --- a/chromium/cc/input/scroll_snap_data.cc +++ b/chromium/cc/input/scroll_snap_data.cc @@ -4,17 +4,42 @@ #include "cc/input/scroll_snap_data.h" +#include <algorithm> +#include <cmath> +#include <limits> +#include <memory> + #include "base/check.h" #include "base/notreached.h" #include "base/numerics/ranges.h" #include "cc/input/snap_selection_strategy.h" - -#include <algorithm> -#include <cmath> +#include "ui/gfx/geometry/vector2d_f.h" namespace cc { namespace { +gfx::Vector2dF DistanceFromCorridor(double dx, + double dy, + const gfx::RectF& area) { + gfx::Vector2dF distance; + + if (dx < 0) + distance.set_x(-dx); + else if (dx > area.width()) + distance.set_x(dx - area.width()); + else + distance.set_x(0); + + if (dy < 0) + distance.set_y(-dy); + else if (dy > area.height()) + distance.set_y(dy - area.height()); + else + distance.set_y(0); + + return distance; +} + bool IsMutualVisible(const SnapSearchResult& a, const SnapSearchResult& b) { return gfx::RangeF(b.snap_offset()).IsBoundedBy(a.visible_range()) && gfx::RangeF(a.snap_offset()).IsBoundedBy(b.visible_range()); @@ -152,7 +177,6 @@ bool SnapContainerData::FindSnapPosition( SnapSearchResult initial_snap_position_y = { base::ClampToRange(base_position.y(), 0.f, max_position_.y()), gfx::RangeF(0, max_position_.x())}; - selected_x = FindClosestValidArea(SearchAxis::kX, strategy, initial_snap_position_y); } @@ -170,8 +194,15 @@ bool SnapContainerData::FindSnapPosition( } } - if (!selected_x.has_value() && !selected_y.has_value()) + if (!selected_x.has_value() && !selected_y.has_value()) { + // Searching along each axis separately can miss valid snap positions if + // snapping along both axes and the snap positions are off screen. + if (should_snap_on_x && should_snap_on_y && + !strategy.ShouldRespectSnapStop()) + return FindSnapPositionForMutualSnap(strategy, snap_position); + return false; + } // If snapping in one axis pushes off-screen the other snap area, this snap // position is invalid. https://drafts.csswg.org/css-scroll-snap-1/#snap-scope @@ -211,6 +242,61 @@ bool SnapContainerData::FindSnapPosition( return true; } +// This method is called only if the preferred algorithm fails to find either an +// x or a y snap position. +// The base algorithm searches on x (if appropriate) and then y (if +// appropriate). Each search is along the corridor in the search direction. +// For a search in the x-direction, areas as excluded from consideration if the +// range in the y-direction does not overlap the y base position (i.e. can +// scroll-snap in the x-direction without scrolling in the y-direction). Rules +// for scroll-snap in the y-direction are symmetric. This is the preferred +// approach, though the ordering of the searches should perhaps be determined +// based on axis locking. +// In cases where no valid snap points are found via searches along the axis +// corridors, the snap selection strategy allows for selection of areas outside +// of the corridors. +bool SnapContainerData::FindSnapPositionForMutualSnap( + const SnapSelectionStrategy& strategy, + gfx::ScrollOffset* snap_position) const { + DCHECK(strategy.ShouldSnapOnX() && strategy.ShouldSnapOnY()); + bool found = false; + gfx::Vector2dF smallest_distance(std::numeric_limits<float>::max(), + std::numeric_limits<float>::max()); + + // Snap to same element for x & y if possible. + for (const SnapAreaData& area : snap_area_list_) { + if (!strategy.IsValidSnapArea(SearchAxis::kX, area)) + continue; + + if (!strategy.IsValidSnapArea(SearchAxis::kY, area)) + continue; + + SnapSearchResult x_candidate = GetSnapSearchResult(SearchAxis::kX, area); + float dx = x_candidate.snap_offset() - strategy.current_position().x(); + if (std::abs(dx) > proximity_range_.x()) + continue; + + SnapSearchResult y_candidate = GetSnapSearchResult(SearchAxis::kY, area); + float dy = y_candidate.snap_offset() - strategy.current_position().y(); + if (std::abs(dy) > proximity_range_.y()) + continue; + + // Preferentially minimize block scrolling distance. Ties in block scrolling + // distance are resolved by considering inline scrolling distance. + gfx::Vector2dF distance = DistanceFromCorridor(dx, dy, rect_); + if (distance.y() < smallest_distance.y() || + (distance.y() == smallest_distance.y() && + distance.x() < smallest_distance.x())) { + smallest_distance = distance; + snap_position->set_x(x_candidate.snap_offset()); + snap_position->set_y(y_candidate.snap_offset()); + found = true; + } + } + + return found; +} + base::Optional<SnapSearchResult> SnapContainerData::GetTargetSnapAreaSearchResult(SearchAxis axis) const { ElementId target_id = axis == SearchAxis::kX @@ -384,6 +470,8 @@ SnapSearchResult SnapContainerData::GetSnapSearchResult( result.set_visible_range(gfx::RangeF(area.rect.y() - rect_.bottom(), area.rect.bottom() - rect_.y())); // https://www.w3.org/TR/css-scroll-snap-1/#scroll-snap-align + // Snap alignment has been normalized for a horizontal left to right and top + // to bottom writing mode. switch (area.scroll_snap_align.alignment_inline) { case SnapAlignment::kStart: result.set_snap_offset(area.rect.x() - rect_.x()); diff --git a/chromium/cc/input/scroll_snap_data.h b/chromium/cc/input/scroll_snap_data.h index 8c80440d5cf..e22fc9b6a84 100644 --- a/chromium/cc/input/scroll_snap_data.h +++ b/chromium/cc/input/scroll_snap_data.h @@ -275,6 +275,9 @@ class CC_EXPORT SnapContainerData { const SnapSelectionStrategy& strategy, const SnapSearchResult& cross_axis_snap_result) const; + bool FindSnapPositionForMutualSnap(const SnapSelectionStrategy& strategy, + gfx::ScrollOffset* snap_position) const; + // Finds the snap area associated with the target snap area element id for the // given axis. base::Optional<SnapSearchResult> GetTargetSnapAreaSearchResult( diff --git a/chromium/cc/input/scroll_state.h b/chromium/cc/input/scroll_state.h index 8ac3850ca51..5589879a82f 100644 --- a/chromium/cc/input/scroll_state.h +++ b/chromium/cc/input/scroll_state.h @@ -59,6 +59,14 @@ class CC_EXPORT ScrollState { data_.is_direct_manipulation = is_direct_manipulation; } + // True if the user interacts with the scrollbar. + bool is_scrollbar_interaction() const { + return data_.is_scrollbar_interaction; + } + void set_is_scrollbar_interaction(bool is_scrollbar_interaction) { + data_.is_scrollbar_interaction = is_scrollbar_interaction; + } + bool delta_consumed_for_scroll_sequence() const { return data_.delta_consumed_for_scroll_sequence; } diff --git a/chromium/cc/input/scroll_state_data.cc b/chromium/cc/input/scroll_state_data.cc index f18676f4b85..3409421c5a2 100644 --- a/chromium/cc/input/scroll_state_data.cc +++ b/chromium/cc/input/scroll_state_data.cc @@ -22,6 +22,7 @@ ScrollStateData::ScrollStateData() from_user_input(false), delta_consumed_for_scroll_sequence(false), is_direct_manipulation(false), + is_scrollbar_interaction(false), delta_granularity(ui::ScrollGranularity::kScrollByPrecisePixel), caused_scroll_x(false), caused_scroll_y(false), diff --git a/chromium/cc/input/scroll_state_data.h b/chromium/cc/input/scroll_state_data.h index a3a2755c61b..82ea52fa607 100644 --- a/chromium/cc/input/scroll_state_data.h +++ b/chromium/cc/input/scroll_state_data.h @@ -47,6 +47,8 @@ class CC_EXPORT ScrollStateData { // True if the user interacts directly with the display, e.g., via // touch. bool is_direct_manipulation; + // True if the scroll is the result of a scrollbar interaction. + bool is_scrollbar_interaction; // Granularity units for the scroll delta. ui::ScrollGranularity delta_granularity; diff --git a/chromium/cc/input/threaded_input_handler.cc b/chromium/cc/input/threaded_input_handler.cc index a22f04db5f7..18804b0c370 100644 --- a/chromium/cc/input/threaded_input_handler.cc +++ b/chromium/cc/input/threaded_input_handler.cc @@ -366,6 +366,15 @@ InputHandlerScrollResult ThreadedInputHandler::ScrollUpdate( last_scroll_update_state_ = *scroll_state; + // Snap on update if interacting with the scrollbar track or arrow buttons. + // Interactions with the scrollbar thumb have kScrollByPrecisePixel + // granularity. + if (scroll_state->is_scrollbar_interaction() && + scroll_state->delta_granularity() != + ui::ScrollGranularity::kScrollByPrecisePixel) { + AdjustScrollDeltaForScrollbarSnap(scroll_state); + } + gfx::Vector2dF resolvedScrollDelta = ResolveScrollGranularityToPixels( *CurrentlyScrollingNode(), gfx::Vector2dF(scroll_state->delta_x(), scroll_state->delta_y()), @@ -466,6 +475,36 @@ InputHandlerScrollResult ThreadedInputHandler::ScrollUpdate( return scroll_result; } +void ThreadedInputHandler::AdjustScrollDeltaForScrollbarSnap( + ScrollState* scroll_state) { + ScrollNode* scroll_node = CurrentlyScrollingNode(); + if (!scroll_node || !scroll_node->snap_container_data) + return; + + // Ideally, scrollbar track and arrow interactions would have + // kScrollByPage and kScrollByLine, respectively. Currently, both have + // kScrollByPixel granularity. + // TODO(crbug.com/959441): Update snap strategy once the granularity is + // properly set. Currently, track and arrow scrolls both use a direction + // strategy; however, the track should be using an "end and direction" + // strategy. + gfx::ScrollOffset current_position = GetVisualScrollOffset(*scroll_node); + const SnapContainerData& data = scroll_node->snap_container_data.value(); + std::unique_ptr<SnapSelectionStrategy> strategy = + SnapSelectionStrategy::CreateForDirection( + gfx::ScrollOffset(current_position.x(), current_position.y()), + gfx::ScrollOffset(scroll_state->delta_x(), scroll_state->delta_y()), + true); + + gfx::ScrollOffset snap_position; + TargetSnapAreaElementIds snap_target_ids; + if (!data.FindSnapPosition(*strategy, &snap_position, &snap_target_ids)) + return; + + scroll_state->data()->delta_x = snap_position.x() - current_position.x(); + scroll_state->data()->delta_y = snap_position.y() - current_position.y(); +} + void ThreadedInputHandler::ScrollEnd(bool should_snap) { scrollbar_controller_->ResetState(); if (!CurrentlyScrollingNode()) @@ -1967,18 +2006,7 @@ bool ThreadedInputHandler::ShouldAnimateScroll( bool has_precise_scroll_deltas = scroll_state.delta_granularity() == ui::ScrollGranularity::kScrollByPrecisePixel; -#if defined(OS_MAC) - if (has_precise_scroll_deltas) - return false; - - // Mac does not smooth scroll wheel events (crbug.com/574283). We allow tests - // to force it on. - return latched_scroll_type_ == ui::ScrollInputType::kScrollbar - ? true - : force_smooth_wheel_scrolling_for_testing_; -#else return !has_precise_scroll_deltas; -#endif } bool ThreadedInputHandler::SnapAtScrollEnd() { diff --git a/chromium/cc/input/threaded_input_handler.h b/chromium/cc/input/threaded_input_handler.h index 80b33189b2f..3467d942f1c 100644 --- a/chromium/cc/input/threaded_input_handler.h +++ b/chromium/cc/input/threaded_input_handler.h @@ -340,6 +340,8 @@ class CC_EXPORT ThreadedInputHandler : public InputHandler, gfx::Vector2dF UserScrollableDelta(const ScrollNode& node, const gfx::Vector2dF& delta) const; + void AdjustScrollDeltaForScrollbarSnap(ScrollState* scroll_state); + FrameSequenceTrackerType GetTrackerTypeForScroll( ui::ScrollInputType input_type) const; diff --git a/chromium/cc/layers/heads_up_display_layer_impl.cc b/chromium/cc/layers/heads_up_display_layer_impl.cc index de7936aad42..e755b3b37fd 100644 --- a/chromium/cc/layers/heads_up_display_layer_impl.cc +++ b/chromium/cc/layers/heads_up_display_layer_impl.cc @@ -272,6 +272,7 @@ void HeadsUpDisplayLayerImpl::UpdateHudTexture( // Allocate a backing for the resource if needed, either for gpu or software // compositing. ResourcePool::InUsePoolResource pool_resource; + bool needs_clear = false; if (draw_mode == DRAW_MODE_HARDWARE) { DCHECK(raster_context_provider || context_provider); const auto& caps = raster_context_provider @@ -317,6 +318,7 @@ void HeadsUpDisplayLayerImpl::UpdateHudTexture( gl->WaitSyncTokenCHROMIUM(sii->GenUnverifiedSyncToken().GetConstData()); } pool_resource.set_gpu_backing(std::move(backing)); + needs_clear = true; } else if (pool_resource.gpu_backing()->returned_sync_token.HasData()) { if (raster_context_provider) { auto* ri = raster_context_provider->RasterInterface(); @@ -373,7 +375,7 @@ void HeadsUpDisplayLayerImpl::UpdateHudTexture( constexpr GLuint background_color = SkColorSetARGB(0, 0, 0, 0); constexpr GLuint msaa_sample_count = -1; constexpr bool can_use_lcd_text = true; - ri->BeginRasterCHROMIUM(background_color, msaa_sample_count, + ri->BeginRasterCHROMIUM(background_color, needs_clear, msaa_sample_count, can_use_lcd_text, gfx::ColorSpace::CreateSRGB(), backing->mailbox.name); gfx::Vector2dF post_translate(0.f, 0.f); @@ -553,6 +555,10 @@ void HeadsUpDisplayLayerImpl::SetLayoutShiftRects( layout_shift_rects_ = rects; } +void HeadsUpDisplayLayerImpl::ClearLayoutShiftRects() { + layout_shift_rects_.clear(); +} + void HeadsUpDisplayLayerImpl::SetWebVitalMetrics( std::unique_ptr<WebVitalMetrics> web_vital_metrics) { web_vital_metrics_ = std::move(web_vital_metrics); @@ -1075,7 +1081,6 @@ void HeadsUpDisplayLayerImpl::DrawDebugRects( DebugColors::PaintRectBorderWidth(), ""); } } - if (new_layout_shift_rects.size()) { layout_shift_debug_rects_.swap(new_layout_shift_rects); layout_shift_rects_fade_step_ = DebugColors::kFadeSteps; diff --git a/chromium/cc/layers/heads_up_display_layer_impl.h b/chromium/cc/layers/heads_up_display_layer_impl.h index 804201da7d9..bfd8b775aae 100644 --- a/chromium/cc/layers/heads_up_display_layer_impl.h +++ b/chromium/cc/layers/heads_up_display_layer_impl.h @@ -68,6 +68,7 @@ class CC_EXPORT HeadsUpDisplayLayerImpl : public LayerImpl { void SetHUDTypeface(sk_sp<SkTypeface> typeface); void SetLayoutShiftRects(const std::vector<gfx::Rect>& rects); + void ClearLayoutShiftRects(); const std::vector<gfx::Rect>& LayoutShiftRects() const; void SetWebVitalMetrics(std::unique_ptr<WebVitalMetrics> web_vital_metrics); diff --git a/chromium/cc/layers/layer_impl.cc b/chromium/cc/layers/layer_impl.cc index 2d69deaaddd..29fb9db2e05 100644 --- a/chromium/cc/layers/layer_impl.cc +++ b/chromium/cc/layers/layer_impl.cc @@ -661,6 +661,8 @@ void LayerImpl::AsValueInto(base::trace_event::TracedValue* state) const { state->SetInteger("effect_tree_index", effect_tree_index()); state->SetInteger("scroll_tree_index", scroll_tree_index()); + state->SetInteger("sorting_context_id", GetSortingContextId()); + state->SetInteger("draws_content", DrawsContent()); state->SetInteger("gpu_memory_usage", base::saturated_cast<int>(GPUMemoryUsageInBytes())); diff --git a/chromium/cc/layers/layer_unittest.cc b/chromium/cc/layers/layer_unittest.cc index 9770b8c6873..0164391ffbb 100644 --- a/chromium/cc/layers/layer_unittest.cc +++ b/chromium/cc/layers/layer_unittest.cc @@ -6,6 +6,7 @@ #include <stddef.h> +#include <memory> #include <utility> #include "base/bind.h" @@ -149,8 +150,8 @@ class LayerTest : public testing::Test { params.task_graph_runner = &task_graph_runner_; params.mutator_host = animation_host_.get(); - layer_tree_host_.reset(new StrictMock<MockLayerTreeHost>( - &single_thread_client_, std::move(params))); + layer_tree_host_ = std::make_unique<StrictMock<MockLayerTreeHost>>( + &single_thread_client_, std::move(params)); } void TearDown() override { diff --git a/chromium/cc/layers/picture_layer.cc b/chromium/cc/layers/picture_layer.cc index 900533c2d38..bd0e801e304 100644 --- a/chromium/cc/layers/picture_layer.cc +++ b/chromium/cc/layers/picture_layer.cc @@ -94,7 +94,7 @@ void PictureLayer::SetLayerTreeHost(LayerTreeHost* host) { return; if (!recording_source_) - recording_source_.reset(new RecordingSource); + recording_source_ = std::make_unique<RecordingSource>(); recording_source_->SetSlowdownRasterScaleFactor( host->GetDebugState().slow_down_raster_scale_factor); diff --git a/chromium/cc/layers/picture_layer_impl.cc b/chromium/cc/layers/picture_layer_impl.cc index e49acdc47ec..08b2552ac9f 100644 --- a/chromium/cc/layers/picture_layer_impl.cc +++ b/chromium/cc/layers/picture_layer_impl.cc @@ -98,25 +98,10 @@ gfx::Rect SafeIntersectRects(const gfx::Rect& one, const gfx::Rect& two) { PictureLayerImpl::PictureLayerImpl(LayerTreeImpl* tree_impl, int id) : LayerImpl(tree_impl, id, /*will_always_push_properties=*/true), - twin_layer_(nullptr), - tilings_(CreatePictureLayerTilingSet()), - ideal_page_scale_(0.f), - ideal_device_scale_(0.f), - ideal_source_scale_(0.f), - ideal_contents_scale_(0.f), - raster_page_scale_(0.f), - raster_device_scale_(0.f), - raster_source_scale_(0.f), - raster_contents_scale_(0.f), - low_res_raster_contents_scale_(0.f), is_backdrop_filter_mask_(false), was_screen_space_transform_animating_(false), only_used_low_res_last_append_quads_(false), - nearest_neighbor_(false), - lcd_text_disallowed_reason_(LCDTextDisallowedReason::kNone), - directly_composited_image_size_(base::nullopt), - directly_composited_image_initial_raster_scale_(0.f), - tile_size_calculator_(this) { + nearest_neighbor_(false) { layer_tree_impl()->RegisterPictureLayerImpl(this); } diff --git a/chromium/cc/layers/picture_layer_impl.h b/chromium/cc/layers/picture_layer_impl.h index e526ef44341..67b1ea5faad 100644 --- a/chromium/cc/layers/picture_layer_impl.h +++ b/chromium/cc/layers/picture_layer_impl.h @@ -236,31 +236,32 @@ class CC_EXPORT PictureLayerImpl // will change transform. bool HasWillChangeTransformHint() const; - PictureLayerImpl* twin_layer_; + PictureLayerImpl* twin_layer_ = nullptr; - std::unique_ptr<PictureLayerTilingSet> tilings_; + std::unique_ptr<PictureLayerTilingSet> tilings_ = + CreatePictureLayerTilingSet(); scoped_refptr<RasterSource> raster_source_; Region invalidation_; // Ideal scales are calcuated from the transforms applied to the layer. They // represent the best known scale from the layer to the final output. // Page scale is from user pinch/zoom. - float ideal_page_scale_; + float ideal_page_scale_ = 0.f; // Device scale is from screen dpi, and it comes from device scale facter. - float ideal_device_scale_; + float ideal_device_scale_ = 0.f; // Source scale comes from javascript css scale. - float ideal_source_scale_; + float ideal_source_scale_ = 0.f; // Contents scale = device scale * page scale * source scale. - float ideal_contents_scale_; + float ideal_contents_scale_ = 0.f; // Raster scales are set from ideal scales. They are scales we choose to // raster at. They may not match the ideal scales at times to avoid raster for // performance reasons. - float raster_page_scale_; - float raster_device_scale_; - float raster_source_scale_; - float raster_contents_scale_; - float low_res_raster_contents_scale_; + float raster_page_scale_ = 0.f; + float raster_device_scale_ = 0.f; + float raster_source_scale_ = 0.f; + float raster_contents_scale_ = 0.f; + float low_res_raster_contents_scale_ = 0.f; bool is_backdrop_filter_mask_ : 1; @@ -269,7 +270,8 @@ class CC_EXPORT PictureLayerImpl bool nearest_neighbor_ : 1; - LCDTextDisallowedReason lcd_text_disallowed_reason_; + LCDTextDisallowedReason lcd_text_disallowed_reason_ = + LCDTextDisallowedReason::kNone; // 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 @@ -280,7 +282,7 @@ class CC_EXPORT PictureLayerImpl // 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_; + float directly_composited_image_initial_raster_scale_ = 0.f; // Use this instead of |visible_layer_rect()| for tiling calculations. This // takes external viewport and transform for tile priority into account. @@ -303,7 +305,7 @@ class CC_EXPORT PictureLayerImpl PaintWorkletRecordMap paint_worklet_records_; gfx::Size content_bounds_; - TileSizeCalculator tile_size_calculator_; + TileSizeCalculator tile_size_calculator_{this}; // Denotes an area that is damaged and needs redraw. This is in the layer's // space. diff --git a/chromium/cc/layers/picture_layer_impl_unittest.cc b/chromium/cc/layers/picture_layer_impl_unittest.cc index 61ac7452a91..dbf6b3b2b7b 100644 --- a/chromium/cc/layers/picture_layer_impl_unittest.cc +++ b/chromium/cc/layers/picture_layer_impl_unittest.cc @@ -3229,9 +3229,9 @@ TEST_F(LegacySWPictureLayerImplTest, TilingSetRasterQueue) { RasterTilePriorityQueue::Type::REQUIRED_FOR_DRAW)); EXPECT_TRUE(required_queue->IsEmpty()); - required_queue.reset(new TilingSetRasterQueueRequired( + required_queue = std::make_unique<TilingSetRasterQueueRequired>( pending_layer()->picture_layer_tiling_set(), - RasterTilePriorityQueue::Type::REQUIRED_FOR_ACTIVATION)); + RasterTilePriorityQueue::Type::REQUIRED_FOR_ACTIVATION); EXPECT_FALSE(required_queue->IsEmpty()); int required_for_activation_count = 0; while (!required_queue->IsEmpty()) { @@ -3255,8 +3255,8 @@ TEST_F(LegacySWPictureLayerImplTest, TilingSetRasterQueue) { unique_tiles.clear(); high_res_tile_count = 0u; - queue.reset(new TilingSetRasterQueueAll( - pending_layer()->picture_layer_tiling_set(), false, false)); + queue = std::make_unique<TilingSetRasterQueueAll>( + pending_layer()->picture_layer_tiling_set(), false, false); while (!queue->IsEmpty()) { PrioritizedTile prioritized_tile = queue->Top(); TilePriority priority = prioritized_tile.priority(); @@ -3291,8 +3291,8 @@ TEST_F(LegacySWPictureLayerImplTest, TilingSetRasterQueue) { draw_info.SetSolidColorForTesting(SK_ColorRED); } - queue.reset(new TilingSetRasterQueueAll( - pending_layer()->picture_layer_tiling_set(), true, false)); + queue = std::make_unique<TilingSetRasterQueueAll>( + pending_layer()->picture_layer_tiling_set(), true, false); EXPECT_TRUE(queue->IsEmpty()); } @@ -3322,9 +3322,9 @@ TEST_F(LegacySWPictureLayerImplTest, TilingSetRasterQueueActiveTree) { queue->Pop(); } - queue.reset(new TilingSetRasterQueueRequired( + queue = std::make_unique<TilingSetRasterQueueRequired>( active_layer()->picture_layer_tiling_set(), - RasterTilePriorityQueue::Type::REQUIRED_FOR_ACTIVATION)); + RasterTilePriorityQueue::Type::REQUIRED_FOR_ACTIVATION); EXPECT_TRUE(queue->IsEmpty()); } @@ -3407,9 +3407,9 @@ TEST_F(LegacySWPictureLayerImplTest, TilingSetEvictionQueue) { PrioritizedTile last_tile; size_t distance_decreasing = 0; size_t distance_increasing = 0; - queue.reset(new TilingSetEvictionQueue( + queue = std::make_unique<TilingSetEvictionQueue>( pending_layer()->picture_layer_tiling_set(), - pending_layer()->contributes_to_drawn_render_surface())); + pending_layer()->contributes_to_drawn_render_surface()); while (!queue->IsEmpty()) { PrioritizedTile prioritized_tile = queue->Top(); Tile* tile = prioritized_tile.tile(); @@ -4258,8 +4258,8 @@ TEST_F(OcclusionTrackingPictureLayerImplTest, UpdateDrawProperties(host_impl()->pending_tree()); unoccluded_tile_count = 0; - queue.reset(new TilingSetRasterQueueAll( - pending_layer()->picture_layer_tiling_set(), false, false)); + queue = std::make_unique<TilingSetRasterQueueAll>( + pending_layer()->picture_layer_tiling_set(), false, false); while (!queue->IsEmpty()) { PrioritizedTile prioritized_tile = queue->Top(); Tile* tile = prioritized_tile.tile(); @@ -4282,8 +4282,8 @@ TEST_F(OcclusionTrackingPictureLayerImplTest, UpdateDrawProperties(host_impl()->pending_tree()); unoccluded_tile_count = 0; - queue.reset(new TilingSetRasterQueueAll( - pending_layer()->picture_layer_tiling_set(), false, false)); + queue = std::make_unique<TilingSetRasterQueueAll>( + pending_layer()->picture_layer_tiling_set(), false, false); while (!queue->IsEmpty()) { PrioritizedTile prioritized_tile = queue->Top(); Tile* tile = prioritized_tile.tile(); diff --git a/chromium/cc/layers/render_surface_impl.cc b/chromium/cc/layers/render_surface_impl.cc index 25d5ca89aa5..305ef1cde13 100644 --- a/chromium/cc/layers/render_surface_impl.cc +++ b/chromium/cc/layers/render_surface_impl.cc @@ -187,6 +187,11 @@ const EffectNode* RenderSurfaceImpl::OwningEffectNode() const { EffectTreeIndex()); } +const DocumentTransitionSharedElementId& +RenderSurfaceImpl::GetDocumentTransitionSharedElementId() const { + return OwningEffectNode()->document_transition_shared_element_id; +} + void RenderSurfaceImpl::SetClipRect(const gfx::Rect& clip_rect) { if (clip_rect == draw_properties_.clip_rect) return; diff --git a/chromium/cc/layers/render_surface_impl.h b/chromium/cc/layers/render_surface_impl.h index 105e04a7067..eb18ec27ef8 100644 --- a/chromium/cc/layers/render_surface_impl.h +++ b/chromium/cc/layers/render_surface_impl.h @@ -12,6 +12,7 @@ #include <vector> #include "cc/cc_export.h" +#include "cc/document_transition/document_transition_shared_element_id.h" #include "cc/layers/draw_mode.h" #include "cc/layers/layer_collections.h" #include "cc/trees/occlusion.h" @@ -210,6 +211,9 @@ class CC_EXPORT RenderSurfaceImpl { const EffectNode* OwningEffectNode() const; + const DocumentTransitionSharedElementId& + GetDocumentTransitionSharedElementId() const; + private: void SetContentRect(const gfx::Rect& content_rect); gfx::Rect CalculateClippedAccumulatedContentRect(); diff --git a/chromium/cc/layers/scrollbar_layer_unittest.cc b/chromium/cc/layers/scrollbar_layer_unittest.cc index 69f004aa98e..c237739b5f7 100644 --- a/chromium/cc/layers/scrollbar_layer_unittest.cc +++ b/chromium/cc/layers/scrollbar_layer_unittest.cc @@ -4,6 +4,7 @@ #include <stddef.h> +#include <memory> #include <unordered_map> #include "base/threading/thread_task_runner_handle.h" @@ -1019,8 +1020,8 @@ class ScrollbarLayerSolidColorThumbTest : public testing::Test { public: ScrollbarLayerSolidColorThumbTest() { LayerTreeSettings layer_tree_settings; - host_impl_.reset(new FakeLayerTreeHostImpl( - layer_tree_settings, &task_runner_provider_, &task_graph_runner_)); + host_impl_ = std::make_unique<FakeLayerTreeHostImpl>( + layer_tree_settings, &task_runner_provider_, &task_graph_runner_); const int kThumbThickness = 3; const int kTrackStart = 0; diff --git a/chromium/cc/metrics/compositor_frame_reporter.cc b/chromium/cc/metrics/compositor_frame_reporter.cc index fcad7a8cd4e..16e58f6ecef 100644 --- a/chromium/cc/metrics/compositor_frame_reporter.cc +++ b/chromium/cc/metrics/compositor_frame_reporter.cc @@ -46,6 +46,12 @@ constexpr int kBlinkBreakdownInitialIndex = constexpr int kFrameSequenceTrackerTypeCount = static_cast<int>(FrameSequenceTrackerType::kMaxType) + 1; +// Maximum number of partial update dependents a reporter can own. When a +// reporter with too many dependents is terminated, it will terminate all its +// dependents which will block the pipeline for a long time. Too many dependents +// also means too much memory usage. +constexpr size_t kMaxOwnedPartialUpdateDependents = 300u; + // Names for the viz breakdowns that are shown in trace as substages under // PipelineReporter -> SubmitCompositorFrameToPresentationCompositorFrame or // EventLatency -> SubmitCompositorFrameToPresentationCompositorFrame. @@ -539,16 +545,20 @@ CompositorFrameReporter::CompositorFrameReporter( LatencyUkmReporter* latency_ukm_reporter, bool should_report_metrics, SmoothThread smooth_thread, + FrameSequenceMetrics::ThreadType scrolling_thread, int layer_tree_host_id, DroppedFrameCounter* dropped_frame_counter) : should_report_metrics_(should_report_metrics), args_(args), active_trackers_(active_trackers), + scrolling_thread_(scrolling_thread), latency_ukm_reporter_(latency_ukm_reporter), dropped_frame_counter_(dropped_frame_counter), smooth_thread_(smooth_thread), layer_tree_host_id_(layer_tree_host_id) { dropped_frame_counter_->OnBeginFrame(args, IsScrollActive(active_trackers_)); + DCHECK(IsScrollActive(active_trackers_) || + scrolling_thread_ == FrameSequenceMetrics::ThreadType::kUnknown); } std::unique_ptr<CompositorFrameReporter> @@ -565,7 +575,8 @@ CompositorFrameReporter::CopyReporterAtBeginImplStage() { } auto new_reporter = std::make_unique<CompositorFrameReporter>( active_trackers_, args_, latency_ukm_reporter_, should_report_metrics_, - smooth_thread_, layer_tree_host_id_, dropped_frame_counter_); + smooth_thread_, scrolling_thread_, layer_tree_host_id_, + dropped_frame_counter_); 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_; @@ -576,7 +587,7 @@ CompositorFrameReporter::CopyReporterAtBeginImplStage() { // Set up the new reporter so that it depends on |this| for partial update // information. - new_reporter->SetPartialUpdateDecider(weak_factory_.GetWeakPtr()); + new_reporter->SetPartialUpdateDecider(this); return new_reporter; } @@ -672,10 +683,17 @@ void CompositorFrameReporter::SetVizBreakdown( viz_breakdown_ = viz_breakdown; } -void CompositorFrameReporter::SetEventsMetrics( +void CompositorFrameReporter::AddEventsMetrics( EventMetrics::List events_metrics) { - DCHECK_EQ(0u, events_metrics_.size()); - events_metrics_ = std::move(events_metrics); + events_metrics_.insert(events_metrics_.end(), + std::make_move_iterator(events_metrics.begin()), + std::make_move_iterator(events_metrics.end())); +} + +EventMetrics::List CompositorFrameReporter::TakeEventsMetrics() { + EventMetrics::List result = std::move(events_metrics_); + events_metrics_.clear(); + return result; } void CompositorFrameReporter::TerminateReporter() { @@ -1059,11 +1077,26 @@ void CompositorFrameReporter::ReportCompositorLatencyTraceEvents() const { reporter->set_state(state); reporter->set_frame_source(args_.frame_id.source_id); reporter->set_frame_sequence(args_.frame_id.sequence_number); + reporter->set_has_missing_content(has_missing_content_); if (IsDroppedFrameAffectingSmoothness()) { DCHECK(state == ChromeFrameReporter::STATE_DROPPED || state == ChromeFrameReporter::STATE_PRESENTED_PARTIAL); reporter->set_affects_smoothness(true); } + ChromeFrameReporter::ScrollState scroll_state; + switch (scrolling_thread_) { + case FrameSequenceMetrics::ThreadType::kMain: + scroll_state = ChromeFrameReporter::SCROLL_MAIN_THREAD; + break; + case FrameSequenceMetrics::ThreadType::kCompositor: + scroll_state = ChromeFrameReporter::SCROLL_COMPOSITOR_THREAD; + break; + case FrameSequenceMetrics::ThreadType::kUnknown: + scroll_state = ChromeFrameReporter::SCROLL_NONE; + break; + } + reporter->set_scroll_state(scroll_state); + // TODO(crbug.com/1086974): Set 'drop reason' if applicable. }); @@ -1255,10 +1288,6 @@ bool CompositorFrameReporter::IsDroppedFrameAffectingSmoothness() const { return false; } -base::WeakPtr<CompositorFrameReporter> CompositorFrameReporter::GetWeakPtr() { - return weak_factory_.GetWeakPtr(); -} - void CompositorFrameReporter::AdoptReporter( std::unique_ptr<CompositorFrameReporter> reporter) { // If |this| reporter is dependent on another reporter to decide about partial @@ -1270,32 +1299,36 @@ void CompositorFrameReporter::AdoptReporter( } void CompositorFrameReporter::SetPartialUpdateDecider( - base::WeakPtr<CompositorFrameReporter> decider) { + CompositorFrameReporter* decider) { DCHECK(decider); + DCHECK(partial_update_dependents_.empty()); has_partial_update_ = true; - partial_update_decider_ = decider; + partial_update_decider_ = decider->GetWeakPtr(); decider->partial_update_dependents_.push(GetWeakPtr()); - DCHECK(partial_update_dependents_.empty()); } void CompositorFrameReporter::DiscardOldPartialUpdateReporters() { DCHECK_LE(owned_partial_update_dependents_.size(), partial_update_dependents_.size()); - while (owned_partial_update_dependents_.size() > 300u) { + // Remove old owned partial update dependents if there are too many. + while (owned_partial_update_dependents_.size() > + kMaxOwnedPartialUpdateDependents) { auto& dependent = owned_partial_update_dependents_.front(); dependent->set_has_partial_update(false); - partial_update_dependents_.pop(); owned_partial_update_dependents_.pop(); discarded_partial_update_dependents_count_++; } -} -bool CompositorFrameReporter::MightHavePartialUpdate() const { - return !!partial_update_decider_; + // Remove dependent reporters from the front of `partial_update_dependents_` + // queue if they are already destroyed. + while (!partial_update_dependents_.empty() && + !partial_update_dependents_.front()) { + partial_update_dependents_.pop(); + } } -size_t CompositorFrameReporter::GetPartialUpdateDependentsCount() const { - return partial_update_dependents_.size(); +base::WeakPtr<CompositorFrameReporter> CompositorFrameReporter::GetWeakPtr() { + return weak_factory_.GetWeakPtr(); } } // namespace cc diff --git a/chromium/cc/metrics/compositor_frame_reporter.h b/chromium/cc/metrics/compositor_frame_reporter.h index 0e9f0001b06..52cd56f5be1 100644 --- a/chromium/cc/metrics/compositor_frame_reporter.h +++ b/chromium/cc/metrics/compositor_frame_reporter.h @@ -231,6 +231,7 @@ class CC_EXPORT CompositorFrameReporter { LatencyUkmReporter* latency_ukm_reporter, bool should_report_metrics, SmoothThread smooth_thread, + FrameSequenceMetrics::ThreadType scrolling_thread, int layer_tree_host_id, DroppedFrameCounter* dropped_frame_counter); ~CompositorFrameReporter(); @@ -254,9 +255,11 @@ class CC_EXPORT CompositorFrameReporter { void SetBlinkBreakdown(std::unique_ptr<BeginMainFrameMetrics> blink_breakdown, base::TimeTicks begin_main_start); void SetVizBreakdown(const viz::FrameTimingDetails& viz_breakdown); - void SetEventsMetrics(EventMetrics::List events_metrics); - int StageHistorySizeForTesting() { return stage_history_.size(); } + void AddEventsMetrics(EventMetrics::List events_metrics); + EventMetrics::List TakeEventsMetrics(); + + int stage_history_size_for_testing() const { return stage_history_.size(); } void OnFinishImplFrame(base::TimeTicks timestamp); void OnAbortBeginMainFrame(base::TimeTicks timestamp); @@ -288,10 +291,19 @@ class CC_EXPORT CompositorFrameReporter { tick_clock_ = tick_clock; } - void SetPartialUpdateDecider(base::WeakPtr<CompositorFrameReporter> decider); + void set_has_missing_content(bool has_missing_content) { + has_missing_content_ = has_missing_content; + } + + void SetPartialUpdateDecider(CompositorFrameReporter* decider); - bool MightHavePartialUpdate() const; - size_t GetPartialUpdateDependentsCount() const; + size_t partial_update_dependents_size_for_testing() const { + return partial_update_dependents_.size(); + } + + size_t owned_partial_update_dependents_size_for_testing() const { + return owned_partial_update_dependents_.size(); + } const viz::BeginFrameId& frame_id() const { return args_.frame_id; } @@ -303,12 +315,10 @@ class CC_EXPORT CompositorFrameReporter { // If this is a cloned reporter, then this returns a weak-ptr to the original // reporter this was cloned from (using |CopyReporterAtBeginImplStage()|). - base::WeakPtr<CompositorFrameReporter> partial_update_decider() { - return partial_update_decider_; + CompositorFrameReporter* partial_update_decider() const { + return partial_update_decider_.get(); } - base::WeakPtr<CompositorFrameReporter> GetWeakPtr(); - protected: void set_has_partial_update(bool has_partial_update) { has_partial_update_ = has_partial_update; @@ -354,6 +364,8 @@ class CC_EXPORT CompositorFrameReporter { bool IsDroppedFrameAffectingSmoothness() const; + base::WeakPtr<CompositorFrameReporter> GetWeakPtr(); + const bool should_report_metrics_; const viz::BeginFrameArgs args_; @@ -384,6 +396,7 @@ class CC_EXPORT CompositorFrameReporter { FrameTerminationStatus::kUnknown; const ActiveTrackers active_trackers_; + const FrameSequenceMetrics::ThreadType scrolling_thread_; LatencyUkmReporter* latency_ukm_reporter_; @@ -407,6 +420,10 @@ class CC_EXPORT CompositorFrameReporter { const SmoothThread smooth_thread_; const int layer_tree_host_id_; + // Indicates whether the submitted frame had any missing content (i.e. content + // with checkerboarding). + bool has_missing_content_ = false; + // For a reporter A, if the main-thread takes a long time to respond // to a begin-main-frame, then all reporters created (and terminated) until // the main-thread responds depends on this reporter to decide whether those diff --git a/chromium/cc/metrics/compositor_frame_reporter_unittest.cc b/chromium/cc/metrics/compositor_frame_reporter_unittest.cc index e7a38ba77fc..48a06f46119 100644 --- a/chromium/cc/metrics/compositor_frame_reporter_unittest.cc +++ b/chromium/cc/metrics/compositor_frame_reporter_unittest.cc @@ -30,16 +30,7 @@ using ::testing::NotNull; class CompositorFrameReporterTest : public testing::Test { public: - CompositorFrameReporterTest() - : pipeline_reporter_(std::make_unique<CompositorFrameReporter>( - CompositorFrameReporter::ActiveTrackers(), - viz::BeginFrameArgs(), - nullptr, - /*should_report_metrics=*/true, - CompositorFrameReporter::SmoothThread::kSmoothBoth, - /*layer_tree_host_id=*/1, - &dropped_frame_counter_)) { - pipeline_reporter_->set_tick_clock(&test_tick_clock_); + CompositorFrameReporterTest() : pipeline_reporter_(CreatePipelineReporter()) { AdvanceNowByMs(1); dropped_frame_counter_.set_total_counter(&total_frame_counter_); } @@ -115,6 +106,18 @@ class CompositorFrameReporterTest : public testing::Test { return event_times; } + std::unique_ptr<CompositorFrameReporter> CreatePipelineReporter() { + auto reporter = std::make_unique<CompositorFrameReporter>( + CompositorFrameReporter::ActiveTrackers(), viz::BeginFrameArgs(), + /*latency_ukm_reporter=*/nullptr, + /*should_report_metrics=*/true, + CompositorFrameReporter::SmoothThread::kSmoothBoth, + FrameSequenceMetrics::ThreadType::kUnknown, + /*layer_tree_host_id=*/1, &dropped_frame_counter_); + reporter->set_tick_clock(&test_tick_clock_); + return reporter; + } + // This should be defined before |pipeline_reporter_| so it is created before // and destroyed after that. base::SimpleTestTickClock test_tick_clock_; @@ -130,30 +133,30 @@ TEST_F(CompositorFrameReporterTest, MainFrameAbortedReportingTest) { pipeline_reporter_->StartStage( CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame, Now()); - EXPECT_EQ(0, pipeline_reporter_->StageHistorySizeForTesting()); + EXPECT_EQ(0, pipeline_reporter_->stage_history_size_for_testing()); AdvanceNowByMs(3); pipeline_reporter_->StartStage( CompositorFrameReporter::StageType::kSendBeginMainFrameToCommit, Now()); - EXPECT_EQ(1, pipeline_reporter_->StageHistorySizeForTesting()); + EXPECT_EQ(1, pipeline_reporter_->stage_history_size_for_testing()); AdvanceNowByMs(3); pipeline_reporter_->StartStage( CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame, Now()); - EXPECT_EQ(2, pipeline_reporter_->StageHistorySizeForTesting()); + EXPECT_EQ(2, pipeline_reporter_->stage_history_size_for_testing()); AdvanceNowByMs(3); pipeline_reporter_->StartStage( CompositorFrameReporter::StageType:: kSubmitCompositorFrameToPresentationCompositorFrame, Now()); - EXPECT_EQ(3, pipeline_reporter_->StageHistorySizeForTesting()); + EXPECT_EQ(3, pipeline_reporter_->stage_history_size_for_testing()); AdvanceNowByMs(3); pipeline_reporter_->TerminateFrame( CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame, Now()); - EXPECT_EQ(4, pipeline_reporter_->StageHistorySizeForTesting()); + EXPECT_EQ(4, pipeline_reporter_->stage_history_size_for_testing()); pipeline_reporter_ = nullptr; histogram_tester.ExpectTotalCount( @@ -175,18 +178,18 @@ TEST_F(CompositorFrameReporterTest, ReplacedByNewReporterReportingTest) { pipeline_reporter_->StartStage(CompositorFrameReporter::StageType::kCommit, Now()); - EXPECT_EQ(0, pipeline_reporter_->StageHistorySizeForTesting()); + EXPECT_EQ(0, pipeline_reporter_->stage_history_size_for_testing()); AdvanceNowByMs(3); pipeline_reporter_->StartStage( CompositorFrameReporter::StageType::kEndCommitToActivation, Now()); - EXPECT_EQ(1, pipeline_reporter_->StageHistorySizeForTesting()); + EXPECT_EQ(1, pipeline_reporter_->stage_history_size_for_testing()); AdvanceNowByMs(2); pipeline_reporter_->TerminateFrame( CompositorFrameReporter::FrameTerminationStatus::kReplacedByNewReporter, Now()); - EXPECT_EQ(2, pipeline_reporter_->StageHistorySizeForTesting()); + EXPECT_EQ(2, pipeline_reporter_->stage_history_size_for_testing()); pipeline_reporter_ = nullptr; histogram_tester.ExpectTotalCount("CompositorLatency.Commit", 0); @@ -199,18 +202,18 @@ TEST_F(CompositorFrameReporterTest, SubmittedFrameReportingTest) { pipeline_reporter_->StartStage( CompositorFrameReporter::StageType::kActivation, Now()); - EXPECT_EQ(0, pipeline_reporter_->StageHistorySizeForTesting()); + EXPECT_EQ(0, pipeline_reporter_->stage_history_size_for_testing()); AdvanceNowByMs(3); pipeline_reporter_->StartStage( CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame, Now()); - EXPECT_EQ(1, pipeline_reporter_->StageHistorySizeForTesting()); + EXPECT_EQ(1, pipeline_reporter_->stage_history_size_for_testing()); AdvanceNowByMs(2); pipeline_reporter_->TerminateFrame( CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame, Now()); - EXPECT_EQ(2, pipeline_reporter_->StageHistorySizeForTesting()); + EXPECT_EQ(2, pipeline_reporter_->stage_history_size_for_testing()); pipeline_reporter_ = nullptr; histogram_tester.ExpectTotalCount("CompositorLatency.Activation", 1); @@ -235,18 +238,18 @@ TEST_F(CompositorFrameReporterTest, SubmittedDroppedFrameReportingTest) { pipeline_reporter_->StartStage( CompositorFrameReporter::StageType::kSendBeginMainFrameToCommit, Now()); - EXPECT_EQ(0, pipeline_reporter_->StageHistorySizeForTesting()); + EXPECT_EQ(0, pipeline_reporter_->stage_history_size_for_testing()); AdvanceNowByMs(3); pipeline_reporter_->StartStage(CompositorFrameReporter::StageType::kCommit, Now()); - EXPECT_EQ(1, pipeline_reporter_->StageHistorySizeForTesting()); + EXPECT_EQ(1, pipeline_reporter_->stage_history_size_for_testing()); AdvanceNowByMs(2); pipeline_reporter_->TerminateFrame( CompositorFrameReporter::FrameTerminationStatus::kDidNotPresentFrame, Now()); - EXPECT_EQ(2, pipeline_reporter_->StageHistorySizeForTesting()); + EXPECT_EQ(2, pipeline_reporter_->stage_history_size_for_testing()); pipeline_reporter_ = nullptr; histogram_tester.ExpectTotalCount( @@ -299,7 +302,7 @@ TEST_F(CompositorFrameReporterTest, CompositorFrameReporter::StageType:: kSubmitCompositorFrameToPresentationCompositorFrame, Now()); - pipeline_reporter_->SetEventsMetrics(std::move(events_metrics)); + pipeline_reporter_->AddEventsMetrics(std::move(events_metrics)); const base::TimeTicks presentation_time = AdvanceNowByMs(3); pipeline_reporter_->TerminateFrame( @@ -381,7 +384,7 @@ TEST_F(CompositorFrameReporterTest, CompositorFrameReporter::StageType:: kSubmitCompositorFrameToPresentationCompositorFrame, Now()); - pipeline_reporter_->SetEventsMetrics(std::move(events_metrics)); + pipeline_reporter_->AddEventsMetrics(std::move(events_metrics)); AdvanceNowByMs(3); viz::FrameTimingDetails viz_breakdown = BuildVizBreakdown(); @@ -467,7 +470,7 @@ TEST_F(CompositorFrameReporterTest, CompositorFrameReporter::StageType:: kSubmitCompositorFrameToPresentationCompositorFrame, Now()); - pipeline_reporter_->SetEventsMetrics(std::move(events_metrics)); + pipeline_reporter_->AddEventsMetrics(std::move(events_metrics)); AdvanceNowByMs(3); pipeline_reporter_->TerminateFrame( @@ -480,5 +483,122 @@ TEST_F(CompositorFrameReporterTest, IsEmpty()); } +// Verifies that partial update dependent queues are working as expected when +// they reach their maximum capacity. +TEST_F(CompositorFrameReporterTest, PartialUpdateDependentQueues) { + // This constant should match the constant with the same name in + // compositor_frame_reporter.cc. + const size_t kMaxOwnedPartialUpdateDependents = 300u; + + // The first three dependent reporters for the front of the queue. + std::unique_ptr<CompositorFrameReporter> deps[] = { + CreatePipelineReporter(), + CreatePipelineReporter(), + CreatePipelineReporter(), + }; + + // Set `deps[0]` as a dependent of the main reporter and adopt it at the same + // time. This should enqueue it in both non-owned and owned dependents queues. + deps[0]->SetPartialUpdateDecider(pipeline_reporter_.get()); + pipeline_reporter_->AdoptReporter(std::move(deps[0])); + DCHECK_EQ(1u, + pipeline_reporter_->partial_update_dependents_size_for_testing()); + DCHECK_EQ( + 1u, + pipeline_reporter_->owned_partial_update_dependents_size_for_testing()); + + // Set `deps[1]` as a dependent of the main reporter, but don't adopt it yet. + // This should enqueue it in non-owned dependents queue only. + deps[1]->SetPartialUpdateDecider(pipeline_reporter_.get()); + DCHECK_EQ(2u, + pipeline_reporter_->partial_update_dependents_size_for_testing()); + DCHECK_EQ( + 1u, + pipeline_reporter_->owned_partial_update_dependents_size_for_testing()); + + // Set `deps[2]` as a dependent of the main reporter and adopt it at the same + // time. This should enqueue it in both non-owned and owned dependents queues. + deps[2]->SetPartialUpdateDecider(pipeline_reporter_.get()); + pipeline_reporter_->AdoptReporter(std::move(deps[2])); + DCHECK_EQ(3u, + pipeline_reporter_->partial_update_dependents_size_for_testing()); + DCHECK_EQ( + 2u, + pipeline_reporter_->owned_partial_update_dependents_size_for_testing()); + + // Now adopt `deps[1]` to enqueue it in the owned dependents queue. + pipeline_reporter_->AdoptReporter(std::move(deps[1])); + DCHECK_EQ(3u, + pipeline_reporter_->partial_update_dependents_size_for_testing()); + DCHECK_EQ( + 3u, + pipeline_reporter_->owned_partial_update_dependents_size_for_testing()); + + // Fill the queues with more dependent reporters until the capacity is + // reached. After this, the queues should look like this (assuming n equals + // `kMaxOwnedPartialUpdateDependents`): + // Partial Update Dependents: [0, 1, 2, 3, 4, ..., n-1] + // Owned Partial Update Dependents: [0, 2, 1, 3, 4, ..., n-1] + while ( + pipeline_reporter_->owned_partial_update_dependents_size_for_testing() < + kMaxOwnedPartialUpdateDependents) { + std::unique_ptr<CompositorFrameReporter> dependent = + CreatePipelineReporter(); + dependent->SetPartialUpdateDecider(pipeline_reporter_.get()); + pipeline_reporter_->AdoptReporter(std::move(dependent)); + } + DCHECK_EQ(kMaxOwnedPartialUpdateDependents, + pipeline_reporter_->partial_update_dependents_size_for_testing()); + DCHECK_EQ( + kMaxOwnedPartialUpdateDependents, + pipeline_reporter_->owned_partial_update_dependents_size_for_testing()); + + // Enqueue a new dependent reporter. This should pop `deps[0]` from the front + // of the owned dependents queue and destroy it. Since the same one is in + // front of the non-owned dependents queue, it will be popped out of that + // queue, too. The queues will look like this: + // Partial Update Dependents: [1, 2, 3, 4, ..., n] + // Owned Partial Update Dependents: [2, 1, 3, 4, ..., n] + auto new_dep = CreatePipelineReporter(); + new_dep->SetPartialUpdateDecider(pipeline_reporter_.get()); + pipeline_reporter_->AdoptReporter(std::move(new_dep)); + DCHECK_EQ(kMaxOwnedPartialUpdateDependents, + pipeline_reporter_->partial_update_dependents_size_for_testing()); + DCHECK_EQ( + kMaxOwnedPartialUpdateDependents, + pipeline_reporter_->owned_partial_update_dependents_size_for_testing()); + + // Enqueue another new dependent reporter. This should pop `deps[2]` from the + // front of the owned dependents queue and destroy it. Since another reporter + // is in front of the non-owned dependents queue it won't be popped out of + // that queue. The queues will look like this: + // Partial Update Dependents: [2, 3, 4, ..., n+1] + // Owned Partial Update Dependents: [2, nullptr, 3, 4, ..., n+1] + new_dep = CreatePipelineReporter(); + new_dep->SetPartialUpdateDecider(pipeline_reporter_.get()); + pipeline_reporter_->AdoptReporter(std::move(new_dep)); + DCHECK_EQ(kMaxOwnedPartialUpdateDependents + 1, + pipeline_reporter_->partial_update_dependents_size_for_testing()); + DCHECK_EQ( + kMaxOwnedPartialUpdateDependents, + pipeline_reporter_->owned_partial_update_dependents_size_for_testing()); + + // Enqueue yet another new dependent reporter. This should pop `deps[1]` from + // the front of the owned dependents queue and destroy it. Since the same one + // is in front of the non-owned dependents queue followed by `deps[2]` which + // was destroyed in the previous step, they will be popped out of that queue, + // too. The queues will look like this: + // Partial Update Dependents: [3, 4, ..., n+2] + // Owned Partial Update Dependents: [3, 4, ..., n+2] + new_dep = CreatePipelineReporter(); + new_dep->SetPartialUpdateDecider(pipeline_reporter_.get()); + pipeline_reporter_->AdoptReporter(std::move(new_dep)); + DCHECK_EQ(kMaxOwnedPartialUpdateDependents, + pipeline_reporter_->partial_update_dependents_size_for_testing()); + DCHECK_EQ( + kMaxOwnedPartialUpdateDependents, + pipeline_reporter_->owned_partial_update_dependents_size_for_testing()); +} + } // namespace } // namespace cc diff --git a/chromium/cc/metrics/compositor_frame_reporting_controller.cc b/chromium/cc/metrics/compositor_frame_reporting_controller.cc index dec573d13d9..279c9547e70 100644 --- a/chromium/cc/metrics/compositor_frame_reporting_controller.cc +++ b/chromium/cc/metrics/compositor_frame_reporting_controller.cc @@ -94,8 +94,8 @@ void CompositorFrameReportingController::WillBeginImplFrame( } auto reporter = std::make_unique<CompositorFrameReporter>( active_trackers_, args, latency_ukm_reporter_.get(), - should_report_metrics_, GetSmoothThread(), layer_tree_host_id_, - dropped_frame_counter_); + should_report_metrics_, GetSmoothThread(), scrolling_thread_, + layer_tree_host_id_, dropped_frame_counter_); reporter->set_tick_clock(tick_clock_); reporter->StartStage(StageType::kBeginImplFrameToSendBeginMainFrame, begin_time); @@ -121,8 +121,8 @@ void CompositorFrameReportingController::WillBeginMainFrame( // deadline yet). So will start a new reporter at BeginMainFrame. auto reporter = std::make_unique<CompositorFrameReporter>( active_trackers_, args, latency_ukm_reporter_.get(), - should_report_metrics_, GetSmoothThread(), layer_tree_host_id_, - dropped_frame_counter_); + should_report_metrics_, GetSmoothThread(), scrolling_thread_, + layer_tree_host_id_, dropped_frame_counter_); reporter->set_tick_clock(tick_clock_); reporter->StartStage(StageType::kSendBeginMainFrameToCommit, Now()); reporters_[PipelineStage::kBeginMainFrame] = std::move(reporter); @@ -177,7 +177,8 @@ void CompositorFrameReportingController::DidSubmitCompositorFrame( uint32_t frame_token, const viz::BeginFrameId& current_frame_id, const viz::BeginFrameId& last_activated_frame_id, - EventMetricsSet events_metrics) { + EventMetricsSet events_metrics, + bool has_missing_content) { bool is_activated_frame_new = (last_activated_frame_id != last_submitted_frame_id_); @@ -222,8 +223,8 @@ void CompositorFrameReportingController::DidSubmitCompositorFrame( AdvanceReporterStage(PipelineStage::kBeginImplFrame, PipelineStage::kActivate); impl_reporter = std::move(reporters_[PipelineStage::kActivate]); - auto partial_update_decider = - HasOutstandingUpdatesFromMain(current_frame_id); + CompositorFrameReporter* partial_update_decider = + GetOutstandingUpdatesFromMain(current_frame_id); if (partial_update_decider) impl_reporter->SetPartialUpdateDecider(partial_update_decider); } else if (CanSubmitMainFrame(current_frame_id)) { @@ -277,8 +278,9 @@ void CompositorFrameReportingController::DidSubmitCompositorFrame( if (main_reporter) { main_reporter->StartStage( StageType::kSubmitCompositorFrameToPresentationCompositorFrame, Now()); - main_reporter->SetEventsMetrics( + main_reporter->AddEventsMetrics( std::move(events_metrics.main_event_metrics)); + main_reporter->set_has_missing_content(has_missing_content); submitted_compositor_frames_.emplace_back(frame_token, std::move(main_reporter)); } @@ -287,8 +289,9 @@ void CompositorFrameReportingController::DidSubmitCompositorFrame( impl_reporter->EnableCompositorOnlyReporting(); impl_reporter->StartStage( StageType::kSubmitCompositorFrameToPresentationCompositorFrame, Now()); - impl_reporter->SetEventsMetrics( + impl_reporter->AddEventsMetrics( std::move(events_metrics.impl_event_metrics)); + impl_reporter->set_has_missing_content(has_missing_content); submitted_compositor_frames_.emplace_back(frame_token, std::move(impl_reporter)); } @@ -333,8 +336,8 @@ void CompositorFrameReportingController:: } else { // The stage_reporter in this case was waiting for main, so needs to // be adopted by the reporter which is waiting on Main thread's work - auto partial_update_decider = - HasOutstandingUpdatesFromMain(stage_reporter->frame_id()); + CompositorFrameReporter* partial_update_decider = + GetOutstandingUpdatesFromMain(stage_reporter->frame_id()); if (partial_update_decider) { stage_reporter->SetPartialUpdateDecider(partial_update_decider); stage_reporter->OnDidNotProduceFrame(FrameSkippedReason::kWaitingOnMain); @@ -358,37 +361,96 @@ void CompositorFrameReportingController::OnFinishImplFrame( void CompositorFrameReportingController::DidPresentCompositorFrame( uint32_t frame_token, const viz::FrameTimingDetails& details) { - while (!submitted_compositor_frames_.empty()) { - auto submitted_frame = submitted_compositor_frames_.begin(); - if (viz::FrameTokenGT(submitted_frame->frame_token, frame_token)) - break; + bool feedback_failed = details.presentation_feedback.failed(); + for (auto submitted_frame = submitted_compositor_frames_.begin(); + submitted_frame != submitted_compositor_frames_.end() && + !viz::FrameTokenGT(submitted_frame->frame_token, frame_token);) { + bool is_earlier_frame = submitted_frame->frame_token != frame_token; + + // If the presentation feedback is a failure, earlier frames should still be + // left in the queue as they still might end up being presented + // successfully. Skip to the next frame. + if (feedback_failed && is_earlier_frame) { + submitted_frame++; + continue; + } - auto termination_status = FrameTerminationStatus::kPresentedFrame; - if (submitted_frame->frame_token != frame_token || - details.presentation_feedback.failed()) { + auto termination_status = feedback_failed + ? FrameTerminationStatus::kDidNotPresentFrame + : FrameTerminationStatus::kPresentedFrame; + + // If this is an earlier frame, presentation feedback has been successful + // which means this earlier frame should be considered dropped. + if (is_earlier_frame) termination_status = FrameTerminationStatus::kDidNotPresentFrame; - } auto& reporter = submitted_frame->reporter; reporter->SetVizBreakdown(details); reporter->TerminateFrame(termination_status, details.presentation_feedback.timestamp); - // If |reporter| was cloned from a reporter, and the original reporter is - // still alive, then check whether the cloned reporter has the 'partial - // update' flag set. It is still possible for the original reporter to - // terminate with 'no damage', and if that happens, then the cloned - // reporter's 'partial update' flag will need to be reset. To allow this to - // happen, keep the cloned reporter alive, and hand over its ownership to - // the original reporter, so that the cloned reporter stays alive until the - // original reporter is terminated, and the cloned reporter's 'partial - // update' flag can be unset if necessary. - if (reporter->MightHavePartialUpdate()) { - auto orig_reporter = reporter->partial_update_decider(); - if (orig_reporter) + if (termination_status == FrameTerminationStatus::kPresentedFrame) { + // If there are outstanding metrics from dropped frames older than this + // frame, this frame would be the first frame presented after those + // dropped frames. So, this frame is the one presenting updates from those + // frames to the user and should report metrics for them. Note that since + // reporters for submitted but dropped frames are terminated before any + // following frame being presented, all events metrics that should + // potentially be included in this presented frame are already in + // `events_metrics_from_dropped_frames_`. + for (auto it = events_metrics_from_dropped_frames_.begin(); + it != events_metrics_from_dropped_frames_.end() && + !(reporter->frame_id() < it->first); + it = events_metrics_from_dropped_frames_.erase(it)) { + reporter->AddEventsMetrics(std::move(it->second)); + } + + // For presented frames, if `reporter` was cloned from another reporter, + // and the original reporter is still alive, then check whether the cloned + // reporter has a 'partial update decider'. It is still possible for the + // original reporter to terminate with 'no damage', and if that happens, + // then the cloned reporter's 'partial update' flag will need to be reset. + // To allow this to happen, keep the cloned reporter alive, and hand over + // its ownership to the original reporter, so that the cloned reporter + // stays alive until the original reporter is terminated, and the cloned + // reporter's 'partial update' flag can be unset if necessary. This is not + // necessary for frames with failed presentation as we can say for sure + // that they are dropped and nothing will change their fate. + if (CompositorFrameReporter* orig_reporter = + reporter->partial_update_decider()) { orig_reporter->AdoptReporter(std::move(reporter)); + } + } else { + // If the frame didn't end up being presented, keep its metrics around to + // be reported with the first following presented frame. + auto reporter_events_metrics = reporter->TakeEventsMetrics(); + if (!reporter_events_metrics.empty()) { + auto& frame_events_metrics = + events_metrics_from_dropped_frames_[reporter->frame_id()]; + frame_events_metrics.insert( + frame_events_metrics.end(), + std::make_move_iterator(reporter_events_metrics.begin()), + std::make_move_iterator(reporter_events_metrics.end())); + } + } + + if (feedback_failed) { + // When feedback is for a failed presentation, `submitted_frame` is not + // necessarily in the front of the queue. We will reach here only once per + // did-present; so, we will have 1 operation of O(n) complexity (n is the + // number of previous frames). + submitted_frame = submitted_compositor_frames_.erase(submitted_frame); + } else { + // When feedback is for a successful presentation, `submitted_frame` is in + // the front of the queue; so, we will have n operations of O(1) + // complexity for a did-present (n is the number of previous frames). + // `pop_front()` function is used here to shrink the queue when necessary + // to avoid unnecessary memory usage over time. + DCHECK_EQ(submitted_frame->frame_token, + submitted_compositor_frames_.front().frame_token); + submitted_compositor_frames_.pop_front(); + submitted_frame = submitted_compositor_frames_.begin(); } - submitted_compositor_frames_.erase(submitted_frame); } } @@ -427,6 +489,11 @@ void CompositorFrameReportingController::RemoveActiveTracker( dropped_frame_counter_->ReportFrames(); } +void CompositorFrameReportingController::SetScrollingThread( + FrameSequenceMetrics::ThreadType thread) { + scrolling_thread_ = thread; +} + void CompositorFrameReportingController::SetThreadAffectsSmoothness( FrameSequenceMetrics::ThreadType thread_type, bool affects_smoothness) { @@ -532,8 +599,8 @@ CompositorFrameReportingController::GetSmoothThreadAtTime( return smooth_thread_history_.lower_bound(timestamp)->second; } -base::WeakPtr<CompositorFrameReporter> -CompositorFrameReportingController::HasOutstandingUpdatesFromMain( +CompositorFrameReporter* +CompositorFrameReportingController::GetOutstandingUpdatesFromMain( const viz::BeginFrameId& id) const { // Any unterminated reporter in the 'main frame', or 'commit' stages, then // that indicates some pending updates from the main thread. @@ -541,17 +608,17 @@ CompositorFrameReportingController::HasOutstandingUpdatesFromMain( const auto& reporter = reporters_[PipelineStage::kBeginMainFrame]; if (reporter && reporter->frame_id() < id && !reporter->did_abort_main_frame()) { - return reporter->GetWeakPtr(); + return reporter.get(); } } { const auto& reporter = reporters_[PipelineStage::kCommit]; if (reporter && reporter->frame_id() < id) { DCHECK(!reporter->did_abort_main_frame()); - return reporter->GetWeakPtr(); + return reporter.get(); } } - return {}; + return nullptr; } void CompositorFrameReportingController::CreateReportersForDroppedFrames( @@ -577,10 +644,15 @@ void CompositorFrameReportingController::CreateReportersForDroppedFrames( old_args.frame_id.sequence_number + i, timestamp, timestamp + old_args.interval, old_args.interval, viz::BeginFrameArgs::NORMAL); + // ThreadType::kUnknown is used here for scrolling thread, because the + // frames reported here could have a scroll interaction active at their + // start time, but they were skipped and history of scrolling thread might + // change in the diff of start time and report time. auto reporter = std::make_unique<CompositorFrameReporter>( active_trackers_, args, latency_ukm_reporter_.get(), should_report_metrics_, GetSmoothThreadAtTime(timestamp), - layer_tree_host_id_, dropped_frame_counter_); + FrameSequenceMetrics::ThreadType::kUnknown, layer_tree_host_id_, + dropped_frame_counter_); reporter->set_tick_clock(tick_clock_); reporter->StartStage(StageType::kBeginImplFrameToSendBeginMainFrame, timestamp); diff --git a/chromium/cc/metrics/compositor_frame_reporting_controller.h b/chromium/cc/metrics/compositor_frame_reporting_controller.h index 9054c0a325a..e7c6863077b 100644 --- a/chromium/cc/metrics/compositor_frame_reporting_controller.h +++ b/chromium/cc/metrics/compositor_frame_reporting_controller.h @@ -64,7 +64,8 @@ class CC_EXPORT CompositorFrameReportingController { uint32_t frame_token, const viz::BeginFrameId& current_frame_id, const viz::BeginFrameId& last_activated_frame_id, - EventMetricsSet events_metrics); + EventMetricsSet events_metrics, + bool has_missing_content); virtual void DidNotProduceFrame(const viz::BeginFrameId& id, FrameSkippedReason skip_reason); virtual void OnFinishImplFrame(const viz::BeginFrameId& id); @@ -80,6 +81,7 @@ class CC_EXPORT CompositorFrameReportingController { void AddActiveTracker(FrameSequenceTrackerType type); void RemoveActiveTracker(FrameSequenceTrackerType type); + void SetScrollingThread(FrameSequenceMetrics::ThreadType thread); void SetThreadAffectsSmoothness(FrameSequenceMetrics::ThreadType thread_type, bool affects_smoothness); @@ -126,9 +128,9 @@ class CC_EXPORT CompositorFrameReportingController { base::TimeTicks timestamp) const; // Checks whether there are reporters containing updates from the main - // thread, and returns a weak-ptr to that reporter (if any). Otherwise returns - // null. - base::WeakPtr<CompositorFrameReporter> HasOutstandingUpdatesFromMain( + // thread, and returns a pointer to that reporter (if any). Otherwise + // returns nullptr. + CompositorFrameReporter* GetOutstandingUpdatesFromMain( const viz::BeginFrameId& id) const; // If the display-compositor skips over some frames (e.g. when the gpu is @@ -153,18 +155,20 @@ class CC_EXPORT CompositorFrameReportingController { bool next_activate_has_invalidation_ = false; CompositorFrameReporter::ActiveTrackers active_trackers_; + FrameSequenceMetrics::ThreadType scrolling_thread_ = + FrameSequenceMetrics::ThreadType::kUnknown; bool is_compositor_thread_driving_smoothness_ = false; bool is_main_thread_driving_smoothness_ = false; - // Sorted history of smooththread. Element i indicating the smooththread from - // timestamp of element i-1 until timestamp of element i. + // Sorted history of smooththread. Element i indicating the smooththread + // from timestamp of element i-1 until timestamp of element i. std::map<base::TimeTicks, CompositorFrameReporter::SmoothThread> smooth_thread_history_; // The latency reporter passed to each CompositorFrameReporter. Owned here // because it must be common among all reporters. - // DO NOT reorder this line and the ones below. The latency_ukm_reporter_ must - // outlive the objects in |submitted_compositor_frames_|. + // DO NOT reorder this line and the ones below. The latency_ukm_reporter_ + // must outlive the objects in |submitted_compositor_frames_|. std::unique_ptr<LatencyUkmReporter> latency_ukm_reporter_; std::unique_ptr<CompositorFrameReporter> @@ -172,8 +176,8 @@ class CC_EXPORT CompositorFrameReportingController { // 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_|. + // 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_; // The latest frame that was started. @@ -182,6 +186,12 @@ class CC_EXPORT CompositorFrameReportingController { const base::TickClock* tick_clock_ = base::DefaultTickClock::GetInstance(); DroppedFrameCounter* dropped_frame_counter_ = nullptr; + + // When a frame with events metrics fails to be presented, its events metrics + // will be added to this map. The first following presented frame will get + // these metrics and report them. + std::map<viz::BeginFrameId, EventMetrics::List> + events_metrics_from_dropped_frames_; }; } // 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 8f13ac70623..4c0ed45e74c 100644 --- a/chromium/cc/metrics/compositor_frame_reporting_controller_unittest.cc +++ b/chromium/cc/metrics/compositor_frame_reporting_controller_unittest.cc @@ -66,7 +66,8 @@ class TestCompositorFrameReportingController }; for (auto stage : kStages) { auto& reporter = reporters()[stage]; - if (reporter && reporter->GetPartialUpdateDependentsCount() > 0) { + if (reporter && + reporter->partial_update_dependents_size_for_testing() > 0) { ++count; } } @@ -84,7 +85,23 @@ class TestCompositorFrameReportingController for (auto stage : kStages) { auto& reporter = reporters()[stage]; if (reporter) - count += reporter->GetPartialUpdateDependentsCount(); + count += reporter->partial_update_dependents_size_for_testing(); + } + return count; + } + + size_t GetAdoptedReportersCount() { + size_t count = 0; + const PipelineStage kStages[] = { + PipelineStage::kBeginImplFrame, + PipelineStage::kBeginMainFrame, + PipelineStage::kCommit, + PipelineStage::kActivate, + }; + for (auto stage : kStages) { + auto& reporter = reporters()[stage]; + if (reporter) + count += reporter->owned_partial_update_dependents_size_for_testing(); } return count; } @@ -96,8 +113,8 @@ class CompositorFrameReportingControllerTest : public testing::Test { test_tick_clock_.SetNowTicks(base::TimeTicks::Now()); reporting_controller_.set_tick_clock(&test_tick_clock_); args_ = SimulateBeginFrameArgs(current_id_); - reporting_controller_.SetDroppedFrameCounter(&dropped_counter); - dropped_counter.set_total_counter(&total_frame_counter_); + reporting_controller_.SetDroppedFrameCounter(&dropped_counter_); + dropped_counter_.set_total_counter(&total_frame_counter_); } // The following functions simulate the actions that would @@ -150,25 +167,25 @@ class CompositorFrameReportingControllerTest : public testing::Test { last_activated_id_ = current_id_; } - void SimulateSubmitCompositorFrame(uint32_t frame_token, - EventMetricsSet events_metrics) { + void SimulateSubmitCompositorFrame(EventMetricsSet events_metrics) { if (!reporting_controller_.reporters() [CompositorFrameReportingController::PipelineStage::kActivate]) 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)); + ++current_token_; + reporting_controller_.DidSubmitCompositorFrame( + *current_token_, current_id_, last_activated_id_, + std::move(events_metrics), + /*has_missing_content=*/false); } void SimulatePresentCompositorFrame() { - ++next_token_; - SimulateSubmitCompositorFrame(*next_token_, {}); + SimulateSubmitCompositorFrame({}); viz::FrameTimingDetails details = {}; details.presentation_feedback.timestamp = AdvanceNowByMs(10); - reporting_controller_.DidPresentCompositorFrame(*next_token_, details); + reporting_controller_.DidPresentCompositorFrame(*current_token_, details); } viz::BeginFrameArgs SimulateBeginFrameArgs(viz::BeginFrameId frame_id) { @@ -269,8 +286,8 @@ class CompositorFrameReportingControllerTest : public testing::Test { base::TimeTicks begin_activation_time_; base::TimeTicks end_activation_time_; base::TimeTicks submit_time_; - viz::FrameTokenGenerator next_token_; - DroppedFrameCounter dropped_counter; + viz::FrameTokenGenerator current_token_; + DroppedFrameCounter dropped_counter_; TotalFrameCounter total_frame_counter_; }; @@ -340,8 +357,8 @@ TEST_F(CompositorFrameReportingControllerTest, ActiveReporterCounts) { EXPECT_EQ(1, reporting_controller_.ActiveReporters()); last_activated_id_ = current_id_3; - reporting_controller_.DidSubmitCompositorFrame(0, current_id_3, - last_activated_id_, {}); + reporting_controller_.DidSubmitCompositorFrame( + 0, current_id_3, last_activated_id_, {}, /*has_missing_content=*/false); EXPECT_EQ(0, reporting_controller_.ActiveReporters()); // Start a frame and take it all the way to the activate stage. @@ -501,8 +518,8 @@ TEST_F(CompositorFrameReportingControllerTest, DidNotProduceFrame) { reporting_controller_.DidCommit(); reporting_controller_.WillActivate(); reporting_controller_.DidActivate(); - reporting_controller_.DidSubmitCompositorFrame(1, current_id_2, current_id_1, - {}); + reporting_controller_.DidSubmitCompositorFrame( + 1, current_id_2, current_id_1, {}, /*has_missing_content=*/false); viz::FrameTimingDetails details = {}; reporting_controller_.DidPresentCompositorFrame(1, details); @@ -567,8 +584,8 @@ TEST_F(CompositorFrameReportingControllerTest, reporting_controller_.WillActivate(); reporting_controller_.DidActivate(); reporting_controller_.OnFinishImplFrame(current_id_3); - reporting_controller_.DidSubmitCompositorFrame(1, current_id_3, current_id_1, - {}); + reporting_controller_.DidSubmitCompositorFrame( + 1, current_id_3, current_id_1, {}, /*has_missing_content=*/false); viz::FrameTimingDetails details; details.presentation_feedback = {args_3.frame_time + args_3.interval, args_3.interval, 0}; @@ -599,8 +616,8 @@ TEST_F(CompositorFrameReportingControllerTest, MainFrameAborted) { reporting_controller_.WillBeginMainFrame(args_); reporting_controller_.BeginMainFrameAborted(current_id_); reporting_controller_.OnFinishImplFrame(current_id_); - reporting_controller_.DidSubmitCompositorFrame(1, current_id_, - last_activated_id_, {}); + reporting_controller_.DidSubmitCompositorFrame( + 1, current_id_, last_activated_id_, {}, /*has_missing_content=*/false); viz::FrameTimingDetails details = {}; reporting_controller_.DidPresentCompositorFrame(1, details); @@ -654,8 +671,8 @@ TEST_F(CompositorFrameReportingControllerTest, MainFrameAborted2) { reporting_controller_.WillBeginMainFrame(args_2); reporting_controller_.OnFinishImplFrame(current_id_2); reporting_controller_.BeginMainFrameAborted(current_id_2); - reporting_controller_.DidSubmitCompositorFrame(1, current_id_2, current_id_1, - {}); + reporting_controller_.DidSubmitCompositorFrame( + 1, current_id_2, current_id_1, {}, /*has_missing_content=*/false); viz::FrameTimingDetails details = {}; reporting_controller_.DidPresentCompositorFrame(1, details); histogram_tester.ExpectTotalCount( @@ -673,8 +690,8 @@ TEST_F(CompositorFrameReportingControllerTest, MainFrameAborted2) { histogram_tester.ExpectTotalCount( "CompositorLatency.SubmitCompositorFrameToPresentationCompositorFrame", 2); - reporting_controller_.DidSubmitCompositorFrame(2, current_id_2, current_id_1, - {}); + reporting_controller_.DidSubmitCompositorFrame( + 2, current_id_2, current_id_1, {}, /*has_missing_content=*/false); reporting_controller_.DidPresentCompositorFrame(2, details); histogram_tester.ExpectTotalCount( "CompositorLatency.DroppedFrame.BeginImplFrameToSendBeginMainFrame", 0); @@ -693,8 +710,8 @@ TEST_F(CompositorFrameReportingControllerTest, MainFrameAborted2) { 2); reporting_controller_.WillBeginImplFrame(args_3); reporting_controller_.OnFinishImplFrame(current_id_3); - reporting_controller_.DidSubmitCompositorFrame(3, current_id_3, current_id_1, - {}); + reporting_controller_.DidSubmitCompositorFrame( + 3, current_id_3, current_id_1, {}, /*has_missing_content=*/false); reporting_controller_.DidPresentCompositorFrame(3, details); histogram_tester.ExpectTotalCount( "CompositorLatency.DroppedFrame.BeginImplFrameToSendBeginMainFrame", 0); @@ -732,8 +749,8 @@ TEST_F(CompositorFrameReportingControllerTest, LongMainFrame) { reporting_controller_.DidCommit(); reporting_controller_.WillActivate(); reporting_controller_.DidActivate(); - reporting_controller_.DidSubmitCompositorFrame(1, current_id_1, current_id_1, - {}); + reporting_controller_.DidSubmitCompositorFrame( + 1, current_id_1, current_id_1, {}, /*has_missing_content=*/false); reporting_controller_.DidPresentCompositorFrame(1, details); histogram_tester.ExpectTotalCount( @@ -755,8 +772,8 @@ TEST_F(CompositorFrameReportingControllerTest, LongMainFrame) { reporting_controller_.WillBeginImplFrame(args_2); reporting_controller_.WillBeginMainFrame(args_2); reporting_controller_.OnFinishImplFrame(current_id_2); - reporting_controller_.DidSubmitCompositorFrame(2, current_id_2, current_id_1, - {}); + reporting_controller_.DidSubmitCompositorFrame( + 2, current_id_2, current_id_1, {}, /*has_missing_content=*/false); reporting_controller_.DidPresentCompositorFrame(2, details); // The reporting for the second frame is delayed until the main-thread @@ -795,8 +812,8 @@ TEST_F(CompositorFrameReportingControllerTest, LongMainFrame) { reporting_controller_.DidCommit(); reporting_controller_.WillActivate(); reporting_controller_.DidActivate(); - reporting_controller_.DidSubmitCompositorFrame(3, current_id_3, current_id_2, - {}); + reporting_controller_.DidSubmitCompositorFrame( + 3, current_id_3, current_id_2, {}, /*has_missing_content=*/false); reporting_controller_.DidPresentCompositorFrame(3, details); // The main-thread responded, so the metrics for |args_2| should now be @@ -846,8 +863,8 @@ TEST_F(CompositorFrameReportingControllerTest, LongMainFrame2) { reporting_controller_.DidCommit(); reporting_controller_.WillActivate(); reporting_controller_.DidActivate(); - reporting_controller_.DidSubmitCompositorFrame(1, current_id_1, current_id_1, - {}); + reporting_controller_.DidSubmitCompositorFrame( + 1, current_id_1, current_id_1, {}, /*has_missing_content=*/false); reporting_controller_.DidPresentCompositorFrame(1, details); histogram_tester.ExpectTotalCount( @@ -870,8 +887,8 @@ TEST_F(CompositorFrameReportingControllerTest, LongMainFrame2) { reporting_controller_.WillCommit(); reporting_controller_.DidCommit(); reporting_controller_.OnFinishImplFrame(current_id_2); - reporting_controller_.DidSubmitCompositorFrame(2, current_id_2, current_id_1, - {}); + reporting_controller_.DidSubmitCompositorFrame( + 2, current_id_2, current_id_1, {}, /*has_missing_content=*/false); reporting_controller_.DidPresentCompositorFrame(2, details); histogram_tester.ExpectTotalCount( @@ -911,8 +928,8 @@ TEST_F(CompositorFrameReportingControllerTest, LongMainFrame2) { reporting_controller_.DidActivate(); reporting_controller_.WillBeginImplFrame(args_3); reporting_controller_.OnFinishImplFrame(current_id_3); - reporting_controller_.DidSubmitCompositorFrame(3, current_id_3, current_id_2, - {}); + reporting_controller_.DidSubmitCompositorFrame( + 3, current_id_3, current_id_2, {}, /*has_missing_content=*/false); reporting_controller_.DidPresentCompositorFrame(3, details); histogram_tester.ExpectTotalCount( "CompositorLatency.BeginImplFrameToSendBeginMainFrame", 4); @@ -999,8 +1016,8 @@ TEST_F(CompositorFrameReportingControllerTest, ReportingMissedDeadlineFrame1) { reporting_controller_.DidCommit(); reporting_controller_.WillActivate(); reporting_controller_.DidActivate(); - reporting_controller_.DidSubmitCompositorFrame(1, current_id_, current_id_, - {}); + reporting_controller_.DidSubmitCompositorFrame( + 1, current_id_, current_id_, {}, /*has_missing_content=*/false); viz::FrameTimingDetails details = {}; details.presentation_feedback.timestamp = args_.frame_time + args_.interval * 1.5 - @@ -1038,8 +1055,8 @@ TEST_F(CompositorFrameReportingControllerTest, ReportingMissedDeadlineFrame2) { reporting_controller_.DidCommit(); reporting_controller_.WillActivate(); reporting_controller_.DidActivate(); - reporting_controller_.DidSubmitCompositorFrame(1, current_id_, current_id_, - {}); + reporting_controller_.DidSubmitCompositorFrame( + 1, current_id_, current_id_, {}, /*has_missing_content=*/false); viz::FrameTimingDetails details = {}; details.presentation_feedback.timestamp = args_.frame_time + args_.interval * 1.5 + @@ -1113,14 +1130,13 @@ TEST_F(CompositorFrameReportingControllerTest, // Submit a compositor frame and notify CompositorFrameReporter of the events // affecting the frame. - ++next_token_; - SimulateSubmitCompositorFrame(*next_token_, {std::move(events_metrics), {}}); + SimulateSubmitCompositorFrame({std::move(events_metrics), {}}); // Present the submitted compositor frame to the user. const base::TimeTicks presentation_time = AdvanceNowByMs(10); viz::FrameTimingDetails details; details.presentation_feedback.timestamp = presentation_time; - reporting_controller_.DidPresentCompositorFrame(*next_token_, details); + reporting_controller_.DidPresentCompositorFrame(*current_token_, details); // Verify that EventLatency histograms are recorded. struct { @@ -1183,8 +1199,7 @@ TEST_F(CompositorFrameReportingControllerTest, // Submit a compositor frame and notify CompositorFrameReporter of the events // affecting the frame. - ++next_token_; - SimulateSubmitCompositorFrame(*next_token_, {std::move(events_metrics), {}}); + SimulateSubmitCompositorFrame({std::move(events_metrics), {}}); // Present the submitted compositor frame to the user. viz::FrameTimingDetails details; @@ -1193,7 +1208,7 @@ TEST_F(CompositorFrameReportingControllerTest, 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); + reporting_controller_.DidPresentCompositorFrame(*current_token_, details); // Verify that EventLatency histograms are recorded. struct { @@ -1240,10 +1255,10 @@ TEST_F(CompositorFrameReportingControllerTest, } } -// Tests that EventLatency histograms are not reported when the frame is dropped -// and not presented to the user. +// Tests that EventLatency histograms for events of a dropped frame are reported +// in the first subsequent presented frame. TEST_F(CompositorFrameReportingControllerTest, - EventLatencyForDidNotPresentFrameNotReported) { + EventLatencyForDidNotPresentFrameReportedOnNextPresent) { base::HistogramTester histogram_tester; std::unique_ptr<EventMetrics> event_metrics_ptrs[] = { @@ -1255,25 +1270,58 @@ TEST_F(CompositorFrameReportingControllerTest, EventMetrics::List events_metrics( std::make_move_iterator(std::begin(event_metrics_ptrs)), std::make_move_iterator(std::end(event_metrics_ptrs))); + std::vector<base::TimeTicks> event_times = GetEventTimestamps(events_metrics); // Submit a compositor frame and notify CompositorFrameReporter of the events // affecting the frame. - ++next_token_; - SimulateSubmitCompositorFrame(*next_token_, {std::move(events_metrics), {}}); + SimulateSubmitCompositorFrame({std::move(events_metrics), {}}); // Submit another compositor frame. - ++next_token_; IncrementCurrentId(); - SimulateSubmitCompositorFrame(*next_token_, {}); + SimulateSubmitCompositorFrame({}); - // Present the second compositor frame to the uesr, dropping the first one. + // Present the second compositor frame to the user, dropping the first one. + const base::TimeTicks presentation_time = AdvanceNowByMs(10); viz::FrameTimingDetails details; - details.presentation_feedback.timestamp = AdvanceNowByMs(10); - reporting_controller_.DidPresentCompositorFrame(*next_token_, details); + details.presentation_feedback.timestamp = presentation_time; + reporting_controller_.DidPresentCompositorFrame(*current_token_, details); - // Verify that no EventLatency histogram is recorded. - EXPECT_THAT(histogram_tester.GetTotalCountsForPrefix("EventLatency."), - IsEmpty()); + // Verify that EventLatency histograms for the first frame (dropped) are + // recorded using the presentation time of the second frame (presented). + struct { + const char* name; + const base::HistogramBase::Count count; + } expected_counts[] = { + {"EventLatency.TouchPressed.TotalLatency", 1}, + {"EventLatency.TouchMoved.TotalLatency", 2}, + {"EventLatency.TotalLatency", 3}, + }; + for (const auto& expected_count : expected_counts) { + histogram_tester.ExpectTotalCount(expected_count.name, + expected_count.count); + } + + struct { + const char* name; + const base::HistogramBase::Sample latency_ms; + } expected_latencies[] = { + {"EventLatency.TouchPressed.TotalLatency", + (presentation_time - event_times[0]).InMicroseconds()}, + {"EventLatency.TouchMoved.TotalLatency", + (presentation_time - event_times[1]).InMicroseconds()}, + {"EventLatency.TouchMoved.TotalLatency", + (presentation_time - event_times[2]).InMicroseconds()}, + {"EventLatency.TotalLatency", + (presentation_time - event_times[0]).InMicroseconds()}, + {"EventLatency.TotalLatency", + (presentation_time - event_times[1]).InMicroseconds()}, + {"EventLatency.TotalLatency", + (presentation_time - event_times[2]).InMicroseconds()}, + }; + for (const auto& expected_latency : expected_latencies) { + histogram_tester.ExpectBucketCount(expected_latency.name, + expected_latency.latency_ms, 1); + } } TEST_F(CompositorFrameReportingControllerTest, @@ -1284,7 +1332,8 @@ TEST_F(CompositorFrameReportingControllerTest, // for the pending main-thread frame). SimulateBeginMainFrame(); reporting_controller_.OnFinishImplFrame(current_id_); - reporting_controller_.DidSubmitCompositorFrame(1u, current_id_, {}, {}); + reporting_controller_.DidSubmitCompositorFrame(1u, current_id_, {}, {}, + /*has_missing_content=*/false); viz::FrameTimingDetails details = {}; details.presentation_feedback.timestamp = AdvanceNowByMs(10); reporting_controller_.DidPresentCompositorFrame(1u, details); @@ -1302,16 +1351,16 @@ TEST_F(CompositorFrameReportingControllerTest, // and R2M. SimulateBeginMainFrame(); reporting_controller_.OnFinishImplFrame(current_id_); - reporting_controller_.DidSubmitCompositorFrame(1u, current_id_, previous_id, - {}); + reporting_controller_.DidSubmitCompositorFrame( + 1u, current_id_, previous_id, {}, /*has_missing_content=*/false); details.presentation_feedback.timestamp = AdvanceNowByMs(10); reporting_controller_.DidPresentCompositorFrame(1u, details); // In total, two frames have been completed: R1C, and R1M. // R2C has been presented, but it is blocked on R2M to know whether R2C // contains partial update, or complete updates. So it is kept alive. - EXPECT_EQ(2u, dropped_counter.total_frames()); - EXPECT_EQ(1u, dropped_counter.total_main_dropped()); + EXPECT_EQ(2u, dropped_counter_.total_frames()); + EXPECT_EQ(1u, dropped_counter_.total_main_dropped()); EXPECT_EQ(1u, reporting_controller_.GetBlockingReportersCount()); EXPECT_EQ(1u, reporting_controller_.GetBlockedReportersCount()); @@ -1319,38 +1368,98 @@ TEST_F(CompositorFrameReportingControllerTest, reporting_controller_.SetDroppedFrameCounter(nullptr); } +// Verifies that when a dependent frame is submitted to Viz, but not presented +// (hence dropped), should have its reporter immediately terminated and not +// adopted by the decider reporter. +TEST_F(CompositorFrameReportingControllerTest, + DependentDroppedFrameTerminatesReporterImmediately) { + // Start a frame with main-thread update and let it get stuck in main-thread. + SimulateBeginMainFrame(); + reporting_controller_.OnFinishImplFrame(current_id_); + + // Start another frame that has impl-thread update and submit and present it + // successfully. The reporter for this frame should become dependent of the + // main reporter and adopted by it. + SimulateBeginImplFrame(); + reporting_controller_.OnFinishImplFrame(current_id_); + reporting_controller_.DidSubmitCompositorFrame(1u, current_id_, {}, {}, + /*has_missing_content=*/false); + + viz::FrameTimingDetails details_1 = {}; + details_1.presentation_feedback.timestamp = AdvanceNowByMs(10); + reporting_controller_.DidPresentCompositorFrame(1u, details_1); + + // There should be 1 blocking reporter, 1 blocked reporter, and 1 adopted + // reporter. + EXPECT_EQ(1u, reporting_controller_.GetBlockingReportersCount()); + EXPECT_EQ(1u, reporting_controller_.GetBlockedReportersCount()); + EXPECT_EQ(1u, reporting_controller_.GetAdoptedReportersCount()); + + // At this point no frame has been completed, yet. + EXPECT_EQ(0u, dropped_counter_.total_frames()); + EXPECT_EQ(0u, dropped_counter_.total_compositor_dropped()); + + // Start yet another frame that has impl-thread update and submit it, but with + // failed presentation. The reporter for this frame should become dependent of + // the main reporter, but should terminated immediately upon presentation + // failure, hence not adopted by the main reporter. + SimulateBeginImplFrame(); + reporting_controller_.OnFinishImplFrame(current_id_); + reporting_controller_.DidSubmitCompositorFrame(2u, current_id_, {}, {}, + /*has_missing_content=*/false); + + viz::FrameTimingDetails details_2 = {}; + details_2.presentation_feedback.timestamp = AdvanceNowByMs(10); + details_2.presentation_feedback.flags |= gfx::PresentationFeedback::kFailure; + reporting_controller_.DidPresentCompositorFrame(2u, details_2); + + // There should be still 1 blocking reporter, but 2 blocked reporters. There + // should also be only 1 adopted reporter as the new reporter should not be + // adopted. + EXPECT_EQ(1u, reporting_controller_.GetBlockingReportersCount()); + EXPECT_EQ(2u, reporting_controller_.GetBlockedReportersCount()); + EXPECT_EQ(1u, reporting_controller_.GetAdoptedReportersCount()); + + // At this point 1 frame has been completed and it's a dropped frame. + EXPECT_EQ(1u, dropped_counter_.total_frames()); + EXPECT_EQ(1u, dropped_counter_.total_compositor_dropped()); + + reporting_controller_.ResetReporters(); + reporting_controller_.SetDroppedFrameCounter(nullptr); +} + TEST_F(CompositorFrameReportingControllerTest, SkippedFramesFromDisplayCompositorAreDropped) { // Submit and present two compositor frames. SimulatePresentCompositorFrame(); - EXPECT_EQ(1u, dropped_counter.total_frames()); - EXPECT_EQ(0u, dropped_counter.total_main_dropped()); - EXPECT_EQ(0u, dropped_counter.total_compositor_dropped()); + EXPECT_EQ(1u, dropped_counter_.total_frames()); + EXPECT_EQ(0u, dropped_counter_.total_main_dropped()); + EXPECT_EQ(0u, dropped_counter_.total_compositor_dropped()); SimulatePresentCompositorFrame(); - EXPECT_EQ(2u, dropped_counter.total_frames()); - EXPECT_EQ(0u, dropped_counter.total_main_dropped()); - EXPECT_EQ(0u, dropped_counter.total_compositor_dropped()); + EXPECT_EQ(2u, dropped_counter_.total_frames()); + EXPECT_EQ(0u, dropped_counter_.total_main_dropped()); + EXPECT_EQ(0u, dropped_counter_.total_compositor_dropped()); // Now skip over a few frames, and submit + present another frame. const uint32_t kSkipFrames = 5; for (uint32_t i = 0; i < kSkipFrames; ++i) IncrementCurrentId(); SimulatePresentCompositorFrame(); - EXPECT_EQ(3u + kSkipFrames, dropped_counter.total_frames()); - EXPECT_EQ(0u, dropped_counter.total_main_dropped()); - EXPECT_EQ(kSkipFrames, dropped_counter.total_compositor_dropped()); + EXPECT_EQ(3u + kSkipFrames, dropped_counter_.total_frames()); + EXPECT_EQ(0u, dropped_counter_.total_main_dropped()); + EXPECT_EQ(kSkipFrames, dropped_counter_.total_compositor_dropped()); // Stop requesting frames, skip over a few frames, and submit + present // another frame. There should no new dropped frames. - dropped_counter.Reset(); + dropped_counter_.Reset(); reporting_controller_.OnStoppedRequestingBeginFrames(); for (uint32_t i = 0; i < kSkipFrames; ++i) IncrementCurrentId(); SimulatePresentCompositorFrame(); - EXPECT_EQ(1u, dropped_counter.total_frames()); - EXPECT_EQ(0u, dropped_counter.total_main_dropped()); - EXPECT_EQ(0u, dropped_counter.total_compositor_dropped()); + EXPECT_EQ(1u, dropped_counter_.total_frames()); + EXPECT_EQ(0u, dropped_counter_.total_main_dropped()); + EXPECT_EQ(0u, dropped_counter_.total_compositor_dropped()); reporting_controller_.ResetReporters(); reporting_controller_.SetDroppedFrameCounter(nullptr); @@ -1360,14 +1469,14 @@ TEST_F(CompositorFrameReportingControllerTest, SkippedFramesFromDisplayCompositorAreDroppedUpToLimit) { // Submit and present two compositor frames. SimulatePresentCompositorFrame(); - EXPECT_EQ(1u, dropped_counter.total_frames()); - EXPECT_EQ(0u, dropped_counter.total_main_dropped()); - EXPECT_EQ(0u, dropped_counter.total_compositor_dropped()); + EXPECT_EQ(1u, dropped_counter_.total_frames()); + EXPECT_EQ(0u, dropped_counter_.total_main_dropped()); + EXPECT_EQ(0u, dropped_counter_.total_compositor_dropped()); SimulatePresentCompositorFrame(); - EXPECT_EQ(2u, dropped_counter.total_frames()); - EXPECT_EQ(0u, dropped_counter.total_main_dropped()); - EXPECT_EQ(0u, dropped_counter.total_compositor_dropped()); + EXPECT_EQ(2u, dropped_counter_.total_frames()); + EXPECT_EQ(0u, dropped_counter_.total_main_dropped()); + EXPECT_EQ(0u, dropped_counter_.total_compositor_dropped()); // Now skip over a 101 frames (It should be ignored as it more than 100) // and submit + present another frame. @@ -1376,9 +1485,9 @@ TEST_F(CompositorFrameReportingControllerTest, for (uint32_t i = 0; i < kSkipFrames; ++i) IncrementCurrentId(); SimulatePresentCompositorFrame(); - EXPECT_EQ(3u + kSkipFramesActual, dropped_counter.total_frames()); - EXPECT_EQ(0u, dropped_counter.total_main_dropped()); - EXPECT_EQ(kSkipFramesActual, dropped_counter.total_compositor_dropped()); + EXPECT_EQ(3u + kSkipFramesActual, dropped_counter_.total_frames()); + EXPECT_EQ(0u, dropped_counter_.total_main_dropped()); + EXPECT_EQ(kSkipFramesActual, dropped_counter_.total_compositor_dropped()); } TEST_F(CompositorFrameReportingControllerTest, @@ -1398,7 +1507,7 @@ TEST_F(CompositorFrameReportingControllerTest, reporting_controller_.WillBeginImplFrame(args_1); reporting_controller_.WillBeginMainFrame(args_1); reporting_controller_.OnFinishImplFrame(current_id_1); - EXPECT_EQ(0u, dropped_counter.total_compositor_dropped()); + EXPECT_EQ(0u, dropped_counter_.total_compositor_dropped()); reporting_controller_.DidNotProduceFrame(args_1.frame_id, FrameSkippedReason::kWaitingOnMain); @@ -1416,22 +1525,22 @@ TEST_F(CompositorFrameReportingControllerTest, EXPECT_EQ(3u, reporting_controller_.GetBlockedReportersCount()); // All frames are waiting for the main frame - EXPECT_EQ(0u, dropped_counter.total_main_dropped()); - EXPECT_EQ(0u, dropped_counter.total_compositor_dropped()); - EXPECT_EQ(0u, dropped_counter.total_frames()); + EXPECT_EQ(0u, dropped_counter_.total_main_dropped()); + EXPECT_EQ(0u, dropped_counter_.total_compositor_dropped()); + EXPECT_EQ(0u, dropped_counter_.total_frames()); reporting_controller_.BeginMainFrameAborted(args_1.frame_id); reporting_controller_.DidNotProduceFrame(args_1.frame_id, FrameSkippedReason::kNoDamage); - EXPECT_EQ(0u, dropped_counter.total_compositor_dropped()); + EXPECT_EQ(0u, dropped_counter_.total_compositor_dropped()); // New reporters replace older reporters reporting_controller_.WillBeginImplFrame(args_4); reporting_controller_.WillBeginMainFrame(args_4); - EXPECT_EQ(4u, dropped_counter.total_frames()); - EXPECT_EQ(0u, dropped_counter.total_main_dropped()); - EXPECT_EQ(0u, dropped_counter.total_compositor_dropped()); + EXPECT_EQ(4u, dropped_counter_.total_frames()); + EXPECT_EQ(0u, dropped_counter_.total_main_dropped()); + EXPECT_EQ(0u, dropped_counter_.total_compositor_dropped()); } TEST_F(CompositorFrameReportingControllerTest, @@ -1439,28 +1548,28 @@ TEST_F(CompositorFrameReportingControllerTest, auto thread_type_compositor = FrameSequenceMetrics::ThreadType::kCompositor; reporting_controller_.SetThreadAffectsSmoothness(thread_type_compositor, true); - dropped_counter.OnFcpReceived(); + dropped_counter_.OnFcpReceived(); // Submit and present two compositor frames. SimulatePresentCompositorFrame(); - EXPECT_EQ(1u, dropped_counter.total_frames()); - EXPECT_EQ(0u, dropped_counter.total_main_dropped()); - EXPECT_EQ(0u, dropped_counter.total_compositor_dropped()); + EXPECT_EQ(1u, dropped_counter_.total_frames()); + EXPECT_EQ(0u, dropped_counter_.total_main_dropped()); + EXPECT_EQ(0u, dropped_counter_.total_compositor_dropped()); SimulatePresentCompositorFrame(); - EXPECT_EQ(2u, dropped_counter.total_frames()); - EXPECT_EQ(0u, dropped_counter.total_main_dropped()); - EXPECT_EQ(0u, dropped_counter.total_compositor_dropped()); + EXPECT_EQ(2u, dropped_counter_.total_frames()); + EXPECT_EQ(0u, dropped_counter_.total_main_dropped()); + EXPECT_EQ(0u, dropped_counter_.total_compositor_dropped()); // Now skip over a few frames, and submit + present another frame. const uint32_t kSkipFrames_1 = 5; for (uint32_t i = 0; i < kSkipFrames_1; ++i) IncrementCurrentId(); SimulatePresentCompositorFrame(); - EXPECT_EQ(3u + kSkipFrames_1, dropped_counter.total_frames()); - EXPECT_EQ(0u, dropped_counter.total_main_dropped()); - EXPECT_EQ(kSkipFrames_1, dropped_counter.total_compositor_dropped()); - EXPECT_EQ(kSkipFrames_1, dropped_counter.total_smoothness_dropped()); + EXPECT_EQ(3u + kSkipFrames_1, dropped_counter_.total_frames()); + EXPECT_EQ(0u, dropped_counter_.total_main_dropped()); + EXPECT_EQ(kSkipFrames_1, dropped_counter_.total_compositor_dropped()); + EXPECT_EQ(kSkipFrames_1, dropped_counter_.total_smoothness_dropped()); // Now skip over a few frames which are not affecting smoothness. reporting_controller_.SetThreadAffectsSmoothness(thread_type_compositor, @@ -1469,11 +1578,12 @@ TEST_F(CompositorFrameReportingControllerTest, for (uint32_t i = 0; i < kSkipFrames_2; ++i) IncrementCurrentId(); SimulatePresentCompositorFrame(); // Present another frame. - EXPECT_EQ(4u + kSkipFrames_1 + kSkipFrames_2, dropped_counter.total_frames()); - EXPECT_EQ(0u, dropped_counter.total_main_dropped()); + EXPECT_EQ(4u + kSkipFrames_1 + kSkipFrames_2, + dropped_counter_.total_frames()); + EXPECT_EQ(0u, dropped_counter_.total_main_dropped()); EXPECT_EQ(kSkipFrames_1 + kSkipFrames_2, - dropped_counter.total_compositor_dropped()); - EXPECT_EQ(kSkipFrames_1, dropped_counter.total_smoothness_dropped()); + dropped_counter_.total_compositor_dropped()); + EXPECT_EQ(kSkipFrames_1, dropped_counter_.total_smoothness_dropped()); // Now skip over a few frames more frames which are affecting smoothness. reporting_controller_.SetThreadAffectsSmoothness(thread_type_compositor, @@ -1483,12 +1593,44 @@ TEST_F(CompositorFrameReportingControllerTest, IncrementCurrentId(); SimulatePresentCompositorFrame(); // Present another frame. EXPECT_EQ(5u + kSkipFrames_1 + kSkipFrames_2 + kSkipFrames_3, - dropped_counter.total_frames()); - EXPECT_EQ(0u, dropped_counter.total_main_dropped()); + dropped_counter_.total_frames()); + EXPECT_EQ(0u, dropped_counter_.total_main_dropped()); EXPECT_EQ(kSkipFrames_1 + kSkipFrames_2 + kSkipFrames_3, - dropped_counter.total_compositor_dropped()); + dropped_counter_.total_compositor_dropped()); EXPECT_EQ(kSkipFrames_1 + kSkipFrames_3, - dropped_counter.total_smoothness_dropped()); + dropped_counter_.total_smoothness_dropped()); +} + +// Verifies that presentation feedbacks that arrive out of order are handled +// properly. See crbug.com/1195105 for more details. +TEST_F(CompositorFrameReportingControllerTest, + HandleOutOfOrderPresentationFeedback) { + // Submit three compositor frames without sending back their presentation + // feedbacks. + SimulateSubmitCompositorFrame({}); + + SimulateSubmitCompositorFrame({}); + const uint32_t frame_token_2 = *current_token_; + + SimulateSubmitCompositorFrame({}); + const uint32_t frame_token_3 = *current_token_; + + // Send a failed presentation feedback for frame 2. This should only drop + // frame 2 and leave frame 1 in the queue. + viz::FrameTimingDetails details_2; + details_2.presentation_feedback = {AdvanceNowByMs(10), base::TimeDelta(), + gfx::PresentationFeedback::kFailure}; + reporting_controller_.DidPresentCompositorFrame(frame_token_2, details_2); + DCHECK_EQ(1u, dropped_counter_.total_frames()); + DCHECK_EQ(1u, dropped_counter_.total_compositor_dropped()); + + // Send a successful presentation feedback for frame 3. This should drop frame + // 1. + viz::FrameTimingDetails details_3; + details_3.presentation_feedback.timestamp = AdvanceNowByMs(10); + reporting_controller_.DidPresentCompositorFrame(frame_token_3, details_3); + DCHECK_EQ(3u, dropped_counter_.total_frames()); + DCHECK_EQ(2u, dropped_counter_.total_compositor_dropped()); } } // namespace diff --git a/chromium/cc/metrics/compositor_timing_history.cc b/chromium/cc/metrics/compositor_timing_history.cc index 9dd8e1aa0a4..e8af510dba3 100644 --- a/chromium/cc/metrics/compositor_timing_history.cc +++ b/chromium/cc/metrics/compositor_timing_history.cc @@ -16,7 +16,6 @@ #include "base/trace_event/trace_event.h" #include "cc/debug/rendering_stats_instrumentation.h" #include "cc/metrics/compositor_frame_reporting_controller.h" -#include "third_party/perfetto/protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.pbzero.h" namespace cc { @@ -424,23 +423,6 @@ CompositorTimingHistory::CreateUMAReporter(UMACategory category) { return base::WrapUnique<CompositorTimingHistory::UMAReporter>(nullptr); } -void CompositorTimingHistory::AsProtozeroInto( - perfetto::protos::pbzero::CompositorTimingHistory* state) const { - state->set_begin_main_frame_queue_critical_estimate_delta_us( - BeginMainFrameQueueDurationCriticalEstimate().InMicroseconds()); - state->set_begin_main_frame_queue_not_critical_estimate_delta_us( - BeginMainFrameQueueDurationNotCriticalEstimate().InMicroseconds()); - state->set_begin_main_frame_start_to_ready_to_commit_estimate_delta_us( - BeginMainFrameStartToReadyToCommitDurationEstimate().InMicroseconds()); - state->set_commit_to_ready_to_activate_estimate_delta_us( - CommitToReadyToActivateDurationEstimate().InMicroseconds()); - state->set_prepare_tiles_estimate_delta_us( - PrepareTilesDurationEstimate().InMicroseconds()); - state->set_activate_estimate_delta_us( - ActivateDurationEstimate().InMicroseconds()); - state->set_draw_estimate_delta_us(DrawDurationEstimate().InMicroseconds()); -} - base::TimeTicks CompositorTimingHistory::Now() const { return base::TimeTicks::Now(); } @@ -511,6 +493,37 @@ base::TimeDelta CompositorTimingHistory::DrawDurationEstimate() const { return draw_duration_history_.Percentile(kDrawEstimationPercentile); } +base::TimeDelta +CompositorTimingHistory::BeginMainFrameStartToReadyToCommitCriticalEstimate() + const { + return BeginMainFrameStartToReadyToCommitDurationEstimate() + + BeginMainFrameQueueDurationCriticalEstimate(); +} + +base::TimeDelta +CompositorTimingHistory::BeginMainFrameStartToReadyToCommitNotCriticalEstimate() + const { + return BeginMainFrameStartToReadyToCommitDurationEstimate() + + BeginMainFrameQueueDurationNotCriticalEstimate(); +} + +base::TimeDelta +CompositorTimingHistory::BeginMainFrameQueueToActivateCriticalEstimate() const { + return BeginMainFrameStartToReadyToCommitDurationEstimate() + + CommitDurationEstimate() + CommitToReadyToActivateDurationEstimate() + + ActivateDurationEstimate() + + BeginMainFrameQueueDurationCriticalEstimate(); +} + +base::TimeDelta +CompositorTimingHistory::BeginMainFrameQueueToActivateNotCriticalEstimate() + const { + return BeginMainFrameStartToReadyToCommitDurationEstimate() + + CommitDurationEstimate() + CommitToReadyToActivateDurationEstimate() + + ActivateDurationEstimate() + + BeginMainFrameQueueDurationNotCriticalEstimate(); +} + void CompositorTimingHistory::WillBeginImplFrame( const viz::BeginFrameArgs& args, base::TimeTicks now) { @@ -776,10 +789,11 @@ void CompositorTimingHistory::DidSubmitCompositorFrame( uint32_t frame_token, const viz::BeginFrameId& current_frame_id, const viz::BeginFrameId& last_activated_frame_id, - EventMetricsSet events_metrics) { + EventMetricsSet events_metrics, + bool has_missing_content) { compositor_frame_reporting_controller_->DidSubmitCompositorFrame( frame_token, current_frame_id, last_activated_frame_id, - std::move(events_metrics)); + std::move(events_metrics), has_missing_content); } void CompositorTimingHistory::DidNotProduceFrame( diff --git a/chromium/cc/metrics/compositor_timing_history.h b/chromium/cc/metrics/compositor_timing_history.h index 15b1ff0050f..275e6f4c4cc 100644 --- a/chromium/cc/metrics/compositor_timing_history.h +++ b/chromium/cc/metrics/compositor_timing_history.h @@ -52,9 +52,6 @@ class CC_EXPORT CompositorTimingHistory { CompositorTimingHistory& operator=(const CompositorTimingHistory&) = delete; - void AsProtozeroInto( - perfetto::protos::pbzero::CompositorTimingHistory* state) const; - // The main thread responsiveness depends heavily on whether or not the // on_critical_path flag is set, so we record response times separately. virtual base::TimeDelta BeginMainFrameQueueDurationCriticalEstimate() const; @@ -68,6 +65,11 @@ class CC_EXPORT CompositorTimingHistory { virtual base::TimeDelta ActivateDurationEstimate() const; virtual base::TimeDelta DrawDurationEstimate() const; + base::TimeDelta BeginMainFrameStartToReadyToCommitCriticalEstimate() const; + base::TimeDelta BeginMainFrameStartToReadyToCommitNotCriticalEstimate() const; + base::TimeDelta BeginMainFrameQueueToActivateCriticalEstimate() const; + base::TimeDelta BeginMainFrameQueueToActivateNotCriticalEstimate() const; + // State that affects when events should be expected/recorded/reported. void SetRecordingEnabled(bool enabled); @@ -95,7 +97,8 @@ class CC_EXPORT CompositorTimingHistory { uint32_t frame_token, const viz::BeginFrameId& current_frame_id, const viz::BeginFrameId& last_activated_frame_id, - EventMetricsSet events_metrics); + EventMetricsSet events_metrics, + bool has_missing_content); void DidNotProduceFrame(const viz::BeginFrameId& id, FrameSkippedReason skip_reason); void DidPresentCompositorFrame(uint32_t frame_token, diff --git a/chromium/cc/metrics/dropped_frame_counter.cc b/chromium/cc/metrics/dropped_frame_counter.cc index 357acc6e903..be75c7ceb6a 100644 --- a/chromium/cc/metrics/dropped_frame_counter.cc +++ b/chromium/cc/metrics/dropped_frame_counter.cc @@ -228,6 +228,16 @@ void DroppedFrameCounter::ReportFrames() { static_cast<double>(total_smoothness_dropped_) * 100 / total_frames; smoothness_data.worst_smoothness = sliding_window_max_percent_dropped_; smoothness_data.percentile_95 = sliding_window_95pct_percent_dropped; + + if (sliding_window_max_percent_dropped_After_1_sec_.has_value()) + smoothness_data.worst_smoothness_after1sec = + sliding_window_max_percent_dropped_After_1_sec_.value(); + if (sliding_window_max_percent_dropped_After_2_sec_.has_value()) + smoothness_data.worst_smoothness_after2sec = + sliding_window_max_percent_dropped_After_2_sec_.value(); + if (sliding_window_max_percent_dropped_After_5_sec_.has_value()) + smoothness_data.worst_smoothness_after5sec = + sliding_window_max_percent_dropped_After_5_sec_.value(); smoothness_data.time_max_delta = time_max_delta_; ukm_smoothness_data_->Write(smoothness_data); } @@ -258,6 +268,9 @@ void DroppedFrameCounter::Reset() { total_dropped_ = 0; total_smoothness_dropped_ = 0; sliding_window_max_percent_dropped_ = 0; + sliding_window_max_percent_dropped_After_1_sec_.reset(); + sliding_window_max_percent_dropped_After_2_sec_.reset(); + sliding_window_max_percent_dropped_After_5_sec_.reset(); dropped_frame_count_in_window_ = 0; fcp_received_ = false; sliding_window_ = {}; @@ -332,6 +345,7 @@ void DroppedFrameCounter::NotifyFrameResult(const viz::BeginFrameArgs& args, time_max_delta_ = args.frame_time - time_fcp_received_; sliding_window_max_percent_dropped_ = percent_dropped_frame; } + UpdateMaxPercentDroppedFrame(percent_dropped_frame); latest_sliding_window_start_ = last_timestamp; latest_sliding_window_interval_ = remaining_oldest_args.interval; @@ -341,6 +355,27 @@ void DroppedFrameCounter::NotifyFrameResult(const viz::BeginFrameArgs& args, ++dropped_frame_count_in_window_; } +void DroppedFrameCounter::UpdateMaxPercentDroppedFrame( + double percent_dropped_frame) { + if (!fcp_received_) + return; + + const auto fcp_time_delta = base::TimeTicks::Now() - time_fcp_received_; + + if (fcp_time_delta > base::TimeDelta::FromSeconds(1)) + sliding_window_max_percent_dropped_After_1_sec_ = + std::max(sliding_window_max_percent_dropped_After_1_sec_.value_or(0.0), + percent_dropped_frame); + if (fcp_time_delta > base::TimeDelta::FromSeconds(2)) + sliding_window_max_percent_dropped_After_2_sec_ = + std::max(sliding_window_max_percent_dropped_After_2_sec_.value_or(0.0), + percent_dropped_frame); + if (fcp_time_delta > base::TimeDelta::FromSeconds(5)) + sliding_window_max_percent_dropped_After_5_sec_ = + std::max(sliding_window_max_percent_dropped_After_5_sec_.value_or(0.0), + percent_dropped_frame); +} + void DroppedFrameCounter::OnFcpReceived() { fcp_received_ = true; time_fcp_received_ = base::TimeTicks::Now(); diff --git a/chromium/cc/metrics/dropped_frame_counter.h b/chromium/cc/metrics/dropped_frame_counter.h index 7126c32c46d..aee1839db46 100644 --- a/chromium/cc/metrics/dropped_frame_counter.h +++ b/chromium/cc/metrics/dropped_frame_counter.h @@ -6,10 +6,12 @@ #define CC_METRICS_DROPPED_FRAME_COUNTER_H_ #include <stddef.h> +#include <map> #include <queue> #include <utility> #include "base/containers/ring_buffer.h" +#include "base/optional.h" #include "cc/cc_export.h" #include "cc/metrics/frame_sorter.h" #include "cc/metrics/ukm_smoothness_data.h" @@ -102,6 +104,8 @@ class CC_EXPORT DroppedFrameCounter { void NotifyFrameResult(const viz::BeginFrameArgs& args, bool is_dropped); base::TimeDelta ComputeCurrentWindowSize() const; + void UpdateMaxPercentDroppedFrame(double percent_dropped_frame); + const base::TimeDelta kSlidingWindowInterval = base::TimeDelta::FromSeconds(1); std::queue<std::pair<const viz::BeginFrameArgs, bool>> sliding_window_; @@ -119,6 +123,9 @@ class CC_EXPORT DroppedFrameCounter { size_t total_smoothness_dropped_ = 0; bool fcp_received_ = false; double sliding_window_max_percent_dropped_ = 0; + base::Optional<double> sliding_window_max_percent_dropped_After_1_sec_; + base::Optional<double> sliding_window_max_percent_dropped_After_2_sec_; + base::Optional<double> sliding_window_max_percent_dropped_After_5_sec_; base::TimeTicks time_fcp_received_; base::TimeDelta time_max_delta_; UkmSmoothnessDataShared* ukm_smoothness_data_ = nullptr; diff --git a/chromium/cc/metrics/frame_sequence_tracker.cc b/chromium/cc/metrics/frame_sequence_tracker.cc index 9576ad3b63d..ce16e4ef9db 100644 --- a/chromium/cc/metrics/frame_sequence_tracker.cc +++ b/chromium/cc/metrics/frame_sequence_tracker.cc @@ -142,7 +142,8 @@ void FrameSequenceTracker::ReportBeginImplFrame( DCHECK(!compositor_frame_submitted_) << TRACKER_DCHECK_MSG; UpdateTrackedFrameData(&begin_impl_frame_data_, args.frame_id.source_id, - args.frame_id.sequence_number); + args.frame_id.sequence_number, + args.frames_throttled_since_last); impl_throughput().frames_expected += begin_impl_frame_data_.previous_sequence_delta; #if DCHECK_IS_ON() @@ -188,7 +189,8 @@ void FrameSequenceTracker::ReportBeginMainFrame( awaiting_main_response_sequence_ = args.frame_id.sequence_number; UpdateTrackedFrameData(&begin_main_frame_data_, args.frame_id.source_id, - args.frame_id.sequence_number); + args.frame_id.sequence_number, + args.frames_throttled_since_last); if (!first_received_main_sequence_ || first_received_main_sequence_ <= last_no_main_damage_sequence_) { first_received_main_sequence_ = args.frame_id.sequence_number; @@ -639,12 +641,15 @@ void FrameSequenceTracker::PauseFrameProduction() { reset_all_state_ = true; } -void FrameSequenceTracker::UpdateTrackedFrameData(TrackedFrameData* frame_data, - uint64_t source_id, - uint64_t sequence_number) { +void FrameSequenceTracker::UpdateTrackedFrameData( + TrackedFrameData* frame_data, + uint64_t source_id, + uint64_t sequence_number, + uint64_t throttled_frame_count) { if (frame_data->previous_sequence && frame_data->previous_source == source_id) { - uint32_t current_latency = sequence_number - frame_data->previous_sequence; + uint32_t current_latency = + sequence_number - frame_data->previous_sequence - throttled_frame_count; DCHECK_GT(current_latency, 0u) << TRACKER_DCHECK_MSG; frame_data->previous_sequence_delta = current_latency; } else { diff --git a/chromium/cc/metrics/frame_sequence_tracker.h b/chromium/cc/metrics/frame_sequence_tracker.h index 387a50b71f3..d343c4ff574 100644 --- a/chromium/cc/metrics/frame_sequence_tracker.h +++ b/chromium/cc/metrics/frame_sequence_tracker.h @@ -156,7 +156,8 @@ class CC_EXPORT FrameSequenceTracker { void UpdateTrackedFrameData(TrackedFrameData* frame_data, uint64_t source_id, - uint64_t sequence_number); + uint64_t sequence_number, + uint64_t throttled_frame_count); bool ShouldIgnoreBeginFrameSource(uint64_t source_id) const; diff --git a/chromium/cc/metrics/frame_sequence_tracker_collection.cc b/chromium/cc/metrics/frame_sequence_tracker_collection.cc index 22c7941a8ac..263c4215ef1 100644 --- a/chromium/cc/metrics/frame_sequence_tracker_collection.cc +++ b/chromium/cc/metrics/frame_sequence_tracker_collection.cc @@ -65,6 +65,8 @@ FrameSequenceTracker* FrameSequenceTrackerCollection::StartSequenceInternal( if (IsScrollType(type)) { DCHECK_NE(scrolling_thread, ThreadType::kUnknown); metrics->SetScrollingThread(scrolling_thread); + compositor_frame_reporting_controller_->SetScrollingThread( + scrolling_thread); } if (metrics->GetEffectiveThread() == ThreadType::kCompositor) { @@ -117,6 +119,8 @@ void FrameSequenceTrackerCollection::StopSequence( auto key = std::make_pair(type, ThreadType::kUnknown); if (IsScrollType(type)) { + compositor_frame_reporting_controller_->SetScrollingThread( + ThreadType::kUnknown); key = std::make_pair(type, ThreadType::kCompositor); if (!frame_trackers_.contains(key)) key = std::make_pair(type, ThreadType::kMain); diff --git a/chromium/cc/metrics/jank_injector.cc b/chromium/cc/metrics/jank_injector.cc index 12c13e41cc5..1ffcbfbe194 100644 --- a/chromium/cc/metrics/jank_injector.cc +++ b/chromium/cc/metrics/jank_injector.cc @@ -45,10 +45,12 @@ struct JankInjectionParams { bool busy_loop = true; }; +bool g_jank_enabled_for_test = false; + bool IsJankInjectionEnabled() { static bool enabled = base::FeatureList::IsEnabled(features::kJankInjectionAblationFeature); - return enabled; + return enabled || g_jank_enabled_for_test; } using AllowedURLsMap = std::map<std::string, std::vector<std::string>>; @@ -105,6 +107,16 @@ void RunJank(JankInjectionParams params) { } // namespace +ScopedJankInjectionEnabler::ScopedJankInjectionEnabler() { + DCHECK(!g_jank_enabled_for_test); + g_jank_enabled_for_test = true; +} + +ScopedJankInjectionEnabler::~ScopedJankInjectionEnabler() { + DCHECK(g_jank_enabled_for_test); + g_jank_enabled_for_test = false; +} + JankInjector::JankInjector() { if (IsJankInjectionEnabled()) { config_.target_dropped_frames_percent = diff --git a/chromium/cc/metrics/jank_injector.h b/chromium/cc/metrics/jank_injector.h index b27bf2143c9..5b3f650ed1e 100644 --- a/chromium/cc/metrics/jank_injector.h +++ b/chromium/cc/metrics/jank_injector.h @@ -15,6 +15,16 @@ class GURL; namespace cc { +class CC_EXPORT ScopedJankInjectionEnabler { + public: + ScopedJankInjectionEnabler(); + ~ScopedJankInjectionEnabler(); + + ScopedJankInjectionEnabler(const ScopedJankInjectionEnabler&) = delete; + ScopedJankInjectionEnabler& operator=(const ScopedJankInjectionEnabler&) = + delete; +}; + class CC_EXPORT JankInjector { public: struct CC_EXPORT JankConfig { diff --git a/chromium/cc/metrics/jank_injector_unittest.cc b/chromium/cc/metrics/jank_injector_unittest.cc index cbc50ccf272..a6587373c2d 100644 --- a/chromium/cc/metrics/jank_injector_unittest.cc +++ b/chromium/cc/metrics/jank_injector_unittest.cc @@ -27,6 +27,7 @@ TEST_F(JankInjectorTest, Basic) { scoped_refptr<base::TestSimpleTaskRunner> task_runner( new base::TestSimpleTaskRunner()); + ScopedJankInjectionEnabler enable_jank; JankInjector injector; const auto& config = injector.config(); EXPECT_EQ(config.target_dropped_frames_percent, 10u); diff --git a/chromium/cc/metrics/ukm_smoothness_data.h b/chromium/cc/metrics/ukm_smoothness_data.h index eb74020c249..b1c05a42ca8 100644 --- a/chromium/cc/metrics/ukm_smoothness_data.h +++ b/chromium/cc/metrics/ukm_smoothness_data.h @@ -15,6 +15,13 @@ namespace cc { struct UkmSmoothnessData { double avg_smoothness = 0.0; double worst_smoothness = 0.0; + + // Values are set to -1 to help with recognizing when these metrics are not + // calculated. + double worst_smoothness_after1sec = -1.0; + double worst_smoothness_after2sec = -1.0; + double worst_smoothness_after5sec = -1.0; + double above_threshold = 0.0; double percentile_95 = 0.0; base::TimeDelta time_max_delta = base::TimeDelta::FromMilliseconds(1); 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 467fac7f00f..3615ceee935 100644 --- a/chromium/cc/mojo_embedder/async_layer_tree_frame_sink.cc +++ b/chromium/cc/mojo_embedder/async_layer_tree_frame_sink.cc @@ -277,6 +277,11 @@ void AsyncLayerTreeFrameSink::ReclaimResources( client_->ReclaimResources(resources); } +void AsyncLayerTreeFrameSink::OnCompositorFrameTransitionDirectiveProcessed( + uint32_t sequence_id) { + client_->OnCompositorFrameTransitionDirectiveProcessed(sequence_id); +} + void AsyncLayerTreeFrameSink::OnNeedsBeginFrames(bool needs_begin_frames) { DCHECK(compositor_frame_sink_ptr_); if (needs_begin_frames_ != needs_begin_frames) { diff --git a/chromium/cc/mojo_embedder/async_layer_tree_frame_sink.h b/chromium/cc/mojo_embedder/async_layer_tree_frame_sink.h index c7efb10500a..2ca8e8b68a3 100644 --- a/chromium/cc/mojo_embedder/async_layer_tree_frame_sink.h +++ b/chromium/cc/mojo_embedder/async_layer_tree_frame_sink.h @@ -107,6 +107,8 @@ class CC_MOJO_EMBEDDER_EXPORT AsyncLayerTreeFrameSink void OnBeginFramePausedChanged(bool paused) override; void ReclaimResources( const std::vector<viz::ReturnedResource>& resources) override; + void OnCompositorFrameTransitionDirectiveProcessed( + uint32_t sequence_id) override; // ExternalBeginFrameSourceClient implementation. void OnNeedsBeginFrames(bool needs_begin_frames) override; diff --git a/chromium/cc/paint/oop_pixeltest.cc b/chromium/cc/paint/oop_pixeltest.cc index d5eec1c990b..63986010b93 100644 --- a/chromium/cc/paint/oop_pixeltest.cc +++ b/chromium/cc/paint/oop_pixeltest.cc @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <memory> #include <tuple> #include <vector> @@ -46,6 +47,10 @@ #include "ui/gfx/skia_util.h" #include "ui/gl/gl_implementation.h" +#if defined(OS_ANDROID) +#include "base/android/build_info.h" +#endif + namespace cc { namespace { scoped_refptr<DisplayItemList> MakeNoopDisplayItemList() { @@ -75,10 +80,10 @@ class OopPixelTest : public testing::Test, DCHECK_EQ(result, gpu::ContextResult::kSuccess); const int gles2_max_texture_size = gles2_context_provider_->ContextCapabilities().max_texture_size; - gpu_image_cache_.reset(new GpuImageDecodeCache( + gpu_image_cache_ = std::make_unique<GpuImageDecodeCache>( gles2_context_provider_.get(), false, kRGBA_8888_SkColorType, kWorkingSetSize, gles2_max_texture_size, - PaintImage::kDefaultGeneratorClientId, nullptr)); + PaintImage::kDefaultGeneratorClientId, nullptr); const int raster_max_texture_size = raster_context_provider_->ContextCapabilities().max_texture_size; @@ -102,10 +107,10 @@ class OopPixelTest : public testing::Test, DCHECK_EQ(result, gpu::ContextResult::kSuccess); const int raster_max_texture_size = raster_context_provider_->ContextCapabilities().max_texture_size; - oop_image_cache_.reset(new GpuImageDecodeCache( + oop_image_cache_ = std::make_unique<GpuImageDecodeCache>( raster_context_provider_.get(), true, kRGBA_8888_SkColorType, kWorkingSetSize, raster_max_texture_size, - PaintImage::kDefaultGeneratorClientId, nullptr)); + PaintImage::kDefaultGeneratorClientId, nullptr); } class RasterOptions { @@ -174,16 +179,20 @@ class OopPixelTest : public testing::Test, if (options.preclear) { raster_implementation->BeginRasterCHROMIUM( - options.preclear_color, options.msaa_sample_count, - options.use_lcd_text, options.color_space, mailbox.name); + options.preclear_color, /*needs_clear=*/options.preclear, + options.msaa_sample_count, options.use_lcd_text, options.color_space, + mailbox.name); raster_implementation->EndRasterCHROMIUM(); } // "Out of process" raster! \o/ - + // If |options.preclear| is true, the mailbox has already been cleared by + // the BeginRasterCHROMIUM call above, and we want to test that it is indeed + // cleared, so set |needs_clear| to false here. raster_implementation->BeginRasterCHROMIUM( - options.background_color, options.msaa_sample_count, - options.use_lcd_text, options.color_space, mailbox.name); + options.background_color, /*needs_clear=*/!options.preclear, + options.msaa_sample_count, options.use_lcd_text, options.color_space, + mailbox.name); size_t max_op_size_limit = gpu::raster::RasterInterface::kDefaultMaxOpSizeHint; raster_implementation->RasterCHROMIUM( @@ -329,7 +338,8 @@ class OopPixelTest : public testing::Test, options.resource_size.width(), options.resource_size.height(), options.color_space.ToSkColorSpace()); auto surface = SkSurface::MakeRenderTarget( - gles2_context_provider_->GrContext(), SkBudgeted::kYes, image_info); + gles2_context_provider_->GrContext(), SkBudgeted::kYes, image_info, 0, + &surface_props); SkCanvas* canvas = surface->getCanvas(); if (options.preclear) canvas->drawColor(options.preclear_color); @@ -360,11 +370,22 @@ class OopPixelTest : public testing::Test, void ExpectEquals(SkBitmap actual, SkBitmap expected, const char* label = nullptr) { + ExactPixelComparator exact(/* discard_alpha */ false); + ExpectEquals(actual, expected, exact, label); + } + + void ExpectEquals(SkBitmap actual, + SkBitmap expected, + const PixelComparator& comparator, + const char* label = nullptr) { EXPECT_EQ(actual.dimensions(), expected.dimensions()); + + // We don't just use MatchesBitmap so that we can control logging output. + if (comparator.Compare(actual, expected)) + return; + auto expected_url = GetPNGDataUrl(expected); auto actual_url = GetPNGDataUrl(actual); - if (actual_url == expected_url) - return; if (label) { ADD_FAILURE() << "\nCase: " << label << "\nExpected: " << expected_url << "\nActual: " << actual_url; @@ -1655,48 +1676,47 @@ sk_sp<SkTextBlob> BuildTextBlob( SkFont font; font.setTypeface(typeface); font.setHinting(SkFontHinting::kNormal); - font.setSize(1u); + font.setSize(8.f); if (use_lcd_text) { font.setSubpixel(true); font.setEdging(SkFont::Edging::kSubpixelAntiAlias); } - SkTextBlobBuilder builder; - const int glyphCount = 10; - const auto& runBuffer = builder.allocRunPosH(font, glyphCount, 0); - for (int i = 0; i < glyphCount; i++) { - runBuffer.glyphs[i] = static_cast<SkGlyphID>(i); - runBuffer.pos[i] = SkIntToScalar(i); - } - return builder.make(); + return SkTextBlob::MakeFromString("Hamburgefons", font); } -TEST_F(OopPixelTest, DrawTextBlob) { - RasterOptions options; - options.resource_size = gfx::Size(100, 100); - options.content_size = options.resource_size; - options.full_raster_rect = gfx::Rect(options.content_size); - options.playback_rect = options.full_raster_rect; - options.color_space = gfx::ColorSpace::CreateSRGB(); - - auto display_item_list = base::MakeRefCounted<DisplayItemList>(); - display_item_list->StartPaint(); - PaintFlags flags; - flags.setStyle(PaintFlags::kFill_Style); - flags.setColor(SK_ColorGREEN); - display_item_list->push<DrawTextBlobOp>(BuildTextBlob(), 0u, 0u, flags); - display_item_list->EndPaintOfUnpaired(options.full_raster_rect); - display_item_list->Finalize(); +// A reasonable Y offset given the font parameters of BuildTextBlob() that +// ensures the text is not just drawn above the top edge of the surface. +static constexpr SkScalar kTextBlobY = 16.f; + +// OopTextBlobPixelTest's test suite runs through the cross product of these +// strategies. +enum class TextBlobStrategy { + kDirect, // DrawTextBlobOp directly in the display list + kDrawRecord, // DrawRecordOp where the paint record includes text + kRecordShader, // DrawRectOp where the paint has a RecordShader with text + kRecordFilter // DrawRectOp where the paint has a RecordFilter with text +}; +enum class FilterStrategy { + kNone, // No additional PaintFilter interacting with text + kPaintFlags, // A blur is added to the PaintFlags of the draw + kSaveLayer // An explicit save layer with blur is made before the draw +}; +enum class MatrixStrategy { + kIdentity, // Identity matrix (no extra scale factor for text then) + kScaled, // Matrix is an axis-aligned scale factor + kComplex, // Matrix is not axis-aligned and scale must be decomposed + kPerspective, // Matrix has perspective and an approximate scale is needed +}; +enum class LCDStrategy { kNo, kYes }; - auto actual = Raster(display_item_list, options); - auto expected = RasterExpectedBitmap(display_item_list, options); - ExpectEquals(actual, expected); -} +using TextBlobTestConfig = ::testing:: + tuple<TextBlobStrategy, FilterStrategy, MatrixStrategy, LCDStrategy>; -class OopRecordShaderPixelTest : public OopPixelTest, - public ::testing::WithParamInterface<bool> { +class OopTextBlobPixelTest + : public OopPixelTest, + public ::testing::WithParamInterface<TextBlobTestConfig> { public: - bool UseLcdText() const { return GetParam(); } void RunTest() { RasterOptions options; options.resource_size = gfx::Size(100, 100); @@ -1706,90 +1726,272 @@ class OopRecordShaderPixelTest : public OopPixelTest, options.color_space = gfx::ColorSpace::CreateSRGB(); options.use_lcd_text = UseLcdText(); - auto paint_record = sk_make_sp<PaintOpBuffer>(); - PaintFlags flags; - flags.setStyle(PaintFlags::kFill_Style); - flags.setColor(SK_ColorGREEN); - paint_record->push<DrawTextBlobOp>( - BuildTextBlob(SkTypeface::MakeDefault(), UseLcdText()), 0u, 0u, flags); - auto paint_record_shader = PaintShader::MakePaintRecord( - paint_record, SkRect::MakeWH(25, 25), SkTileMode::kRepeat, - SkTileMode::kRepeat, nullptr, - PaintShader::ScalingBehavior::kRasterAtScale); - // Force paint_flags to convert this to kFixedScale, so we can safely - // compare pixels between direct and oop-r modes (since oop will convert to - // kFixedScale no matter what. - paint_record_shader->set_has_animated_images(true); - auto display_item_list = base::MakeRefCounted<DisplayItemList>(); display_item_list->StartPaint(); - display_item_list->push<ScaleOp>(2.f, 2.f); - PaintFlags shader_flags; - shader_flags.setShader(paint_record_shader); - display_item_list->push<DrawRectOp>(SkRect::MakeWH(50, 50), shader_flags); + + // Set matrix before any image filter is applied, which may force the + // matrix to be decomposed into a transform compatible with the filter. + SetMatrix(display_item_list); + + const bool save_layer = + GetFilterStrategy(GetParam()) == FilterStrategy::kSaveLayer; + sk_sp<PaintFilter> filter = MakeFilter(); + if (save_layer) { + PaintFlags layer_flags; + layer_flags.setImageFilter(std::move(filter)); + filter = nullptr; + display_item_list->push<SaveLayerOp>(nullptr, &layer_flags); + } + + PushDrawOp(display_item_list, std::move(filter)); + + if (save_layer) { + display_item_list->push<RestoreOp>(); + } + display_item_list->EndPaintOfUnpaired(options.full_raster_rect); display_item_list->Finalize(); auto actual = Raster(display_item_list, options); auto expected = RasterExpectedBitmap(display_item_list, options); - ExpectEquals(actual, expected); + + // Drawing text into an image and then transforming that can lead to small + // flakiness in devices, although in practice they are very imperceptible, + // and distinctly different from using the wrong glyph or text params. + float error_pixels_percentage = 0.f; + int max_abs_error = 0; +#if defined(OS_ANDROID) + // The nexus5 and nexus5x bots are particularly susceptible to small changes + // when bilerping an image (not visible). + const int sdk = base::android::BuildInfo::GetInstance()->sdk_int(); + if (sdk <= base::android::SDK_VERSION_MARSHMALLOW) { + error_pixels_percentage = 10.f; + max_abs_error = 16; + } else { + // Newer OSes occasionally have smaller flakes when using the real GPU + error_pixels_percentage = 1.5f; + max_abs_error = 2; + } +#elif defined(OS_MAC) || defined(OS_WIN) + // Mac and Windows need very small tolerances only under complex transforms + if (GetMatrixStrategy(GetParam()) == MatrixStrategy::kComplex) { + error_pixels_percentage = 0.2f; + max_abs_error = 2; + } +#endif + // Regardless of OS, perspective triggers path rendering for each glyph, + // which produces its own set of pixel differences. + if (GetMatrixStrategy(GetParam()) == MatrixStrategy::kPerspective) { + error_pixels_percentage = 4.f; + max_abs_error = 36; + } + + FuzzyPixelComparator comparator( + /*discard_alpha=*/false, + /*error_pixels_percentage_limit=*/error_pixels_percentage, + /*small_error_pixels_percentage_limit=*/0.0f, + /*avg_abs_error_limit=*/max_abs_error, + /*max_abs_error_limit=*/max_abs_error, + /*small_error_threshold=*/0); + ExpectEquals(actual, expected, comparator); } -}; -TEST_P(OopRecordShaderPixelTest, ShaderWithTextScaled) { - RunTest(); -} + sk_sp<PaintFilter> MakeFilter() { + if (GetFilterStrategy(GetParam()) == FilterStrategy::kNone) { + return nullptr; + } else { + // Keep the blur sigmas small to reduce test duration, it's the presence + // of the blur filter that triggers the code path changes we care about. + return sk_make_sp<BlurPaintFilter>(.1f, .1f, SkTileMode::kDecal, nullptr); + } + } -class OopRecordFilterPixelTest : public OopPixelTest, - public ::testing::WithParamInterface<bool> { - public: - bool UseLcdText() const { return GetParam(); } - void RunTest(const SkM44& mat) { - RasterOptions options; - options.resource_size = gfx::Size(100, 100); - options.content_size = options.resource_size; - options.full_raster_rect = gfx::Rect(options.content_size); - options.playback_rect = options.full_raster_rect; - options.color_space = gfx::ColorSpace::CreateSRGB(); - options.use_lcd_text = UseLcdText(); + void SetMatrix(scoped_refptr<DisplayItemList> display_list) { + MatrixStrategy strategy = GetMatrixStrategy(GetParam()); + + SkM44 m; // Default constructed to identity + if (strategy != MatrixStrategy::kIdentity) { + // Scaled, Complex, and Perspective all have a 2x scale factor + m.preScale(2.0f, 2.0f); + if (strategy == MatrixStrategy::kComplex) { + SkM44 skew = SkM44(); + skew.setRC(0, 1, 2.f); + skew.setRC(1, 0, 2.f); + m.preConcat(skew); + } else if (strategy == MatrixStrategy::kPerspective) { + SkM44 persp = SkM44::Perspective(0.01f, 10.f, SK_ScalarPI / 3.f); + persp.preTranslate(0.f, 5.f, -0.1f); + persp.preConcat(SkM44::Rotate({0.f, 1.f, 0.f}, 0.008f /* radians */)); + m.postConcat(persp); + } + } + display_list->push<ConcatOp>(m); + } + + void PushDrawOp(scoped_refptr<DisplayItemList> display_list, + sk_sp<PaintFilter> filter) { + TextBlobStrategy strategy = GetTextBlobStrategy(GetParam()); + + auto text_blob = BuildTextBlob(SkTypeface::MakeDefault(), UseLcdText()); + + PaintFlags text_flags; + text_flags.setStyle(PaintFlags::kFill_Style); + text_flags.setColor(SK_ColorGREEN); + if (filter && (strategy == TextBlobStrategy::kDirect || + strategy == TextBlobStrategy::kDrawRecord)) { + // If there's a filter, the only PaintFlags that are available for these + // two text-drawing strategies is 'text_flags'. + text_flags.setImageFilter(std::move(filter)); + filter = nullptr; + } + if (strategy == TextBlobStrategy::kDirect) { + display_list->push<DrawTextBlobOp>(std::move(text_blob), 0u, kTextBlobY, + text_flags); + return; + } + + // All remaining strategies add the DrawTextBlobOp to an inner paint record. auto paint_record = sk_make_sp<PaintOpBuffer>(); - PaintFlags flags; - flags.setStyle(PaintFlags::kFill_Style); - flags.setColor(SK_ColorGREEN); - paint_record->push<DrawTextBlobOp>( - BuildTextBlob(SkTypeface::MakeDefault(), UseLcdText()), 0u, 0u, flags); - auto paint_record_filter = - sk_make_sp<RecordPaintFilter>(paint_record, SkRect::MakeWH(100, 100)); + paint_record->push<DrawTextBlobOp>(std::move(text_blob), 0u, kTextBlobY, + text_flags); + if (strategy == TextBlobStrategy::kDrawRecord) { + display_list->push<DrawRecordOp>(std::move(paint_record)); + return; + } - auto display_item_list = base::MakeRefCounted<DisplayItemList>(); - display_item_list->StartPaint(); - display_item_list->push<SetMatrixOp>(mat); - PaintFlags shader_flags; - shader_flags.setImageFilter(paint_record_filter); - display_item_list->push<DrawRectOp>(SkRect::MakeWH(50, 50), shader_flags); - display_item_list->EndPaintOfUnpaired(options.full_raster_rect); - display_item_list->Finalize(); + PaintFlags record_flags; + if (strategy == TextBlobStrategy::kRecordShader) { + auto paint_record_shader = PaintShader::MakePaintRecord( + paint_record, SkRect::MakeWH(25, 25), SkTileMode::kRepeat, + SkTileMode::kRepeat, nullptr, + PaintShader::ScalingBehavior::kRasterAtScale); + // Force paint_flags to convert this to kFixedScale, so we can safely + // compare pixels between direct and oop-r modes (since oop will convert + // to kFixedScale no matter what. + paint_record_shader->set_has_animated_images(true); + + record_flags.setShader(paint_record_shader); + record_flags.setImageFilter(std::move(filter)); + } else { + DCHECK(strategy == TextBlobStrategy::kRecordFilter); + + sk_sp<PaintFilter> paint_record_filter = + sk_make_sp<RecordPaintFilter>(paint_record, SkRect::MakeWH(100, 100)); + // If there's an additional filter, we have to compose it with the + // paint record filter. + if (filter) { + paint_record_filter = sk_make_sp<ComposePaintFilter>( + std::move(filter), std::move(paint_record_filter)); + } + record_flags.setImageFilter(std::move(paint_record_filter)); + } - auto actual = Raster(display_item_list, options); - auto expected = RasterExpectedBitmap(display_item_list, options); - ExpectEquals(actual, expected); + // Use bilerp sampling with the PaintRecord to help reduce max RGB error + // from pixel-snapping flakiness when using NN sampling. + record_flags.setFilterQuality(kLow_SkFilterQuality); + + // The text blob is embedded in a paint record, which is attached to the + // paint via a shader or image filter. Just draw a rect with the paint. + display_list->push<DrawRectOp>(SkRect::MakeWH(50, 50), record_flags); + } + + static TextBlobStrategy GetTextBlobStrategy( + const TextBlobTestConfig& config) { + return ::testing::get<0>(config); + } + static FilterStrategy GetFilterStrategy(const TextBlobTestConfig& config) { + return ::testing::get<1>(config); + } + static MatrixStrategy GetMatrixStrategy(const TextBlobTestConfig& config) { + return ::testing::get<2>(config); + } + static LCDStrategy GetLCDStrategy(const TextBlobTestConfig& config) { + return ::testing::get<3>(config); + } + + bool UseLcdText() const { + return GetLCDStrategy(GetParam()) == LCDStrategy::kYes; + } + + static std::string PrintTestName( + const ::testing::TestParamInfo<TextBlobTestConfig>& info) { + std::stringstream ss; + switch (GetTextBlobStrategy(info.param)) { + case TextBlobStrategy::kDirect: + ss << "Direct"; + break; + case TextBlobStrategy::kDrawRecord: + ss << "DrawRecord"; + break; + case TextBlobStrategy::kRecordShader: + ss << "RecordShader"; + break; + case TextBlobStrategy::kRecordFilter: + ss << "RecordFilter"; + break; + } + ss << "_"; + switch (GetFilterStrategy(info.param)) { + case FilterStrategy::kNone: + ss << "NoFilter"; + break; + case FilterStrategy::kPaintFlags: + ss << "FilterOnPaint"; + break; + case FilterStrategy::kSaveLayer: + ss << "FilterOnLayer"; + break; + } + ss << "_"; + switch (GetMatrixStrategy(info.param)) { + case MatrixStrategy::kIdentity: + ss << "IdentityCTM"; + break; + case MatrixStrategy::kScaled: + ss << "ScaledCTM"; + break; + case MatrixStrategy::kComplex: + ss << "ComplexCTM"; + break; + case MatrixStrategy::kPerspective: + ss << "PerspectiveCTM"; + break; + } + ss << "_"; + switch (GetLCDStrategy(info.param)) { + case LCDStrategy::kNo: + ss << "NoLCD"; + break; + case LCDStrategy::kYes: + ss << "LCD"; + break; + } + + return ss.str(); } }; -TEST_P(OopRecordFilterPixelTest, FilterWithTextScaled) { - SkM44 mat = SkM44::Scale(2.f, 2.f); - RunTest(mat); +TEST_P(OopTextBlobPixelTest, Config) { + RunTest(); } -TEST_P(OopRecordFilterPixelTest, FilterWithTextAndComplexCTM) { - SkM44 mat = SkM44::Scale(2.f, 2.f); - SkM44 skew = SkM44(); - skew.setRC(0, 1, 2.f); - skew.setRC(1, 0, 2.f); - mat.preConcat(skew); - RunTest(mat); -} +INSTANTIATE_TEST_SUITE_P( + P, + OopTextBlobPixelTest, + ::testing::Combine(::testing::Values(TextBlobStrategy::kDirect, + TextBlobStrategy::kDrawRecord, + TextBlobStrategy::kRecordShader, + TextBlobStrategy::kRecordFilter), + ::testing::Values(FilterStrategy::kNone, + FilterStrategy::kPaintFlags, + FilterStrategy::kSaveLayer), + ::testing::Values(MatrixStrategy::kIdentity, + MatrixStrategy::kScaled, + MatrixStrategy::kComplex, + MatrixStrategy::kPerspective), + ::testing::Values(LCDStrategy::kNo, LCDStrategy::kYes)), + OopTextBlobPixelTest::PrintTestName); void ClearFontCache(CompletionEvent* event) { SkGraphics::PurgeFontCache(); @@ -1812,8 +2014,8 @@ TEST_F(OopPixelTest, DrawTextMultipleRasterCHROMIUM) { PaintFlags flags; flags.setStyle(PaintFlags::kFill_Style); flags.setColor(SK_ColorGREEN); - display_item_list->push<DrawTextBlobOp>(BuildTextBlob(sk_typeface_1), 0u, 0u, - flags); + display_item_list->push<DrawTextBlobOp>(BuildTextBlob(sk_typeface_1), 0u, + kTextBlobY, flags); display_item_list->EndPaintOfUnpaired(options.full_raster_rect); display_item_list->Finalize(); @@ -1821,7 +2023,7 @@ TEST_F(OopPixelTest, DrawTextMultipleRasterCHROMIUM) { auto display_item_list_2 = base::MakeRefCounted<DisplayItemList>(); display_item_list_2->StartPaint(); display_item_list_2->push<DrawTextBlobOp>(BuildTextBlob(sk_typeface_2), 0u, - 0u, flags); + kTextBlobY, flags); display_item_list_2->EndPaintOfUnpaired(options.full_raster_rect); display_item_list_2->Finalize(); @@ -1853,7 +2055,8 @@ TEST_F(OopPixelTest, DrawTextBlobPersistentShaderCache) { PaintFlags flags; flags.setStyle(PaintFlags::kFill_Style); flags.setColor(SK_ColorGREEN); - display_item_list->push<DrawTextBlobOp>(BuildTextBlob(), 0u, 0u, flags); + display_item_list->push<DrawTextBlobOp>(BuildTextBlob(), 0u, kTextBlobY, + flags); display_item_list->EndPaintOfUnpaired(options.full_raster_rect); display_item_list->Finalize(); @@ -2188,8 +2391,6 @@ TEST_F(OopPixelTest, RecordShaderExceedsMaxTextureSize) { INSTANTIATE_TEST_SUITE_P(P, OopImagePixelTest, ::testing::Bool()); INSTANTIATE_TEST_SUITE_P(P, OopClearPixelTest, ::testing::Bool()); -INSTANTIATE_TEST_SUITE_P(P, OopRecordShaderPixelTest, ::testing::Bool()); -INSTANTIATE_TEST_SUITE_P(P, OopRecordFilterPixelTest, ::testing::Bool()); INSTANTIATE_TEST_SUITE_P(P, OopPathPixelTest, ::testing::Bool()); } // namespace diff --git a/chromium/cc/paint/paint_filter.cc b/chromium/cc/paint/paint_filter.cc index 9e32ec4b729..3ad2d2f46dd 100644 --- a/chromium/cc/paint/paint_filter.cc +++ b/chromium/cc/paint/paint_filter.cc @@ -999,12 +999,10 @@ PaintFlagsPaintFilter::PaintFlagsPaintFilter(PaintFlags flags, shader = SkShaders::Color(paint.getColor()); } - // TODO(michaelludwig): Remove SkFilterQuality arg once all image shaders are - // created with explicit filter settings using Dither = SkImageFilters::Dither; cached_sk_filter_ = SkImageFilters::Shader( std::move(shader), paint.isDither() ? Dither::kYes : Dither::kNo, - paint.getFilterQuality(), crop_rect); + crop_rect); } PaintFlagsPaintFilter::~PaintFlagsPaintFilter() = default; @@ -1034,7 +1032,10 @@ MatrixPaintFilter::MatrixPaintFilter(const SkMatrix& matrix, filter_quality_(filter_quality), input_(std::move(input)) { cached_sk_filter_ = SkImageFilters::MatrixTransform( - matrix_, filter_quality_, GetSkFilter(input_.get())); + matrix_, + SkSamplingOptions(filter_quality_, + SkSamplingOptions::kMedium_asMipmapLinear), + GetSkFilter(input_.get())); } MatrixPaintFilter::~MatrixPaintFilter() = default; diff --git a/chromium/cc/paint/paint_flags.cc b/chromium/cc/paint/paint_flags.cc index 7e138218e96..a55e680b0a7 100644 --- a/chromium/cc/paint/paint_flags.cc +++ b/chromium/cc/paint/paint_flags.cc @@ -11,7 +11,7 @@ namespace { static bool affects_alpha(const SkColorFilter* cf) { - return cf && !(cf->getFlags() & SkColorFilter::kAlphaUnchanged_Flag); + return cf && !cf->isAlphaUnchanged(); } } // namespace @@ -126,7 +126,7 @@ SkPaint PaintFlags::ToSkPaint() const { SkPaint paint; paint.setPathEffect(path_effect_); if (shader_) - paint.setShader(shader_->GetSkShader()); + paint.setShader(shader_->GetSkShader(getFilterQuality())); paint.setMaskFilter(mask_filter_); paint.setColorFilter(color_filter_); if (image_filter_) diff --git a/chromium/cc/paint/paint_image.h b/chromium/cc/paint/paint_image.h index ab1a18c7f2d..01a6906a8dd 100644 --- a/chromium/cc/paint/paint_image.h +++ b/chromium/cc/paint/paint_image.h @@ -33,7 +33,17 @@ class PaintWorkletInput; class TextureBacking; using PaintRecord = PaintOpBuffer; -enum class ImageType { kPNG, kJPEG, kWEBP, kGIF, kICO, kBMP, kAVIF, kInvalid }; +enum class ImageType { + kPNG, + kJPEG, + kWEBP, + kGIF, + kICO, + kBMP, + kAVIF, + kJXL, + kInvalid +}; enum class YUVSubsampling { k410, k411, k420, k422, k440, k444, kUnknown }; diff --git a/chromium/cc/paint/paint_op_buffer.cc b/chromium/cc/paint/paint_op_buffer.cc index c9f9ac8bb80..bed909886d6 100644 --- a/chromium/cc/paint/paint_op_buffer.cc +++ b/chromium/cc/paint/paint_op_buffer.cc @@ -2390,6 +2390,8 @@ bool PaintOp::QuickRejectDraw(const PaintOp* op, const SkCanvas* canvas) { SkRect rect; if (!PaintOp::GetBounds(op, &rect)) return false; + if (!rect.isFinite()) + return true; if (op->IsPaintOpWithFlags()) { SkPaint paint = static_cast<const PaintOpWithFlags*>(op)->flags.ToSkPaint(); diff --git a/chromium/cc/paint/paint_op_buffer_unittest.cc b/chromium/cc/paint/paint_op_buffer_unittest.cc index 39288b97b54..a93104dae1f 100644 --- a/chromium/cc/paint/paint_op_buffer_unittest.cc +++ b/chromium/cc/paint/paint_op_buffer_unittest.cc @@ -2613,6 +2613,10 @@ TEST(PaintOpBufferTest, ValidateRects) { static_cast<char*>( base::AlignedAlloc(buffer_size, PaintOpBuffer::PaintOpAlign))); + // Used for QuickRejectDraw + SkCanvas device(256, 256); + SkCanvas* canvas = &device; + SkRect bad_rect = SkRect::MakeEmpty(); bad_rect.fBottom = std::numeric_limits<float>::quiet_NaN(); EXPECT_FALSE(bad_rect.isFinite()); @@ -2647,6 +2651,14 @@ TEST(PaintOpBufferTest, ValidateRects) { serialized.get(), bytes_written, deserialized.get(), buffer_size, &bytes_read, options_provider.deserialize_options()); EXPECT_FALSE(written) << "op: " << op_idx; + + // Additionally, every draw op should be rejected by QuickRejectDraw if + // the paint op buffer were played back directly without going through + // deserialization (e.g. canvas2D, crbug.com/1186392) + if (op->IsDrawOp()) { + EXPECT_TRUE(PaintOp::QuickRejectDraw(op, canvas)); + } + ++op_idx; } } @@ -3392,7 +3404,6 @@ TEST(PaintOpBufferTest, PaintRecordShaderSerialization) { EXPECT_FLOAT_RECT_EQ(rect_op->rect, SkRect::MakeXYWH(1, 2, 3, 4)); EXPECT_TRUE(rect_op->flags == flags); EXPECT_TRUE(*rect_op->flags.getShader() == *flags.getShader()); - EXPECT_TRUE(!!rect_op->flags.getShader()->GetSkShader()); } #if !defined(OS_ANDROID) @@ -3696,7 +3707,7 @@ TEST(PaintOpBufferTest, RecordShadersCached) { // Hold onto records so PaintShader pointer comparisons are valid. sk_sp<PaintRecord> records[5]; - const SkShader* last_shader = nullptr; + SkPicture* last_shader = nullptr; std::vector<uint8_t> scratch_buffer; PaintOp::DeserializeOptions deserialize_options( transfer_cache, options_provider.service_paint_cache(), @@ -3731,8 +3742,8 @@ TEST(PaintOpBufferTest, RecordShadersCached) { // In every case, the shader in the op should get cached for future // use. - auto* op_skshader = op->flags.getShader()->GetSkShader().get(); - EXPECT_EQ(op_skshader, entry->shader()->GetSkShader().get()); + auto* op_skshader = op->flags.getShader()->sk_cached_picture_.get(); + EXPECT_EQ(op_skshader, entry->shader()->sk_cached_picture_.get()); switch (i) { case 0: // Nothing to check. diff --git a/chromium/cc/paint/paint_op_reader.cc b/chromium/cc/paint/paint_op_reader.cc index eb795ba528e..68429994cd9 100644 --- a/chromium/cc/paint/paint_op_reader.cc +++ b/chromium/cc/paint/paint_op_reader.cc @@ -590,7 +590,7 @@ void PaintOpReader::Read(sk_sp<PaintShader>* shader) { // All shader types but records are done. if (shader_type != PaintShader::Type::kPaintRecord) { - (*shader)->CreateSkShader(); + (*shader)->ResolveSkObjects(); return; } @@ -607,10 +607,11 @@ void PaintOpReader::Read(sk_sp<PaintShader>* shader) { // side transfer cache to only having one entry per shader but this will hit // the common case of enabling Skia reuse. if (entry && entry->shader()->tile_ == ref.tile_) { - DCHECK(!ref.cached_shader_); - ref.cached_shader_ = entry->shader()->GetSkShader(); + DCHECK(!ref.sk_cached_picture_); + ref.sk_cached_picture_ = entry->shader()->sk_cached_picture_; } else { - ref.CreateSkShader(); + ref.ResolveSkObjects(); + DCHECK(ref.sk_cached_picture_); options_.transfer_cache->CreateLocalEntry( shader_id, std::make_unique<ServiceShaderTransferCacheEntry>( *shader, shader_size)); diff --git a/chromium/cc/paint/paint_shader.cc b/chromium/cc/paint/paint_shader.cc index 02cdea0b8b9..f221163b646 100644 --- a/chromium/cc/paint/paint_shader.cc +++ b/chromium/cc/paint/paint_shader.cc @@ -69,7 +69,7 @@ const PaintShader::RecordShaderId PaintShader::kInvalidRecordShaderId = -1; sk_sp<PaintShader> PaintShader::MakeEmpty() { sk_sp<PaintShader> shader(new PaintShader(Type::kEmpty)); - shader->CreateSkShader(); + shader->ResolveSkObjects(); return shader; } @@ -79,7 +79,7 @@ sk_sp<PaintShader> PaintShader::MakeColor(SkColor color) { // Just one color. Store it in the fallback color. Easy. shader->fallback_color_ = color; - shader->CreateSkShader(); + shader->ResolveSkObjects(); return shader; } @@ -100,7 +100,7 @@ sk_sp<PaintShader> PaintShader::MakeLinearGradient(const SkPoint points[], shader->SetMatrixAndTiling(local_matrix, mode, mode); shader->SetFlagsAndFallback(flags, fallback_color); - shader->CreateSkShader(); + shader->ResolveSkObjects(); return shader; } @@ -121,7 +121,7 @@ sk_sp<PaintShader> PaintShader::MakeRadialGradient(const SkPoint& center, shader->SetMatrixAndTiling(local_matrix, mode, mode); shader->SetFlagsAndFallback(flags, fallback_color); - shader->CreateSkShader(); + shader->ResolveSkObjects(); return shader; } @@ -147,7 +147,7 @@ sk_sp<PaintShader> PaintShader::MakeTwoPointConicalGradient( shader->SetMatrixAndTiling(local_matrix, mode, mode); shader->SetFlagsAndFallback(flags, fallback_color); - shader->CreateSkShader(); + shader->ResolveSkObjects(); return shader; } @@ -171,7 +171,7 @@ sk_sp<PaintShader> PaintShader::MakeSweepGradient(SkScalar cx, shader->SetMatrixAndTiling(local_matrix, mode, mode); shader->SetFlagsAndFallback(flags, fallback_color); - shader->CreateSkShader(); + shader->ResolveSkObjects(); return shader; } @@ -189,7 +189,7 @@ sk_sp<PaintShader> PaintShader::MakeImage(const PaintImage& image, shader->tile_ = *tile_rect; } - shader->CreateSkShader(); + shader->ResolveSkObjects(); return shader; } @@ -208,7 +208,7 @@ sk_sp<PaintShader> PaintShader::MakePaintRecord( shader->scaling_behavior_ = scaling_behavior; shader->SetMatrixAndTiling(local_matrix, tx, ty); - shader->CreateSkShader(); + shader->ResolveSkObjects(); return shader; } @@ -413,24 +413,19 @@ sk_sp<PaintShader> PaintShader::CreateDecodedImage( return PaintShader::MakeImage(decoded_paint_image, tx_, ty_, &final_matrix); } -sk_sp<SkShader> PaintShader::GetSkShader() const { - return cached_shader_; -} - -void PaintShader::CreateSkShader(const gfx::SizeF* raster_scale, - ImageProvider* image_provider) { - DCHECK(!cached_shader_); +sk_sp<SkShader> PaintShader::GetSkShader(SkFilterQuality quality) const { + SkSamplingOptions sampling(quality, + SkSamplingOptions::kMedium_asMipmapLinear); switch (shader_type_) { case Type::kEmpty: - cached_shader_ = SkShaders::Empty(); - break; + return SkShaders::Empty(); case Type::kColor: // This will be handled by the fallback check below. break; case Type::kLinearGradient: { SkPoint points[2] = {start_point_, end_point_}; - cached_shader_ = SkGradientShader::MakeLinear( + return SkGradientShader::MakeLinear( points, colors_.data(), positions_.empty() ? nullptr : positions_.data(), static_cast<int>(colors_.size()), tx_, flags_, @@ -438,66 +433,84 @@ void PaintShader::CreateSkShader(const gfx::SizeF* raster_scale, break; } case Type::kRadialGradient: - cached_shader_ = SkGradientShader::MakeRadial( + return SkGradientShader::MakeRadial( center_, start_radius_, colors_.data(), positions_.empty() ? nullptr : positions_.data(), static_cast<int>(colors_.size()), tx_, flags_, base::OptionalOrNullptr(local_matrix_)); break; case Type::kTwoPointConicalGradient: - cached_shader_ = SkGradientShader::MakeTwoPointConical( + return SkGradientShader::MakeTwoPointConical( start_point_, start_radius_, end_point_, end_radius_, colors_.data(), positions_.empty() ? nullptr : positions_.data(), static_cast<int>(colors_.size()), tx_, flags_, base::OptionalOrNullptr(local_matrix_)); break; case Type::kSweepGradient: - cached_shader_ = SkGradientShader::MakeSweep( + return SkGradientShader::MakeSweep( center_.x(), center_.y(), colors_.data(), positions_.empty() ? nullptr : positions_.data(), static_cast<int>(colors_.size()), tx_, start_degrees_, end_degrees_, flags_, base::OptionalOrNullptr(local_matrix_)); break; case Type::kImage: - if (image_ && !image_.IsPaintWorklet()) { - cached_shader_ = image_.GetSkImage()->makeShader( - tx_, ty_, base::OptionalOrNullptr(local_matrix_)); + if (sk_cached_image_) { + return sk_cached_image_->makeShader( + tx_, ty_, sampling, base::OptionalOrNullptr(local_matrix_)); } break; - case Type::kPaintRecord: { - // Create a recording at the desired scale if this record has images which - // have been decoded before raster. - auto picture = ToSkPicture(record_, tile_, raster_scale, image_provider); - - switch (scaling_behavior_) { - // For raster scale, we create a picture shader directly. - case ScalingBehavior::kRasterAtScale: - cached_shader_ = picture->makeShader( - tx_, ty_, base::OptionalOrNullptr(local_matrix_), nullptr); - break; - // For fixed scale, we create an image shader with an image backed by - // the picture. - case ScalingBehavior::kFixedScale: { - auto image = SkImage::MakeFromPicture( - std::move(picture), SkISize::Make(tile_.width(), tile_.height()), - nullptr, nullptr, SkImage::BitDepth::kU8, - SkColorSpace::MakeSRGB()); - cached_shader_ = image->makeShader( - tx_, ty_, base::OptionalOrNullptr(local_matrix_)); - break; + case Type::kPaintRecord: + if (sk_cached_picture_) { + switch (scaling_behavior_) { + // For raster scale, we create a picture shader directly. + case ScalingBehavior::kRasterAtScale: + return sk_cached_picture_->makeShader( + tx_, ty_, sampling.filter, + base::OptionalOrNullptr(local_matrix_), nullptr); + break; + // For fixed scale, we create an image shader with an image backed by + // the picture. + case ScalingBehavior::kFixedScale: { + auto image = SkImage::MakeFromPicture( + sk_cached_picture_, + SkISize::Make(tile_.width(), tile_.height()), nullptr, nullptr, + SkImage::BitDepth::kU8, SkColorSpace::MakeSRGB()); + return image->makeShader(tx_, ty_, sampling, + base::OptionalOrNullptr(local_matrix_)); + break; + } } + break; } break; - } case Type::kShaderCount: NOTREACHED(); break; } - // If we didn't create a shader for whatever reason, create a fallback color - // one. - if (!cached_shader_) - cached_shader_ = SkShaders::Color(fallback_color_); + // If we didn't create a shader for whatever reason, create a fallback + // color one. + return SkShaders::Color(fallback_color_); +} + +void PaintShader::ResolveSkObjects(const gfx::SizeF* raster_scale, + ImageProvider* image_provider) { + switch (shader_type_) { + case Type::kImage: + if (image_ && !image_.IsPaintWorklet()) { + sk_cached_image_ = image_.GetSkImage(); + } + break; + case Type::kPaintRecord: { + // Create a recording at the desired scale if this record has images + // which have been decoded before raster. + sk_cached_picture_ = + ToSkPicture(record_, tile_, raster_scale, image_provider); + break; + } + default: + break; + } } void PaintShader::SetColorsAndPositions(const SkColor* colors, @@ -528,15 +541,44 @@ void PaintShader::SetFlagsAndFallback(uint32_t flags, SkColor fallback_color) { } bool PaintShader::IsOpaque() const { - // TODO(enne): don't create a shader to answer this. - return GetSkShader()->isOpaque(); + switch (shader_type_) { + case Type::kEmpty: + return false; + case Type::kColor: + // This will be handled by the fallback check below. + break; + case Type::kLinearGradient: // fall-through + case Type::kRadialGradient: // fall-through + case Type::kSweepGradient: + if (tx_ == SkTileMode::kDecal) + return false; + for (const auto& c : colors_) + if (SkColorGetA(c) != 0xFF) + return false; + return true; + case Type::kTwoPointConicalGradient: + // Because two-point-conical can sometimes ignore its tiling and not draw + // everywhere, we conservatively return false here. If we measure + // performance reasons to be more aggressive here, we can ask Skia to + // expose private functionality to compute this with having to actually + // instantiate a sk_shader object. + return false; + case Type::kImage: + if (tx_ == SkTileMode::kDecal || ty_ == SkTileMode::kDecal) + return false; + if (sk_cached_image_) + return sk_cached_image_->isOpaque(); + return false; + case Type::kPaintRecord: + return false; + case Type::kShaderCount: + NOTREACHED(); + break; + } + return SkColorGetA(fallback_color_) == 0xFF; } bool PaintShader::IsValid() const { - // If we managed to create a shader already, then we should be valid. - if (cached_shader_) - return true; - switch (shader_type_) { case Type::kEmpty: case Type::kColor: diff --git a/chromium/cc/paint/paint_shader.h b/chromium/cc/paint/paint_shader.h index 7b6940cea87..a2ae2576a93 100644 --- a/chromium/cc/paint/paint_shader.h +++ b/chromium/cc/paint/paint_shader.h @@ -182,9 +182,13 @@ class CC_PAINT_EXPORT PaintShader : public SkRefCnt { explicit PaintShader(Type type); - sk_sp<SkShader> GetSkShader() const; - void CreateSkShader(const gfx::SizeF* raster_scale = nullptr, - ImageProvider* image_provider = nullptr); + sk_sp<SkShader> GetSkShader(SkFilterQuality quality) const; + + // If the type needs a resolve skia object (e.g. SkImage or SkPicture), this + // will create and cache it internally. Most types do not need this, but it + // is safe to call on any type. + void ResolveSkObjects(const gfx::SizeF* raster_scale = nullptr, + ImageProvider* image_provider = nullptr); // Creates a PaintShader to be rasterized at the given ctm. |raster_scale| is // set to the scale at which the record should be rasterized when the shader @@ -251,10 +255,9 @@ class CC_PAINT_EXPORT PaintShader : public SkRefCnt { std::vector<SkColor> colors_; std::vector<SkScalar> positions_; - // The |cached_shader_| can be derived/creates from other inputs present in - // the PaintShader but we always construct it at creation time to ensure that - // accesses to it are thread-safe. - sk_sp<SkShader> cached_shader_; + // Cached intermediates, for cc::Paint objects that may not be thread-safe + sk_sp<SkPicture> sk_cached_picture_; + sk_sp<SkImage> sk_cached_image_; ImageAnalysisState image_analysis_state_ = ImageAnalysisState::kNoAnalysis; }; diff --git a/chromium/cc/paint/scoped_raster_flags.cc b/chromium/cc/paint/scoped_raster_flags.cc index 1e5b87e6ad3..8bd3ce0640c 100644 --- a/chromium/cc/paint/scoped_raster_flags.cc +++ b/chromium/cc/paint/scoped_raster_flags.cc @@ -101,8 +101,8 @@ void ScopedRasterFlags::DecodeRecordShader(const SkMatrix& ctm, gfx::SizeF raster_scale(1.f, 1.f); auto decoded_shader = flags()->getShader()->CreateScaledPaintRecord( ctm, max_texture_size, &raster_scale); - decoded_shader->CreateSkShader(&raster_scale, - &*decode_stashing_image_provider_); + decoded_shader->ResolveSkObjects(&raster_scale, + &*decode_stashing_image_provider_); MutableFlags()->setShader(std::move(decoded_shader)); } diff --git a/chromium/cc/raster/gpu_raster_buffer_provider.cc b/chromium/cc/raster/gpu_raster_buffer_provider.cc index 0f76c1a85c2..3726fd4ae6f 100644 --- a/chromium/cc/raster/gpu_raster_buffer_provider.cc +++ b/chromium/cc/raster/gpu_raster_buffer_provider.cc @@ -61,6 +61,7 @@ static void RasterizeSourceOOP( const RasterSource::PlaybackSettings& playback_settings, viz::RasterContextProvider* context_provider) { gpu::raster::RasterInterface* ri = context_provider->RasterInterface(); + bool mailbox_needs_clear = false; if (mailbox->IsZero()) { DCHECK(!sync_token.HasData()); auto* sii = context_provider->SharedImageInterface(); @@ -72,14 +73,16 @@ static void RasterizeSourceOOP( *mailbox = sii->CreateSharedImage( resource_format, resource_size, color_space, kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, flags, gpu::kNullSurfaceHandle); + mailbox_needs_clear = true; ri->WaitSyncTokenCHROMIUM(sii->GenUnverifiedSyncToken().GetConstData()); } else { ri->WaitSyncTokenCHROMIUM(sync_token.GetConstData()); } ri->BeginRasterCHROMIUM( - raster_source->background_color(), playback_settings.msaa_sample_count, - playback_settings.use_lcd_text, color_space, mailbox->name); + raster_source->background_color(), mailbox_needs_clear, + playback_settings.msaa_sample_count, playback_settings.use_lcd_text, + color_space, mailbox->name); float recording_to_raster_scale = transform.scale() / raster_source->recording_scale_factor(); gfx::Size content_size = raster_source->GetContentSize(transform.scale()); diff --git a/chromium/cc/raster/raster_buffer_provider_unittest.cc b/chromium/cc/raster/raster_buffer_provider_unittest.cc index a19a04e98a9..4a281c36c24 100644 --- a/chromium/cc/raster/raster_buffer_provider_unittest.cc +++ b/chromium/cc/raster/raster_buffer_provider_unittest.cc @@ -174,6 +174,7 @@ class RasterImplementationForOOPR } } void BeginRasterCHROMIUM(GLuint sk_color, + GLboolean needs_clear, GLuint msaa_sample_count, GLboolean can_use_lcd_text, const gfx::ColorSpace& color_space, diff --git a/chromium/cc/raster/single_thread_task_graph_runner.cc b/chromium/cc/raster/single_thread_task_graph_runner.cc index 0735d4e91ee..c772814faa8 100644 --- a/chromium/cc/raster/single_thread_task_graph_runner.cc +++ b/chromium/cc/raster/single_thread_task_graph_runner.cc @@ -6,6 +6,7 @@ #include <stdint.h> +#include <memory> #include <string> #include <utility> @@ -26,8 +27,8 @@ SingleThreadTaskGraphRunner::~SingleThreadTaskGraphRunner() = default; void SingleThreadTaskGraphRunner::Start( const std::string& thread_name, const base::SimpleThread::Options& thread_options) { - thread_.reset( - new base::DelegateSimpleThread(this, thread_name, thread_options)); + thread_ = std::make_unique<base::DelegateSimpleThread>(this, thread_name, + thread_options); thread_->StartAsync(); } diff --git a/chromium/cc/scheduler/scheduler.cc b/chromium/cc/scheduler/scheduler.cc index bacb2272f72..f5f68d76a23 100644 --- a/chromium/cc/scheduler/scheduler.cc +++ b/chromium/cc/scheduler/scheduler.cc @@ -36,14 +36,18 @@ Scheduler::Scheduler( const SchedulerSettings& settings, int layer_tree_host_id, base::SingleThreadTaskRunner* task_runner, - std::unique_ptr<CompositorTimingHistory> compositor_timing_history) + std::unique_ptr<CompositorTimingHistory> compositor_timing_history, + gfx::RenderingPipeline* main_thread_pipeline, + gfx::RenderingPipeline* compositor_thread_pipeline) : settings_(settings), client_(client), layer_tree_host_id_(layer_tree_host_id), task_runner_(task_runner), compositor_timing_history_(std::move(compositor_timing_history)), begin_impl_frame_tracker_(FROM_HERE), - state_machine_(settings) { + state_machine_(settings), + main_thread_pipeline_(main_thread_pipeline), + compositor_thread_pipeline_(compositor_thread_pipeline) { TRACE_EVENT1("cc", "Scheduler::Scheduler", "settings", settings_.AsValue()); DCHECK(client_); DCHECK(!state_machine_.BeginFrameNeeded()); @@ -148,10 +152,12 @@ void Scheduler::SetNeedsPrepareTiles() { } void Scheduler::DidSubmitCompositorFrame(uint32_t frame_token, - EventMetricsSet events_metrics) { + EventMetricsSet events_metrics, + bool has_missing_content) { compositor_timing_history_->DidSubmitCompositorFrame( frame_token, begin_main_frame_args_.frame_id, - last_activate_origin_frame_args_.frame_id, std::move(events_metrics)); + last_activate_origin_frame_args_.frame_id, std::move(events_metrics), + has_missing_content); state_machine_.DidSubmitCompositorFrame(); // There is no need to call ProcessScheduledActions here because @@ -185,6 +191,8 @@ void Scheduler::NotifyReadyToCommit( } void Scheduler::DidCommit() { + if (main_thread_pipeline_) + main_thread_pipeline_->NotifyFrameFinished(); compositor_timing_history_->DidCommit(); } @@ -194,6 +202,8 @@ void Scheduler::BeginMainFrameAborted(CommitEarlyOutReason reason) { compositor_timing_history_->BeginMainFrameAborted( last_dispatched_begin_main_frame_args_.frame_id, reason); state_machine_.BeginMainFrameAborted(reason); + if (main_thread_pipeline_) + main_thread_pipeline_->NotifyFrameFinished(); ProcessScheduledActions(); } @@ -283,6 +293,8 @@ void Scheduler::StartOrStopBeginFrames() { devtools_instrumentation::NeedsBeginFrameChanged(layer_tree_host_id_, false); client_->WillNotReceiveBeginFrame(); + main_thread_pipeline_active_.reset(); + compositor_thread_pipeline_active_.reset(); } } @@ -477,20 +489,9 @@ void Scheduler::BeginImplFrameWithDeadline(const viz::BeginFrameArgs& args) { adjusted_args.interval - compositor_timing_history_->DrawDurationEstimate() - kDeadlineFudgeFactor; - // An estimate of time from starting the main frame on the main thread to when - // the resulting pending tree is activated. Note that this excludes the - // durations where progress is blocked due to back pressure in the pipeline - // (ready to commit to commit, ready to activate to activate, etc.) - base::TimeDelta bmf_start_to_activate = - compositor_timing_history_ - ->BeginMainFrameStartToReadyToCommitDurationEstimate() + - compositor_timing_history_->CommitDurationEstimate() + - compositor_timing_history_->CommitToReadyToActivateDurationEstimate() + - compositor_timing_history_->ActivateDurationEstimate(); - base::TimeDelta bmf_to_activate_estimate_critical = - bmf_start_to_activate + - compositor_timing_history_->BeginMainFrameQueueDurationCriticalEstimate(); + compositor_timing_history_ + ->BeginMainFrameQueueToActivateCriticalEstimate(); state_machine_.SetCriticalBeginMainFrameToActivateIsFast( bmf_to_activate_estimate_critical < bmf_to_activate_threshold); @@ -510,17 +511,15 @@ void Scheduler::BeginImplFrameWithDeadline(const viz::BeginFrameArgs& args) { time_since_main_frame_sent = now - compositor_timing_history_->begin_main_frame_sent_time(); } - base::TimeDelta bmf_sent_to_ready_to_commit_estimate = - compositor_timing_history_ - ->BeginMainFrameStartToReadyToCommitDurationEstimate(); + base::TimeDelta bmf_sent_to_ready_to_commit_estimate; if (begin_main_frame_args_.on_critical_path) { - bmf_sent_to_ready_to_commit_estimate += + bmf_sent_to_ready_to_commit_estimate = compositor_timing_history_ - ->BeginMainFrameQueueDurationCriticalEstimate(); + ->BeginMainFrameStartToReadyToCommitCriticalEstimate(); } else { - bmf_sent_to_ready_to_commit_estimate += + bmf_sent_to_ready_to_commit_estimate = compositor_timing_history_ - ->BeginMainFrameQueueDurationNotCriticalEstimate(); + ->BeginMainFrameStartToReadyToCommitNotCriticalEstimate(); } bool main_thread_response_expected_before_deadline; @@ -540,9 +539,8 @@ void Scheduler::BeginImplFrameWithDeadline(const viz::BeginFrameArgs& args) { base::TimeDelta bmf_to_activate_estimate = bmf_to_activate_estimate_critical; if (!begin_main_frame_args_.on_critical_path) { bmf_to_activate_estimate = - bmf_start_to_activate + compositor_timing_history_ - ->BeginMainFrameQueueDurationNotCriticalEstimate(); + ->BeginMainFrameQueueToActivateNotCriticalEstimate(); } bool can_activate_before_deadline = CanBeginMainFrameAndActivateBeforeDeadline(adjusted_args, @@ -563,6 +561,29 @@ void Scheduler::BeginImplFrameWithDeadline(const viz::BeginFrameArgs& args) { skipped_last_frame_to_reduce_latency_ = false; + // A pipeline is activated if it's subscribed to BeginFrame callbacks. For the + // compositor this implies BeginImplFrames while for the main thread it would + // be BeginMainFrames. + // TODO(crbug.com/1157620) : For now this also includes cases where the + // rendering loop doesn't lead to any visual updates, for example a rAF + // callback updating content offscreen. These can be treated differently from + // a scheduling perspective (Idle vs Animating). + if (compositor_thread_pipeline_) { + if (!compositor_thread_pipeline_active_) + compositor_thread_pipeline_active_.emplace(compositor_thread_pipeline_); + compositor_thread_pipeline_->SetTargetDuration(adjusted_args.interval); + } + + if (main_thread_pipeline_) { + // TODO(crbug.com/1157620) : We need a heuristic to mark the main pipeline + // inactive if it stops subscribing to main frames. For instance after a + // composited animation is committed. + if (state_machine_.did_commit_during_frame() && + !main_thread_pipeline_active_) + main_thread_pipeline_active_.emplace(main_thread_pipeline_); + main_thread_pipeline_->SetTargetDuration(adjusted_args.interval); + } + BeginImplFrame(adjusted_args, now); } @@ -618,6 +639,9 @@ void Scheduler::FinishImplFrame() { if (begin_frame_source_) begin_frame_source_->DidFinishFrame(this); + + if (compositor_thread_pipeline_) + compositor_thread_pipeline_->NotifyFrameFinished(); } void Scheduler::SendDidNotProduceFrame(const viz::BeginFrameArgs& args, @@ -942,9 +966,6 @@ void Scheduler::AsProtozeroInto( if (begin_frame_source_) { begin_frame_source_->AsProtozeroInto(state->set_begin_frame_source_state()); } - - compositor_timing_history_->AsProtozeroInto( - state->set_compositor_timing_history()); } void Scheduler::UpdateCompositorTimingHistoryRecordingEnabled() { diff --git a/chromium/cc/scheduler/scheduler.h b/chromium/cc/scheduler/scheduler.h index eb0ba20ac57..3784cda0bb1 100644 --- a/chromium/cc/scheduler/scheduler.h +++ b/chromium/cc/scheduler/scheduler.h @@ -22,6 +22,7 @@ #include "components/viz/common/frame_sinks/begin_frame_args.h" #include "components/viz/common/frame_sinks/begin_frame_source.h" #include "components/viz/common/frame_sinks/delay_based_time_source.h" +#include "ui/gfx/rendering_pipeline.h" namespace perfetto { namespace protos { @@ -97,7 +98,9 @@ class CC_EXPORT Scheduler : public viz::BeginFrameObserverBase { const SchedulerSettings& scheduler_settings, int layer_tree_host_id, base::SingleThreadTaskRunner* task_runner, - std::unique_ptr<CompositorTimingHistory> compositor_timing_history); + std::unique_ptr<CompositorTimingHistory> compositor_timing_history, + gfx::RenderingPipeline* main_thread_pipeline, + gfx::RenderingPipeline* compositor_thread_pipeline); Scheduler(const Scheduler&) = delete; ~Scheduler() override; @@ -175,7 +178,8 @@ class CC_EXPORT Scheduler : public viz::BeginFrameObserverBase { // Drawing should result in submitting a CompositorFrame to the // LayerTreeFrameSink and then calling this. void DidSubmitCompositorFrame(uint32_t frame_token, - EventMetricsSet events_metrics); + EventMetricsSet events_metrics, + bool has_missing_content); // The LayerTreeFrameSink acks when it is ready for a new frame which // should result in this getting called to unblock the next draw. void DidReceiveCompositorFrameAck(); @@ -328,6 +332,14 @@ class CC_EXPORT Scheduler : public viz::BeginFrameObserverBase { // arrive so that |client_| can be informed about changes. base::TimeDelta last_frame_interval_; + gfx::RenderingPipeline* const main_thread_pipeline_; + base::Optional<gfx::RenderingPipeline::ScopedPipelineActive> + main_thread_pipeline_active_; + + gfx::RenderingPipeline* const compositor_thread_pipeline_; + base::Optional<gfx::RenderingPipeline::ScopedPipelineActive> + compositor_thread_pipeline_active_; + private: // Posts the deadline task if needed by checking // SchedulerStateMachine::CurrentBeginImplFrameDeadlineMode(). This only diff --git a/chromium/cc/scheduler/scheduler_state_machine.h b/chromium/cc/scheduler/scheduler_state_machine.h index 69a98b0c94d..400d650855b 100644 --- a/chromium/cc/scheduler/scheduler_state_machine.h +++ b/chromium/cc/scheduler/scheduler_state_machine.h @@ -350,6 +350,7 @@ class CC_EXPORT SchedulerStateMachine { bool should_defer_invalidation_for_fast_main_frame() const { return should_defer_invalidation_for_fast_main_frame_; } + bool did_commit_during_frame() const { return did_commit_during_frame_; } protected: bool BeginFrameRequiredForAction() const; diff --git a/chromium/cc/scheduler/scheduler_unittest.cc b/chromium/cc/scheduler/scheduler_unittest.cc index c7647b28a06..98b0c5a66b7 100644 --- a/chromium/cc/scheduler/scheduler_unittest.cc +++ b/chromium/cc/scheduler/scheduler_unittest.cc @@ -6,6 +6,7 @@ #include <stddef.h> +#include <memory> #include <string> #include <utility> #include <vector> @@ -172,7 +173,8 @@ class FakeSchedulerClient : public SchedulerClient, if (swap_will_happen_if_draw_happens_) { last_begin_frame_ack_ = scheduler_->CurrentBeginFrameAckForActiveTree(); - scheduler_->DidSubmitCompositorFrame(0, EventMetricsSet()); + scheduler_->DidSubmitCompositorFrame(0, EventMetricsSet(), + /*has_missing_content=*/false); if (automatic_ack_) scheduler_->DidReceiveCompositorFrameAck(); @@ -367,11 +369,12 @@ class SchedulerTest : public testing::Test { protected: TestScheduler* CreateScheduler(BeginFrameSourceType bfs_type) { viz::BeginFrameSource* frame_source = nullptr; - unthrottled_frame_source_.reset(new viz::BackToBackBeginFrameSource( - std::make_unique<viz::FakeDelayBasedTimeSource>( - task_runner_->GetMockTickClock(), task_runner_.get()))); - fake_external_begin_frame_source_.reset( - new viz::FakeExternalBeginFrameSource(1.0, false)); + unthrottled_frame_source_ = + std::make_unique<viz::BackToBackBeginFrameSource>( + std::make_unique<viz::FakeDelayBasedTimeSource>( + task_runner_->GetMockTickClock(), task_runner_.get())); + fake_external_begin_frame_source_ = + std::make_unique<viz::FakeExternalBeginFrameSource>(1.0, false); fake_external_begin_frame_source_->SetClient(client_.get()); synthetic_frame_source_ = std::make_unique<viz::DelayBasedBeginFrameSource>( std::make_unique<viz::FakeDelayBasedTimeSource>( @@ -395,9 +398,9 @@ class SchedulerTest : public testing::Test { scheduler_settings_.using_synchronous_renderer_compositor); fake_compositor_timing_history_ = fake_compositor_timing_history.get(); - scheduler_.reset(new TestScheduler( + scheduler_ = std::make_unique<TestScheduler>( task_runner_->GetMockTickClock(), client_.get(), scheduler_settings_, 0, - task_runner_.get(), std::move(fake_compositor_timing_history))); + task_runner_.get(), std::move(fake_compositor_timing_history)); client_->set_scheduler(scheduler_.get()); scheduler_->SetBeginFrameSource(frame_source); diff --git a/chromium/cc/tiles/image_controller_unittest.cc b/chromium/cc/tiles/image_controller_unittest.cc index 120e0c6431f..5d1f57d1b9c 100644 --- a/chromium/cc/tiles/image_controller_unittest.cc +++ b/chromium/cc/tiles/image_controller_unittest.cc @@ -4,6 +4,7 @@ #include "cc/tiles/image_controller.h" +#include <memory> #include <utility> #include "base/bind.h" @@ -261,8 +262,8 @@ class ImageControllerTest : public testing::Test { void SetUp() override { worker_task_runner_ = base::MakeRefCounted<WorkerTaskRunner>(); - controller_.reset( - new ImageController(task_runner_.get(), worker_task_runner_)); + controller_ = std::make_unique<ImageController>(task_runner_.get(), + worker_task_runner_); cache_ = TestableCache(); controller_->SetImageDecodeCache(&cache_); } diff --git a/chromium/cc/tiles/image_decode_cache.h b/chromium/cc/tiles/image_decode_cache.h index bd77daad149..6ab0a4539da 100644 --- a/chromium/cc/tiles/image_decode_cache.h +++ b/chromium/cc/tiles/image_decode_cache.h @@ -78,6 +78,8 @@ class CC_EXPORT ImageDecodeCache { using ScopedImageType = devtools_instrumentation::ScopedImageDecodeTask::ImageType; switch (image_type) { + case ImageType::kJXL: + return ScopedImageType::kJxl; case ImageType::kAVIF: return ScopedImageType::kAvif; case ImageType::kBMP: diff --git a/chromium/cc/tiles/picture_layer_tiling_set.cc b/chromium/cc/tiles/picture_layer_tiling_set.cc index f958c316e26..17d7437952d 100644 --- a/chromium/cc/tiles/picture_layer_tiling_set.cc +++ b/chromium/cc/tiles/picture_layer_tiling_set.cc @@ -281,9 +281,9 @@ PictureLayerTiling* PictureLayerTilingSet::AddTiling( raster_source_ = raster_source; #if DCHECK_IS_ON() - for (size_t i = 0; i < tilings_.size(); ++i) { - DCHECK_NE(tilings_[i]->contents_scale_key(), raster_transform.scale()); - DCHECK_EQ(tilings_[i]->raster_source(), raster_source.get()); + for (const auto& tiling : tilings_) { + DCHECK_NE(tiling->contents_scale_key(), raster_transform.scale()); + DCHECK_EQ(tiling->raster_source(), raster_source.get()); } #endif // DCHECK_IS_ON() @@ -307,9 +307,9 @@ int PictureLayerTilingSet::NumHighResTilings() const { PictureLayerTiling* PictureLayerTilingSet::FindTilingWithScaleKey( float scale_key) const { - for (size_t i = 0; i < tilings_.size(); ++i) { - if (tilings_[i]->contents_scale_key() == scale_key) - return tilings_[i].get(); + for (const auto& tiling : tilings_) { + if (tiling->contents_scale_key() == scale_key) + return tiling.get(); } return nullptr; } @@ -365,8 +365,8 @@ void PictureLayerTilingSet::Remove(PictureLayerTiling* tiling) { } void PictureLayerTilingSet::RemoveAllTiles() { - for (size_t i = 0; i < tilings_.size(); ++i) - tilings_[i]->Reset(); + for (const auto& tiling : tilings_) + tiling->Reset(); } float PictureLayerTilingSet::GetSnappedContentsScaleKey( @@ -725,17 +725,17 @@ PictureLayerTilingSet::CoverageIterator::operator bool() const { void PictureLayerTilingSet::AsValueInto( base::trace_event::TracedValue* state) const { - for (size_t i = 0; i < tilings_.size(); ++i) { + for (const auto& tiling : tilings_) { state->BeginDictionary(); - tilings_[i]->AsValueInto(state); + tiling->AsValueInto(state); state->EndDictionary(); } } size_t PictureLayerTilingSet::GPUMemoryUsageInBytes() const { size_t amount = 0; - for (size_t i = 0; i < tilings_.size(); ++i) - amount += tilings_[i]->GPUMemoryUsageInBytes(); + for (const auto& tiling : tilings_) + amount += tiling->GPUMemoryUsageInBytes(); return amount; } @@ -746,7 +746,7 @@ PictureLayerTilingSet::TilingRange PictureLayerTilingSet::GetTilingRange( // compute them only when the tiling set has changed instead. size_t tilings_size = tilings_.size(); TilingRange high_res_range(0, 0); - TilingRange low_res_range(tilings_.size(), tilings_.size()); + TilingRange low_res_range(tilings_size, tilings_size); for (size_t i = 0; i < tilings_size; ++i) { const PictureLayerTiling* tiling = tilings_[i].get(); if (tiling->resolution() == HIGH_RESOLUTION) diff --git a/chromium/cc/trees/damage_tracker.cc b/chromium/cc/trees/damage_tracker.cc index a86bf950bec..2ef6c808508 100644 --- a/chromium/cc/trees/damage_tracker.cc +++ b/chromium/cc/trees/damage_tracker.cc @@ -7,7 +7,6 @@ #include <stddef.h> #include <algorithm> -#include <utility> #include "base/memory/ptr_util.h" #include "cc/base/math_util.h" @@ -357,14 +356,13 @@ void DamageTracker::AccumulateDamageFromLayer(LayerImpl* layer) { LayerRectMapData& data = RectDataForLayer(layer->id(), &layer_is_new); gfx::Rect old_rect_in_target_space = data.rect_; - gfx::Rect visible_rect_in_target_space = - layer->visible_drawable_content_rect(); - data.Update(visible_rect_in_target_space, mailboxId_); + gfx::Rect rect_in_target_space = layer->GetEnclosingRectInTargetSpace(); + data.Update(rect_in_target_space, mailboxId_); if (layer_is_new || layer->LayerPropertyChanged()) { // If a layer is new or has changed, then its entire layer rect affects the // target surface. - damage_for_this_update_.Union(visible_rect_in_target_space); + damage_for_this_update_.Union(rect_in_target_space); // The layer's old region is now exposed on the target surface, too. // Note old_rect_in_target_space is already in target space. diff --git a/chromium/cc/trees/damage_tracker_unittest.cc b/chromium/cc/trees/damage_tracker_unittest.cc index 00fa95b025d..3b0139f0a5e 100644 --- a/chromium/cc/trees/damage_tracker_unittest.cc +++ b/chromium/cc/trees/damage_tracker_unittest.cc @@ -5,8 +5,6 @@ #include "cc/trees/damage_tracker.h" #include <stddef.h> -#include <limits> -#include <utility> #include "base/memory/ptr_util.h" #include "cc/base/math_util.h" @@ -73,13 +71,6 @@ void ClearDamageForAllSurfaces(LayerImpl* root) { } } -void SetCopyRequest(LayerImpl* root) { - auto* root_node = root->layer_tree_impl()->property_trees()->effect_tree.Node( - root->effect_tree_index()); - root_node->has_copy_request = true; - root->layer_tree_impl()->property_trees()->effect_tree.set_needs_update(true); -} - class DamageTrackerTest : public LayerTreeImplTestBase, public testing::Test { public: LayerImpl* CreateTestTreeWithOneSurface(int number_of_children) { @@ -279,6 +270,7 @@ class DamageTrackerTest : public LayerTreeImplTestBase, public testing::Test { // 2. updating all damage trackers in the correct order // 3. resetting all update_rects and property_changed flags for all layers // and surfaces. + root->layer_tree_impl()->SetDeviceScaleFactor(device_scale_factor); root->layer_tree_impl()->set_needs_update_draw_properties(); UpdateDrawProperties(root->layer_tree_impl()); @@ -1758,8 +1750,6 @@ TEST_F(DamageTrackerTest, HugeDamageRect) { for (int i = 0; i < kRange; ++i) { LayerImpl* root = CreateAndSetUpTestTreeWithOneSurface(); LayerImpl* child = child_layers_[0]; - // Set copy request to damage the entire layer. - SetCopyRequest(root); gfx::Transform transform; transform.Translate(-kBigNumber, -kBigNumber); @@ -1797,9 +1787,6 @@ TEST_F(DamageTrackerTest, DamageRectTooBig) { LayerImpl* child1 = child_layers_[0]; LayerImpl* child2 = child_layers_[1]; - // Set copy request to damage the entire layer. - SetCopyRequest(root); - // Really far left. child1->SetOffsetToTransformParent( gfx::Vector2dF(std::numeric_limits<int>::min() + 100, 0)); @@ -1809,7 +1796,9 @@ TEST_F(DamageTrackerTest, DamageRectTooBig) { child2->SetOffsetToTransformParent( gfx::Vector2dF(std::numeric_limits<int>::max() - 100, 0)); child2->SetBounds(gfx::Size(1, 1)); - EmulateDrawingOneFrame(root, 1.f); + + float device_scale_factor = 1.f; + EmulateDrawingOneFrame(root, device_scale_factor); // The expected damage would be too large to store in a gfx::Rect, so we // should damage everything (ie, we don't have a valid rect). @@ -1828,9 +1817,6 @@ TEST_F(DamageTrackerTest, DamageRectTooBigWithFilter) { LayerImpl* child1 = child_layers_[0]; LayerImpl* child2 = child_layers_[1]; - // Set copy request to damage the entire layer. - SetCopyRequest(root); - FilterOperations filters; filters.Append(FilterOperation::CreateBlurFilter(5.f)); root->SetDrawsContent(true); @@ -1864,9 +1850,6 @@ TEST_F(DamageTrackerTest, DamageRectTooBigWithFilter) { TEST_F(DamageTrackerTest, DamageRectTooBigInRenderSurface) { LayerImpl* root = CreateAndSetUpTestTreeWithTwoSurfacesDrawingFullyVisible(); - // Set copy request to damage the entire layer. - SetCopyRequest(root); - // Really far left. grand_child1_->SetOffsetToTransformParent( gfx::Vector2dF(std::numeric_limits<int>::min() + 500, 0)); @@ -1948,9 +1931,6 @@ TEST_F(DamageTrackerTest, DamageRectTooBigInRenderSurface) { TEST_F(DamageTrackerTest, DamageRectTooBigInRenderSurfaceWithFilter) { LayerImpl* root = CreateAndSetUpTestTreeWithTwoSurfaces(); - // Set copy request to damage the entire layer. - SetCopyRequest(root); - // Set up a moving pixels filter on the child. FilterOperations filters; filters.Append(FilterOperation::CreateBlurFilter(5.f)); @@ -2206,164 +2186,5 @@ TEST_F(DamageTrackerTest, CanUseCachedBackdropFilterResultTest) { EXPECT_TRUE(GetRenderSurface(grand_child4_)->intersects_damage_under()); } -TEST_F(DamageTrackerTest, DamageRectOnlyVisibleContentsMoveToOutside) { - LayerImpl* root = CreateAndSetUpTestTreeWithOneSurface(2); - ClearDamageForAllSurfaces(root); - - LayerImpl* child1 = child_layers_[0]; - LayerImpl* child2 = child_layers_[1]; - gfx::Rect origin_damage = child1->visible_drawable_content_rect(); - origin_damage.Union(child2->visible_drawable_content_rect()); - - // Really far left. - child1->SetOffsetToTransformParent( - gfx::Vector2dF(std::numeric_limits<int>::min() + 100, 0)); - child1->SetBounds(gfx::Size(1, 1)); - - // Really far right. - child2->SetOffsetToTransformParent( - gfx::Vector2dF(std::numeric_limits<int>::max() - 100, 0)); - child2->SetBounds(gfx::Size(1, 1)); - EmulateDrawingOneFrame(root, 1.f); - - // Above damages should be excludebe because they're outside of - // the root surface. - gfx::Rect damage_rect; - EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( - &damage_rect)); - EXPECT_EQ(origin_damage, damage_rect); - EXPECT_TRUE(GetRenderSurface(root)->content_rect().Contains(damage_rect)); - EXPECT_TRUE(GetRenderSurface(root) - ->damage_tracker() - ->has_damage_from_contributing_content()); -} - -TEST_F(DamageTrackerTest, DamageRectOnlyVisibleContentsLargeTwoContents) { - LayerImpl* root = CreateAndSetUpTestTreeWithOneSurface(2); - ClearDamageForAllSurfaces(root); - - LayerImpl* child1 = child_layers_[0]; - LayerImpl* child2 = child_layers_[1]; - - gfx::Rect expected_damage = child1->visible_drawable_content_rect(); - expected_damage.Union(child2->visible_drawable_content_rect()); - expected_damage.set_x(0); - expected_damage.set_width(GetRenderSurface(root)->content_rect().width()); - - // Really far left. - child1->SetOffsetToTransformParent( - gfx::Vector2dF(std::numeric_limits<int>::min() + 100, 100)); - child1->SetBounds( - gfx::Size(std::numeric_limits<int>::max(), child1->bounds().height())); - - // Really far right. - child2->SetOffsetToTransformParent(gfx::Vector2dF(100, 100)); - child2->SetBounds( - gfx::Size(std::numeric_limits<int>::max(), child2->bounds().height())); - EmulateDrawingOneFrame(root, 1.f); - - // Above damages should be excludebe because they're outside of - // the root surface. - gfx::Rect damage_rect; - EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( - &damage_rect)); - EXPECT_EQ(expected_damage, damage_rect); - EXPECT_TRUE(GetRenderSurface(root)->content_rect().Contains(damage_rect)); - EXPECT_TRUE(GetRenderSurface(root) - ->damage_tracker() - ->has_damage_from_contributing_content()); -} - -TEST_F(DamageTrackerTest, - DamageRectOnlyVisibleContentsHugeContentPartiallyVisible) { - LayerImpl* root = CreateAndSetUpTestTreeWithOneSurface(1); - int content_width = GetRenderSurface(root)->content_rect().width(); - - ClearDamageForAllSurfaces(root); - - LayerImpl* child1 = child_layers_[0]; - int y = child1->offset_to_transform_parent().y(); - int offset = 100; - int expected_width = offset + child1->bounds().width(); - // Huge content that exceeds on both side. - child1->SetOffsetToTransformParent( - gfx::Vector2dF(std::numeric_limits<int>::min() + offset, y)); - child1->SetBounds( - gfx::Size(std::numeric_limits<int>::max(), child1->bounds().height())); - - EmulateDrawingOneFrame(root); - - gfx::Rect expected_damage_rect1(0, y, expected_width, - child1->bounds().height()); - - // Above damages should be excludebe because they're outside of - // the root surface. - gfx::Rect damage_rect; - EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( - &damage_rect)); - EXPECT_EQ(expected_damage_rect1, damage_rect); - EXPECT_TRUE(GetRenderSurface(root) - ->damage_tracker() - ->has_damage_from_contributing_content()); - - ClearDamageForAllSurfaces(root); - - // Now move the huge layer to the right, keeping offset visible. - child1->SetOffsetToTransformParent(gfx::Vector2dF(content_width - offset, y)); - child1->NoteLayerPropertyChanged(); - - EmulateDrawingOneFrame(root); - - // The damaged rect should be "letter boxed" region. - gfx::Rect expected_damage_rect2(0, y, content_width, - child1->bounds().height()); - EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( - &damage_rect)); - EXPECT_EQ(expected_damage_rect2, damage_rect); - EXPECT_TRUE(GetRenderSurface(root) - ->damage_tracker() - ->has_damage_from_contributing_content()); -} - -TEST_F(DamageTrackerTest, VerifyDamageExpansionWithBackdropBlurFilters) { - LayerImpl* root = CreateAndSetUpTestTreeWithTwoSurfaces(); - - // Allow us to set damage on child1_. - child1_->SetDrawsContent(true); - - FilterOperations filters; - filters.Append(FilterOperation::CreateBlurFilter(2.f)); - - // Setting the filter will damage the whole surface. - ClearDamageForAllSurfaces(root); - SetBackdropFilter(child1_, filters); - child1_->NoteLayerPropertyChanged(); - EmulateDrawingOneFrame(root); - - ClearDamageForAllSurfaces(root); - root->UnionUpdateRect(gfx::Rect(297, 297, 2, 2)); - EmulateDrawingOneFrame(root); - - // child1_'s render surface has a size of 206x208 due to the contributions - // from grand_child1_ and grand_child2_. The blur filter on child1_ intersects - // the damage from root and expands it to (100,100 206x208). - gfx::Rect expected_damage_rect = gfx::Rect(100, 100, 206, 208); - gfx::Rect root_damage_rect; - EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( - &root_damage_rect)); - EXPECT_EQ(expected_damage_rect, root_damage_rect); - - ClearDamageForAllSurfaces(root); - gfx::Rect damage_rect(97, 97, 2, 2); - root->UnionUpdateRect(damage_rect); - EmulateDrawingOneFrame(root); - - // The blur filter on child1_ doesn't intersect the damage from root so the - // damage remains unchanged. - EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( - &root_damage_rect)); - EXPECT_EQ(damage_rect, root_damage_rect); -} - } // namespace } // namespace cc diff --git a/chromium/cc/trees/debug_rect_history.cc b/chromium/cc/trees/debug_rect_history.cc index f2455eb24d3..6d460fad605 100644 --- a/chromium/cc/trees/debug_rect_history.cc +++ b/chromium/cc/trees/debug_rect_history.cc @@ -76,6 +76,7 @@ void DebugRectHistory::SaveLayoutShiftRects(HeadsUpDisplayLayerImpl* hud) { LAYOUT_SHIFT_RECT_TYPE, MathUtil::MapEnclosingClippedRect(hud->ScreenSpaceTransform(), rect))); } + hud->ClearLayoutShiftRects(); } void DebugRectHistory::SavePaintRects(LayerTreeImpl* tree_impl) { diff --git a/chromium/cc/trees/draw_property_utils.cc b/chromium/cc/trees/draw_property_utils.cc index aa4433edce3..b6c84a81c1d 100644 --- a/chromium/cc/trees/draw_property_utils.cc +++ b/chromium/cc/trees/draw_property_utils.cc @@ -412,15 +412,23 @@ bool IsTransformToRootOf3DRenderingContextBackFaceVisible( const TransformNode& transform_node = *transform_tree.Node(transform_tree_index); - if (transform_node.delegates_to_parent_for_backface) + const TransformNode* root_node = &transform_node; + if (transform_node.delegates_to_parent_for_backface) { transform_tree_index = transform_node.parent_id; + root_node = transform_tree.Node(transform_tree_index); + } int root_id = transform_tree_index; int sorting_context_id = transform_node.sorting_context_id; - while (root_id > 0 && transform_tree.Node(root_id - 1)->sorting_context_id == - sorting_context_id) - root_id--; + while (root_id > TransformTree::kRootNodeId) { + int parent_id = root_node->parent_id; + const TransformNode* parent_node = transform_tree.Node(parent_id); + if (parent_node->sorting_context_id != sorting_context_id) + break; + root_id = parent_id; + root_node = parent_node; + } // TODO(chrishtr): cache this on the transform trees if needed, similar to // |to_target| and |to_screen|. @@ -428,8 +436,7 @@ bool IsTransformToRootOf3DRenderingContextBackFaceVisible( if (transform_tree_index != root_id) property_trees->transform_tree.CombineTransformsBetween( transform_tree_index, root_id, &to_3d_root); - to_3d_root.PreconcatTransform( - property_trees->transform_tree.Node(root_id)->to_parent); + to_3d_root.PreconcatTransform(root_node->to_parent); return to_3d_root.IsBackFaceVisible(); } @@ -1187,7 +1194,6 @@ void ComputeDrawPropertiesOfVisibleLayers(const LayerImplList* layer_list, if (!only_draws_visible_content) { drawable_bounds = gfx::Rect(layer->bounds()); } - gfx::Rect visible_bounds_in_target_space = MathUtil::MapEnclosingClippedRect( layer->draw_properties().target_space_transform, drawable_bounds); diff --git a/chromium/cc/trees/effect_node.cc b/chromium/cc/trees/effect_node.cc index 20b4257603f..bf2aa003a08 100644 --- a/chromium/cc/trees/effect_node.cc +++ b/chromium/cc/trees/effect_node.cc @@ -146,6 +146,8 @@ const char* RenderSurfaceReasonToString(RenderSurfaceReason reason) { return "mirrored"; case RenderSurfaceReason::kSubtreeIsBeingCaptured: return "subtree being captured"; + case RenderSurfaceReason::kDocumentTransitionParticipant: + return "document transition participant"; case RenderSurfaceReason::kTest: return "test"; default: diff --git a/chromium/cc/trees/effect_node.h b/chromium/cc/trees/effect_node.h index 65edbca03b1..b0d13f4fcde 100644 --- a/chromium/cc/trees/effect_node.h +++ b/chromium/cc/trees/effect_node.h @@ -6,6 +6,7 @@ #define CC_TREES_EFFECT_NODE_H_ #include "cc/cc_export.h" +#include "cc/document_transition/document_transition_shared_element_id.h" #include "cc/paint/element_id.h" #include "cc/paint/filter_operations.h" #include "components/viz/common/surfaces/subtree_capture_id.h" @@ -46,6 +47,7 @@ enum class RenderSurfaceReason : uint8_t { kCopyRequest, kMirrored, kSubtreeIsBeingCaptured, + kDocumentTransitionParticipant, // This must be the last value because it's used in tracing code to know the // number of reasons. kTest, @@ -160,6 +162,9 @@ struct CC_EXPORT EffectNode { int closest_ancestor_with_copy_request_id; int closest_ancestor_being_captured_id; + // Represents a shared element id for the document transition API. + DocumentTransitionSharedElementId document_transition_shared_element_id; + bool HasRenderSurface() const { return render_surface_reason != RenderSurfaceReason::kNone; } diff --git a/chromium/cc/trees/frame_rate_estimator.cc b/chromium/cc/trees/frame_rate_estimator.cc index 577b6a1d710..0ba9354e594 100644 --- a/chromium/cc/trees/frame_rate_estimator.cc +++ b/chromium/cc/trees/frame_rate_estimator.cc @@ -49,7 +49,7 @@ void FrameRateEstimator::WillDraw(base::TimeTicks now) { // 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; + (viz::BeginFrameArgs::DefaultInterval() * 2) - kFudgeDelta; if (draw_delta < kMinDelta) num_of_consecutive_frames_with_min_delta_++; else diff --git a/chromium/cc/trees/frame_rate_estimator.h b/chromium/cc/trees/frame_rate_estimator.h index 7ea16833ca5..378a21784e6 100644 --- a/chromium/cc/trees/frame_rate_estimator.h +++ b/chromium/cc/trees/frame_rate_estimator.h @@ -20,6 +20,7 @@ class CC_EXPORT FrameRateEstimator { void WillDraw(base::TimeTicks now); void NotifyInputEvent(); base::TimeDelta GetPreferredInterval() const; + bool input_priority_mode() const { return input_priority_mode_; } private: void OnExitInputPriorityMode(); diff --git a/chromium/cc/trees/frame_rate_estimator_unittest.cc b/chromium/cc/trees/frame_rate_estimator_unittest.cc index d99fabf842b..03df3952a9b 100644 --- a/chromium/cc/trees/frame_rate_estimator_unittest.cc +++ b/chromium/cc/trees/frame_rate_estimator_unittest.cc @@ -4,6 +4,7 @@ #include "cc/trees/frame_rate_estimator.h" +#include "base/stl_util.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" @@ -69,5 +70,28 @@ TEST_F(FrameRateEstimatorTest, InputPriorityMode) { EXPECT_NE(estimator_->GetPreferredInterval(), viz::BeginFrameArgs::MinInterval()); } + +TEST_F(FrameRateEstimatorTest, RafAtHalfFps) { + estimator_->SetFrameEstimationEnabled(true); + // Recorded rAF intervals at 30 fps. + const base::TimeDelta kIntervals[] = { + base::TimeDelta::FromMicroseconds(33425), + base::TimeDelta::FromMicroseconds(33298), + base::TimeDelta::FromMicroseconds(33396), + base::TimeDelta::FromMicroseconds(33339), + base::TimeDelta::FromMicroseconds(33431), + base::TimeDelta::FromMicroseconds(33320), + base::TimeDelta::FromMicroseconds(33364), + base::TimeDelta::FromMicroseconds(33360)}; + const base::TimeDelta kIntervalForHalfFps = + viz::BeginFrameArgs::DefaultInterval() * 2; + base::TimeTicks time; + for (size_t i = 0; i <= base::size(kIntervals); ++i) { + estimator_->WillDraw(time); + EXPECT_EQ(kIntervalForHalfFps, estimator_->GetPreferredInterval()); + if (i < base::size(kIntervals)) + time += kIntervals[i]; + } +} } // namespace } // namespace cc diff --git a/chromium/cc/trees/layer_tree_frame_sink_client.h b/chromium/cc/trees/layer_tree_frame_sink_client.h index e56d9c563cd..a093f88e43e 100644 --- a/chromium/cc/trees/layer_tree_frame_sink_client.h +++ b/chromium/cc/trees/layer_tree_frame_sink_client.h @@ -5,6 +5,8 @@ #ifndef CC_TREES_LAYER_TREE_FRAME_SINK_CLIENT_H_ #define CC_TREES_LAYER_TREE_FRAME_SINK_CLIENT_H_ +#include <vector> + #include "base/callback.h" #include "base/memory/ref_counted.h" #include "base/optional.h" @@ -87,6 +89,11 @@ class CC_EXPORT LayerTreeFrameSinkClient { const gfx::Rect& viewport_rect, const gfx::Transform& transform) = 0; + // Notification that the compositor frame transition directive has been + // processed. + virtual void OnCompositorFrameTransitionDirectiveProcessed( + uint32_t sequence_id) {} + protected: virtual ~LayerTreeFrameSinkClient() {} }; diff --git a/chromium/cc/trees/layer_tree_host.cc b/chromium/cc/trees/layer_tree_host.cc index 3b598eb3144..2a5cb55897f 100644 --- a/chromium/cc/trees/layer_tree_host.cc +++ b/chromium/cc/trees/layer_tree_host.cc @@ -17,6 +17,7 @@ #include "base/bind.h" #include "base/command_line.h" #include "base/containers/adapters.h" +#include "base/containers/contains.h" #include "base/location.h" #include "base/logging.h" #include "base/memory/ptr_util.h" @@ -143,6 +144,8 @@ LayerTreeHost::LayerTreeHost(InitParams params, CompositorMode mode) debug_state_(settings_.initial_debug_state), id_(s_layer_tree_host_sequence_number.GetNext() + 1), task_graph_runner_(params.task_graph_runner), + main_thread_pipeline_(params.main_thread_pipeline), + compositor_thread_pipeline_(params.compositor_thread_pipeline), event_listener_properties_(), mutator_host_(params.mutator_host), dark_mode_filter_(params.dark_mode_filter) { @@ -371,7 +374,7 @@ void LayerTreeHost::FinishCommitOnImplThread(LayerTreeHostImpl* host_impl) { // properties, which updates the clobber_active_value flag. // TODO(pdr): Enforce this comment with DCHECKS and a lifecycle state. sync_tree->property_trees()->scroll_tree.PushScrollUpdatesFromMainThread( - property_trees(), sync_tree); + property_trees(), sync_tree, settings_.commit_fractional_scroll_deltas); // This must happen after synchronizing property trees and after push // properties, which updates property tree indices, but before animation @@ -490,10 +493,19 @@ void LayerTreeHost::CommitComplete() { client_->DidCompletePageScaleAnimation(); did_complete_scale_animation_ = false; } +} - for (auto& closure : committed_document_transition_callbacks_) - std::move(closure).Run(); - committed_document_transition_callbacks_.clear(); +void LayerTreeHost::NotifyTransitionRequestsFinished( + const std::vector<uint32_t>& sequence_ids) { + // TODO(vmpstr): This might also be a good spot to expire long standing + // requests if they were not finished. + for (auto& sequence_id : sequence_ids) { + auto it = document_transition_callbacks_.find(sequence_id); + if (it == document_transition_callbacks_.end()) + continue; + std::move(it->second).Run(); + document_transition_callbacks_.erase(it); + } } void LayerTreeHost::SetLayerTreeFrameSink( @@ -652,6 +664,9 @@ void LayerTreeHost::SetNextCommitWaitsForActivation() { void LayerTreeHost::SetNeedsCommitWithForcedRedraw() { next_commit_forces_redraw_ = true; + // This method is used by tests to ensure a commit before grabbing a screen + // shot or processing input, so do not defer the commit. + StopDeferringCommits(PaintHoldingCommitTrigger::kFeatureDisabled); proxy_->SetNeedsCommit(); } @@ -1333,8 +1348,8 @@ void LayerTreeHost::StartPageScaleAnimation(const gfx::Vector2d& target_offset, bool use_anchor, float scale, base::TimeDelta duration) { - pending_page_scale_animation_.reset(new PendingPageScaleAnimation( - target_offset, use_anchor, scale, duration)); + pending_page_scale_animation_ = std::make_unique<PendingPageScaleAnimation>( + target_offset, use_anchor, scale, duration); SetNeedsCommit(); } @@ -1358,6 +1373,14 @@ void LayerTreeHost::SetDisplayColorSpaces( layer->SetNeedsDisplay(); } +void LayerTreeHost::UpdateViewportIsMobileOptimized( + bool is_viewport_mobile_optimized) { + if (is_viewport_mobile_optimized_ == is_viewport_mobile_optimized) + return; + is_viewport_mobile_optimized_ = is_viewport_mobile_optimized; + SetNeedsCommit(); +} + void LayerTreeHost::SetExternalPageScaleFactor( float page_scale_factor, bool is_external_pinch_gesture_active) { @@ -1627,10 +1650,13 @@ void LayerTreeHost::PushLayerTreePropertiesTo(LayerTreeImpl* tree_impl) { // Transfer page transition directives. for (auto& request : document_transition_requests_) { - // Store the commit callback on LayerTreeHost, so that we can invoke them in - // CommitComplete. - committed_document_transition_callbacks_.push_back( - request->TakeCommitCallback()); + // Store the commit callback on LayerTreeHost, so that we can invoke them + // when the request is finished. + DCHECK(!base::Contains(document_transition_callbacks_, + request->sequence_id())); + document_transition_callbacks_[request->sequence_id()] = + request->TakeFinishedCallback(); + tree_impl->AddDocumentTransitionRequest(std::move(request)); } document_transition_requests_.clear(); @@ -1655,6 +1681,7 @@ void LayerTreeHost::PushLayerTreeHostPropertiesTo( host_impl->SetDebugState(debug_state_); host_impl->SetVisualDeviceViewportSize(visual_device_viewport_size_); + host_impl->set_viewport_mobile_optimized(is_viewport_mobile_optimized_); } Layer* LayerTreeHost::LayerByElementId(ElementId element_id) const { @@ -1871,6 +1898,9 @@ void LayerTreeHost::SetSourceURL(ukm::SourceId source_id, const GURL& url) { // produced by this host so far. clear_caches_on_next_commit_ = true; proxy_->SetSourceURL(source_id, url); + // If this is not used as a common web page, don't show HUD. + if (!url.SchemeIsHTTPOrHTTPS()) + debug_state_.TurnOffHudInfoDisplay(); } base::ReadOnlySharedMemoryRegion @@ -1901,14 +1931,25 @@ void LayerTreeHost::SetEnableFrameRateThrottling( } void LayerTreeHost::SetDelegatedInkMetadata( - std::unique_ptr<viz::DelegatedInkMetadata> metadata) { + std::unique_ptr<gfx::DelegatedInkMetadata> metadata) { delegated_ink_metadata_ = std::move(metadata); SetNeedsCommit(); } +gfx::RenderingPipeline* LayerTreeHost::TakeMainPipeline() { + auto* pipeline = main_thread_pipeline_; + main_thread_pipeline_ = nullptr; + return pipeline; +} + +gfx::RenderingPipeline* LayerTreeHost::TakeCompositorPipeline() { + auto* pipeline = compositor_thread_pipeline_; + compositor_thread_pipeline_ = nullptr; + return pipeline; +} + std::vector<std::unique_ptr<DocumentTransitionRequest>> LayerTreeHost::TakeDocumentTransitionRequestsForTesting() { return std::move(document_transition_requests_); } - } // namespace cc diff --git a/chromium/cc/trees/layer_tree_host.h b/chromium/cc/trees/layer_tree_host.h index 3447ff7c385..ec44f47b6d5 100644 --- a/chromium/cc/trees/layer_tree_host.h +++ b/chromium/cc/trees/layer_tree_host.h @@ -52,15 +52,16 @@ #include "cc/trees/swap_promise_manager.h" #include "cc/trees/target_property.h" #include "cc/trees/viewport_layers.h" -#include "components/viz/common/delegated_ink_metadata.h" #include "components/viz/common/resources/resource_format.h" #include "components/viz/common/surfaces/local_surface_id.h" #include "services/metrics/public/cpp/ukm_source_id.h" +#include "ui/gfx/delegated_ink_metadata.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/overlay_transform.h" namespace gfx { struct PresentationFeedback; +class RenderingPipeline; } namespace cc { @@ -114,6 +115,8 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient { scoped_refptr<base::SingleThreadTaskRunner> main_task_runner; MutatorHost* mutator_host = nullptr; RasterDarkModeFilter* dark_mode_filter = nullptr; + gfx::RenderingPipeline* main_thread_pipeline = nullptr; + gfx::RenderingPipeline* compositor_thread_pipeline = nullptr; // The image worker task runner is used to schedule image decodes. The // compositor thread may make sync calls to this thread, analogous to the @@ -424,6 +427,8 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient { gfx::Rect device_viewport_rect() const { return device_viewport_rect_; } + void UpdateViewportIsMobileOptimized(bool is_viewport_mobile_optimized); + void SetBrowserControlsParams(const BrowserControlsParams& params); void SetBrowserControlsShownRatio(float top_ratio, float bottom_ratio); @@ -627,6 +632,11 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient { void RecordEndOfFrameMetrics(base::TimeTicks frame_begin_time, ActiveFrameSequenceTrackers trackers); void NotifyThroughputTrackerResults(CustomTrackerResults results); + void NotifyTransitionRequestsFinished( + const std::vector<uint32_t>& sequence_ids); + // Called during impl side initialization. + gfx::RenderingPipeline* TakeMainPipeline(); + gfx::RenderingPipeline* TakeCompositorPipeline(); LayerTreeHostClient* client() { return client_; } LayerTreeHostSchedulingClient* scheduling_client() { @@ -727,8 +737,8 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient { } void SetDelegatedInkMetadata( - std::unique_ptr<viz::DelegatedInkMetadata> metadata); - viz::DelegatedInkMetadata* DelegatedInkMetadataForTesting() { + std::unique_ptr<gfx::DelegatedInkMetadata> metadata); + gfx::DelegatedInkMetadata* DelegatedInkMetadataForTesting() { return delegated_ink_metadata_.get(); } @@ -839,7 +849,10 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient { // destroyed midway which causes a crash. crbug.com/654672 bool inside_main_frame_ = false; + // State cached until impl side is initialized. TaskGraphRunner* task_graph_runner_; + gfx::RenderingPipeline* main_thread_pipeline_; + gfx::RenderingPipeline* compositor_thread_pipeline_; uint32_t num_consecutive_frames_without_slow_paths_ = 0; @@ -882,6 +895,12 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient { gfx::Rect visual_device_viewport_intersection_rect_; gfx::Size visual_device_viewport_size_; + // Set to true if viewport is mobile optimized by using meta tag + // <meta name="viewport" content="width=device-width"> + // or + // <meta name="viewport" content="initial-scale=1.0"> + bool is_viewport_mobile_optimized_ = false; + bool have_scroll_event_handlers_ = false; EventListenerProperties event_listener_properties_ [static_cast<size_t>(EventListenerClass::kLast) + 1]; @@ -963,17 +982,15 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient { // stroke. std::unique_ptr was specifically chosen so that it would be cleared // as it is forwarded along the pipeline to avoid old information incorrectly // sticking around and potentially being reused. - std::unique_ptr<viz::DelegatedInkMetadata> delegated_ink_metadata_; + std::unique_ptr<gfx::DelegatedInkMetadata> delegated_ink_metadata_; // A list of document transitions that need to be transported from Blink to // Viz, as a CompositorFrameTransitionDirective. std::vector<std::unique_ptr<DocumentTransitionRequest>> document_transition_requests_; - // A list of callbacks that need to be invoked in commit callback, - // representing document transitions that have been committed to - // LayerTreeImpl. - std::vector<base::OnceClosure> committed_document_transition_callbacks_; + // A list of callbacks that need to be invoked when they are processed. + base::flat_map<uint32_t, base::OnceClosure> document_transition_callbacks_; // Used to vend weak pointers to LayerTreeHost to ScopedDeferMainFrameUpdate // objects. diff --git a/chromium/cc/trees/layer_tree_host_impl.cc b/chromium/cc/trees/layer_tree_host_impl.cc index 2c3b6b3a2a8..94c8f648d1b 100644 --- a/chromium/cc/trees/layer_tree_host_impl.cc +++ b/chromium/cc/trees/layer_tree_host_impl.cc @@ -10,6 +10,8 @@ #include <algorithm> #include <limits> #include <list> +#include <map> +#include <memory> #include <string> #include "base/auto_reset.h" @@ -20,6 +22,7 @@ #include "base/containers/flat_map.h" #include "base/debug/crash_logging.h" #include "base/debug/dump_without_crashing.h" +#include "base/feature_list.h" #include "base/location.h" #include "base/logging.h" #include "base/memory/ptr_util.h" @@ -172,7 +175,10 @@ bool HasMobileViewport(LayerTreeImpl* active_tree) { bool IsMobileOptimized(LayerTreeImpl* active_tree) { bool has_mobile_viewport = HasMobileViewport(active_tree); bool has_fixed_page_scale = HasFixedPageScale(active_tree); - return has_fixed_page_scale || has_mobile_viewport; + return has_fixed_page_scale || has_mobile_viewport || + (base::FeatureList::IsEnabled( + ::features::kRemoveMobileViewportDoubleTap) && + active_tree->viewport_mobile_optimized()); } viz::ResourceFormat TileRasterBufferFormat( @@ -1172,12 +1178,14 @@ DrawResult LayerTreeHostImpl::CalculateRenderPasses(FrameData* frame) { size_t surface_index = render_surface_list_size - 1 - i; RenderSurfaceImpl* render_surface = (*frame->render_surface_list)[surface_index]; + const auto& shared_element_id = + render_surface->GetDocumentTransitionSharedElementId(); bool is_root_surface = render_surface->EffectTreeIndex() == EffectTree::kContentsRootNodeId; bool should_draw_into_render_pass = is_root_surface || render_surface->contributes_to_drawn_surface() || - render_surface->CopyOfOutputRequired(); + render_surface->CopyOfOutputRequired() || shared_element_id.valid(); if (should_draw_into_render_pass) frame->render_passes.push_back(render_surface->CreateRenderPass()); } @@ -2031,6 +2039,8 @@ void LayerTreeHostImpl::DidReceiveCompositorFrameAck() { void LayerTreeHostImpl::DidPresentCompositorFrame( uint32_t frame_token, const viz::FrameTimingDetails& details) { + devtools_instrumentation::DidPresentFrame( + id_, frame_token, details.presentation_feedback.timestamp); PresentationTimeCallbackBuffer::PendingCallbacks activated_callbacks = presentation_time_callbacks_.PopPendingCallbacks(frame_token); @@ -2157,6 +2167,12 @@ void LayerTreeHostImpl::OnDraw(const gfx::Transform& transform, } } +void LayerTreeHostImpl::OnCompositorFrameTransitionDirectiveProcessed( + uint32_t sequence_id) { + finished_transition_request_sequence_ids_.push_back(sequence_id); + SetNeedsCommit(); +} + void LayerTreeHostImpl::OnCanDrawStateChangedForTree() { client_->OnCanDrawStateChanged(CanDraw()); } @@ -2210,7 +2226,7 @@ viz::CompositorFrameMetadata LayerTreeHostImpl::MakeCompositorFrameMetadata() { metadata.display_transform_hint = active_tree_->display_transform_hint(); - if (std::unique_ptr<viz::DelegatedInkMetadata> delegated_ink_metadata = + if (std::unique_ptr<gfx::DelegatedInkMetadata> delegated_ink_metadata = active_tree_->take_delegated_ink_metadata()) { delegated_ink_metadata->set_frame_time(CurrentBeginFrameArgs().frame_time); TRACE_EVENT_INSTANT1( @@ -2219,10 +2235,6 @@ viz::CompositorFrameMetadata LayerTreeHostImpl::MakeCompositorFrameMetadata() { delegated_ink_metadata->ToString()); metadata.delegated_ink_metadata = std::move(delegated_ink_metadata); } - - for (auto& request : active_tree_->TakeDocumentTransitionRequests()) - metadata.transition_directives.push_back(request->ConstructDirective()); - return metadata; } @@ -2415,7 +2427,7 @@ bool LayerTreeHostImpl::DrawLayers(FrameData* frame) { active_tree_->ResetAllChangeTracking(); active_tree_->set_has_ever_been_drawn(true); - devtools_instrumentation::DidDrawFrame(id_); + devtools_instrumentation::DidDrawFrame(id_, frame_token); benchmark_instrumentation::IssueImplThreadRenderingStatsEvent( rendering_stats_instrumentation_->TakeImplThreadRenderingStats()); @@ -2483,6 +2495,25 @@ viz::CompositorFrame LayerTreeHostImpl::GenerateCompositorFrame( } viz::CompositorFrameMetadata metadata = MakeCompositorFrameMetadata(); + + std::map<DocumentTransitionSharedElementId, viz::CompositorRenderPassId> + shared_element_render_pass_id_map; + for (RenderSurfaceImpl* render_surface : *frame->render_surface_list) { + const auto& shared_element_id = + render_surface->GetDocumentTransitionSharedElementId(); + if (!shared_element_id.valid()) + continue; + DCHECK( + !base::Contains(shared_element_render_pass_id_map, shared_element_id)); + shared_element_render_pass_id_map[shared_element_id] = + render_surface->render_pass_id(); + } + + for (auto& request : active_tree_->TakeDocumentTransitionRequests()) { + metadata.transition_directives.push_back( + request->ConstructDirective(shared_element_render_pass_id_map)); + } + PopulateMetadataContentColorUsage(frame, &metadata); metadata.may_contain_video = frame->may_contain_video; metadata.deadline = viz::FrameDeadline( @@ -2497,6 +2528,8 @@ viz::CompositorFrame LayerTreeHostImpl::GenerateCompositorFrame( if (enable_frame_rate_throttling_) { metadata.preferred_frame_interval = viz::BeginFrameArgs::MaxInterval(); } else if (mutator_host_->MainThreadAnimationsCount() == 0 && + mutator_host_->NeedsTickAnimations() && + !frame_rate_estimator_.input_priority_mode() && mutator_host_->MinimumTickInterval() > kMinDelta) { // All animations are impl-thread animations that tick at no more than // half the default display compositing fps. @@ -2512,7 +2545,8 @@ viz::CompositorFrame LayerTreeHostImpl::GenerateCompositorFrame( // to general webpages. metadata.preferred_frame_interval = kTwiceOfDefaultInterval; } else { - // There are main-thread or high frequency impl-thread animations. + // There are main-thread, high frequency impl-thread animations, or input + // events. frame_rate_estimator_.WillDraw(CurrentBeginFrameArgs().frame_time); metadata.preferred_frame_interval = frame_rate_estimator_.GetPreferredInterval(); @@ -2525,7 +2559,7 @@ viz::CompositorFrame LayerTreeHostImpl::GenerateCompositorFrame( if (render_frame_metadata_observer_) { last_draw_render_frame_metadata_ = MakeRenderFrameMetadata(frame); - if (viz::DelegatedInkMetadata* ink_metadata = + if (gfx::DelegatedInkMetadata* ink_metadata = metadata.delegated_ink_metadata.get()) { last_draw_render_frame_metadata_->delegated_ink_metadata = DelegatedInkBrowserMetadata(ink_metadata->is_hovering()); @@ -3434,8 +3468,8 @@ void LayerTreeHostImpl::CreateTileManagerResources() { TaskGraphRunner* task_graph_runner = task_graph_runner_; if (is_synchronous_single_threaded_) { DCHECK(!single_thread_synchronous_task_graph_runner_); - single_thread_synchronous_task_graph_runner_.reset( - new SynchronousTaskGraphRunner); + single_thread_synchronous_task_graph_runner_ = + std::make_unique<SynchronousTaskGraphRunner>(); task_graph_runner = single_thread_synchronous_task_graph_runner_.get(); } @@ -3548,6 +3582,13 @@ std::unique_ptr<MutatorEvents> LayerTreeHostImpl::TakeMutatorEvents() { return events; } +std::vector<uint32_t> +LayerTreeHostImpl::TakeFinishedTransitionRequestSequenceIds() { + std::vector<uint32_t> result; + result.swap(finished_transition_request_sequence_ids_); + return result; +} + void LayerTreeHostImpl::ClearCaches() { // It is safe to clear the decode policy tracking on navigations since it // comes with an invalidation and the image ids are never re-used. diff --git a/chromium/cc/trees/layer_tree_host_impl.h b/chromium/cc/trees/layer_tree_host_impl.h index d62e352010b..3490d866cf5 100644 --- a/chromium/cc/trees/layer_tree_host_impl.h +++ b/chromium/cc/trees/layer_tree_host_impl.h @@ -395,6 +395,14 @@ class CC_EXPORT LayerTreeHostImpl : public TileManagerClient, // compositors. This is specified in device viewport coordinate space. void SetVisualDeviceViewportSize(const gfx::Size&); + void set_viewport_mobile_optimized(bool viewport_mobile_optimized) { + is_viewport_mobile_optimized_ = viewport_mobile_optimized; + } + + bool viewport_mobile_optimized() const { + return is_viewport_mobile_optimized_; + } + // Updates registered ElementIds present in |changed_list|. Call this after // changing the property trees for the |changed_list| trees. void UpdateElements(ElementListType changed_list); @@ -549,6 +557,8 @@ class CC_EXPORT LayerTreeHostImpl : public TileManagerClient, const gfx::Rect& viewport, bool resourceless_software_draw, bool skip_draw) override; + void OnCompositorFrameTransitionDirectiveProcessed( + uint32_t sequence_id) override; // Called from LayerTreeImpl. void OnCanDrawStateChangedForTree(); @@ -781,6 +791,9 @@ class CC_EXPORT LayerTreeHostImpl : public TileManagerClient, // Returns mutator events to be handled by BeginMainFrame. std::unique_ptr<MutatorEvents> TakeMutatorEvents(); + // Returns all of the transition request sequence ids that were finished. + std::vector<uint32_t> TakeFinishedTransitionRequestSequenceIds(); + void ClearCaches(); void UpdateImageDecodingHints( @@ -1138,6 +1151,11 @@ class CC_EXPORT LayerTreeHostImpl : public TileManagerClient, std::unique_ptr<Viewport> viewport_; gfx::Size visual_device_viewport_size_; + // Set to true if viewport is mobile optimized by using meta tag + // <meta name="viewport" content="width=device-width"> + // or + // <meta name="viewport" content="initial-scale=1.0"> + bool is_viewport_mobile_optimized_ = false; std::unique_ptr<PendingTreeRasterDurationHistogramTimer> pending_tree_raster_duration_timer_; @@ -1239,6 +1257,8 @@ class CC_EXPORT LayerTreeHostImpl : public TileManagerClient, // some pre-defined criteria. ThrottleDecider throttle_decider_; + std::vector<uint32_t> finished_transition_request_sequence_ids_; + // 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 3c78b9ea2eb..f695ad3a05c 100644 --- a/chromium/cc/trees/layer_tree_host_impl_unittest.cc +++ b/chromium/cc/trees/layer_tree_host_impl_unittest.cc @@ -8,6 +8,7 @@ #include <algorithm> #include <cmath> +#include <memory> #include <utility> #include "base/base_switches.h" @@ -108,12 +109,12 @@ statements; \ } -using ::testing::Mock; -using ::testing::Return; +using media::VideoFrame; +using ::testing::_; using ::testing::AnyNumber; using ::testing::AtLeast; -using ::testing::_; -using media::VideoFrame; +using ::testing::Mock; +using ::testing::Return; using ScrollThread = cc::InputHandler::ScrollThread; @@ -4479,9 +4480,8 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, PageScaleAnimation) { did_request_redraw_ = false; did_request_next_frame_ = false; host_impl_->active_tree()->SetPendingPageScaleAnimation( - std::unique_ptr<PendingPageScaleAnimation>( - new PendingPageScaleAnimation(gfx::Vector2d(), false, 2, - duration))); + std::make_unique<PendingPageScaleAnimation>(gfx::Vector2d(), false, 2, + duration)); host_impl_->ActivateSyncTree(); EXPECT_FALSE(did_request_redraw_); EXPECT_TRUE(did_request_next_frame_); @@ -4540,9 +4540,8 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, PageScaleAnimation) { did_request_redraw_ = false; did_request_next_frame_ = false; host_impl_->active_tree()->SetPendingPageScaleAnimation( - std::unique_ptr<PendingPageScaleAnimation>( - new PendingPageScaleAnimation(gfx::Vector2d(25, 25), true, - min_page_scale, duration))); + std::make_unique<PendingPageScaleAnimation>(gfx::Vector2d(25, 25), true, + min_page_scale, duration)); host_impl_->ActivateSyncTree(); EXPECT_FALSE(did_request_redraw_); EXPECT_TRUE(did_request_next_frame_); @@ -4606,8 +4605,8 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, PageScaleAnimationNoOp) { scroll_layer->element_id(), gfx::ScrollOffset(50, 50)); host_impl_->active_tree()->SetPendingPageScaleAnimation( - std::unique_ptr<PendingPageScaleAnimation>( - new PendingPageScaleAnimation(gfx::Vector2d(), true, 1, duration))); + std::make_unique<PendingPageScaleAnimation>(gfx::Vector2d(), true, 1, + duration)); host_impl_->ActivateSyncTree(); begin_frame_args.frame_time = start_time; begin_frame_args.frame_id.sequence_number++; @@ -4673,8 +4672,8 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, // Make sure TakePageScaleAnimation works properly. host_impl_->sync_tree()->SetPendingPageScaleAnimation( - std::unique_ptr<PendingPageScaleAnimation>(new PendingPageScaleAnimation( - gfx::Vector2d(), false, target_scale, duration))); + std::make_unique<PendingPageScaleAnimation>(gfx::Vector2d(), false, + target_scale, duration)); std::unique_ptr<PendingPageScaleAnimation> psa = host_impl_->sync_tree()->TakePendingPageScaleAnimation(); EXPECT_EQ(target_scale, psa->scale); @@ -4686,8 +4685,8 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, did_request_redraw_ = false; did_request_next_frame_ = false; host_impl_->sync_tree()->SetPendingPageScaleAnimation( - std::unique_ptr<PendingPageScaleAnimation>(new PendingPageScaleAnimation( - gfx::Vector2d(), false, target_scale, duration))); + std::make_unique<PendingPageScaleAnimation>(gfx::Vector2d(), false, + target_scale, duration)); begin_frame_args.frame_time = halfway_through_animation; begin_frame_args.frame_id.sequence_number++; host_impl_->WillBeginImplFrame(begin_frame_args); @@ -4786,8 +4785,8 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, did_complete_page_scale_animation_ = false; host_impl_->active_tree()->SetPendingPageScaleAnimation( - std::unique_ptr<PendingPageScaleAnimation>( - new PendingPageScaleAnimation(gfx::Vector2d(), false, 2, duration))); + std::make_unique<PendingPageScaleAnimation>(gfx::Vector2d(), false, 2, + duration)); host_impl_->ActivateSyncTree(); begin_frame_args.frame_time = start_time; begin_frame_args.frame_id.sequence_number++; @@ -4847,14 +4846,8 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, gfx::Point position(295, 195); gfx::Vector2dF offset(0, 50); -// TODO(bokan): Unfortunately, Mac currently doesn't support smooth scrolling -// wheel events. https://crbug.com/574283. -#if defined(OS_MAC) - std::vector<ui::ScrollInputType> types = {ui::ScrollInputType::kScrollbar}; -#else std::vector<ui::ScrollInputType> types = {ui::ScrollInputType::kScrollbar, ui::ScrollInputType::kWheel}; -#endif for (auto type : types) { auto begin_state = BeginState(position, offset, type); begin_state->data()->set_current_native_scrolling_element( @@ -13142,32 +13135,6 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, EXPECT_EQ(node->surface_contents_scale, gfx::Vector2dF(1, 1)); } -#if defined(OS_MAC) -// Ensure Mac wheel scrolling causes instant scrolling. This test can be removed -// once https://crbug.com/574283 is fixed. -TEST_P(ScrollUnifiedLayerTreeHostImplTest, MacWheelIsNonAnimated) { - const gfx::Size content_size(1000, 1000); - const gfx::Size viewport_size(50, 100); - SetupViewportLayersOuterScrolls(viewport_size, content_size); - LayerImpl* scrolling_layer = OuterViewportScrollLayer(); - - host_impl_->set_force_smooth_wheel_scrolling_for_testing(false); - ASSERT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, - GetInputHandler() - .ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(0, 50), - ui::ScrollInputType::kWheel) - .get(), - ui::ScrollInputType::kWheel) - .thread); - GetInputHandler().ScrollUpdate( - AnimatedUpdateState(gfx::Point(), gfx::Vector2d(0, 50)).get()); - - // Ensure the scroll update happens immediately. - EXPECT_EQ(CurrentScrollOffset(scrolling_layer).y(), 50); - GetInputHandler().ScrollEnd(); -} -#endif - TEST_P(ScrollUnifiedLayerTreeHostImplTest, OneScrollForFirstScrollDelay) { LayerTreeSettings settings = DefaultSettings(); settings.commit_to_active_tree = false; @@ -18270,7 +18237,8 @@ TEST_F(LayerTreeHostImplTest, DocumentTransitionRequestCausesDamage) { // Adding a transition effect should cause us to redraw. host_impl_->active_tree()->AddDocumentTransitionRequest( - DocumentTransitionRequest::CreateStart(base::OnceClosure())); + DocumentTransitionRequest::CreateStart( + /*document_tag=*/0, /*shared_element_count=*/0, base::OnceClosure())); // Ensure there is damage and we requested a redraw. host_impl_->OnDraw(draw_transform, draw_viewport, resourceless_software_draw, diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_filters.cc b/chromium/cc/trees/layer_tree_host_pixeltest_filters.cc index d0a756797cf..68f86cbdd79 100644 --- a/chromium/cc/trees/layer_tree_host_pixeltest_filters.cc +++ b/chromium/cc/trees/layer_tree_host_pixeltest_filters.cc @@ -4,6 +4,8 @@ #include <stddef.h> +#include <memory> + #include "base/strings/string_number_conversions.h" #include "build/build_config.h" #include "cc/layers/picture_layer.h" @@ -77,6 +79,9 @@ INSTANTIATE_TEST_SUITE_P(All, ::testing::ValuesIn(viz::GetRendererTypes()), ::testing::PrintToStringParamName()); +// viz::GetRendererTypes() can return an empty list on some platforms. +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(LayerTreeHostFiltersPixelTest); + using LayerTreeHostFiltersPixelTestGPU = LayerTreeHostFiltersPixelTest; INSTANTIATE_TEST_SUITE_P(All, @@ -84,6 +89,9 @@ INSTANTIATE_TEST_SUITE_P(All, ::testing::ValuesIn(viz::GetGpuRendererTypes()), ::testing::PrintToStringParamName()); +// viz::GetGpuRendererTypes() can return an empty list on some platforms. +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(LayerTreeHostFiltersPixelTestGPU); + TEST_P(LayerTreeHostFiltersPixelTest, BackdropFilterBlurRect) { scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer( gfx::Rect(200, 200), SK_ColorWHITE); @@ -223,11 +231,11 @@ TEST_P(LayerTreeHostFiltersPixelTest, BackdropFilterBlurRounded) { 1.01f * percentage_pixels_small_error; // Divide average error by 4 since we blur most of the result. float average_error_allowed_in_bad_pixels = small_error_threshold / 4.f; - pixel_comparator_.reset(new FuzzyPixelComparator( + pixel_comparator_ = std::make_unique<FuzzyPixelComparator>( true, // discard_alpha percentage_pixels_large_or_small_error, percentage_pixels_small_error, average_error_allowed_in_bad_pixels, large_error_limit, - small_error_threshold)); + small_error_threshold); RunPixelTest(background, use_software_renderer() ? base::FilePath(FILE_PATH_LITERAL( @@ -360,6 +368,10 @@ INSTANTIATE_TEST_SUITE_P(PixelResourceTest, ::testing::ValuesIn(viz::GetGpuRendererTypes()), ::testing::PrintToStringParamName()); +// viz::GetGpuRendererTypes() can return an empty list on some platforms. +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST( + LayerTreeHostBlurFiltersPixelTestGPULayerList); + // TODO(michaelludwig): Re-enable after Skia roll and update expected images. // See skbug.com/9545 TEST_P(LayerTreeHostBlurFiltersPixelTestGPULayerList, @@ -466,6 +478,10 @@ INSTANTIATE_TEST_SUITE_P(All, ::testing::ValuesIn(viz::GetRendererTypes()), ::testing::PrintToStringParamName()); +// viz::GetRendererTypes() can return an empty list on some platforms. +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST( + LayerTreeHostFiltersScaledPixelTest); + TEST_P(LayerTreeHostFiltersScaledPixelTest, StandardDpi) { RunPixelTestType(100, 1.f); } @@ -575,7 +591,8 @@ TEST_P(LayerTreeHostFiltersPixelTest, ImageFilterScaled) { filter->SetBackdropFilters(filters); filter->ClearBackdropFilterBounds(); -#if defined(OS_WIN) || defined(_MIPS_ARCH_LOONGSON) || defined(ARCH_CPU_ARM64) +#if defined(OS_WIN) || defined(OS_MAC) || defined(_MIPS_ARCH_LOONGSON) || \ + defined(ARCH_CPU_ARM64) #if defined(OS_WIN) // Windows has 153 pixels off by at most 2: crbug.com/225027 float percentage_pixels_large_error = 0.3825f; // 153px / (200*200) @@ -585,6 +602,10 @@ TEST_P(LayerTreeHostFiltersPixelTest, ImageFilterScaled) { percentage_pixels_large_error = 0.415f; // 166px / (200*200) large_error_allowed = 1; } +#elif defined(OS_MAC) + // There's a 1 pixel error on MacOS + float percentage_pixels_large_error = 0.0025f; // 1px / (200*200) + int 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) @@ -596,11 +617,11 @@ TEST_P(LayerTreeHostFiltersPixelTest, ImageFilterScaled) { float percentage_pixels_small_error = 0.0f; float average_error_allowed_in_bad_pixels = 1.f; int small_error_allowed = 0; - pixel_comparator_.reset(new FuzzyPixelComparator( + pixel_comparator_ = std::make_unique<FuzzyPixelComparator>( true, // discard_alpha percentage_pixels_large_error, percentage_pixels_small_error, average_error_allowed_in_bad_pixels, large_error_allowed, - small_error_allowed)); + small_error_allowed); #endif RunPixelTest( @@ -657,11 +678,11 @@ TEST_P(LayerTreeHostFiltersPixelTest, BackdropFilterRotated) { float average_error_allowed_in_bad_pixels = 2.f; int large_error_allowed = 2; int small_error_allowed = 0; - pixel_comparator_.reset(new FuzzyPixelComparator( + pixel_comparator_ = std::make_unique<FuzzyPixelComparator>( true, // discard_alpha percentage_pixels_large_error, percentage_pixels_small_error, average_error_allowed_in_bad_pixels, large_error_allowed, - small_error_allowed)); + small_error_allowed); RunPixelTest(background, base::FilePath(FILE_PATH_LITERAL("backdrop_filter_rotated_.png")) @@ -715,10 +736,10 @@ TEST_P(LayerTreeHostFiltersPixelTest, ImageRenderSurfaceScaled) { average_error_allowed_in_bad_pixels = 1.f; large_error_allowed = 1; } - pixel_comparator_.reset(new FuzzyPixelComparator( + pixel_comparator_ = std::make_unique<FuzzyPixelComparator>( /*discard_alpha=*/true, percentage_pixels_large_error, percentage_pixels_small_error, average_error_allowed_in_bad_pixels, - large_error_allowed, small_error_allowed)); + large_error_allowed, small_error_allowed); RunPixelTest( background, @@ -843,11 +864,11 @@ TEST_P(LayerTreeHostFiltersPixelTest, RotatedFilter) { #endif float percentage_pixels_small_error = 0.0f; int small_error_allowed = 0; - pixel_comparator_.reset(new FuzzyPixelComparator( + pixel_comparator_ = std::make_unique<FuzzyPixelComparator>( true, // discard_alpha percentage_pixels_large_error, percentage_pixels_small_error, average_error_allowed_in_bad_pixels, large_error_allowed, - small_error_allowed)); + small_error_allowed); #endif RunPixelTest(background, @@ -882,7 +903,8 @@ TEST_P(LayerTreeHostFiltersPixelTest, RotatedDropShadowFilter) { background->AddChild(child); -#if defined(OS_WIN) || defined(ARCH_CPU_ARM64) +#if defined(OS_WIN) || defined(OS_MAC) || defined(OS_CHROMEOS) || \ + defined(ARCH_CPU_ARM64) || defined(USE_OZONE) #if defined(ARCH_CPU_ARM64) && \ (defined(OS_WIN) || defined(OS_FUCHSIA) || defined(OS_MAC)) // Windows, macOS, and Fuchsia on ARM64 has some pixels difference. @@ -890,6 +912,11 @@ TEST_P(LayerTreeHostFiltersPixelTest, RotatedDropShadowFilter) { float percentage_pixels_large_error = 0.89f; float average_error_allowed_in_bad_pixels = 5.f; int large_error_allowed = 17; +#elif defined(OS_MAC) || defined(OS_CHROMEOS) || defined(USE_OZONE) + // There's a 1 pixel error on MacOS and ChromeOS + float percentage_pixels_large_error = 0.00111112f; // 1px / (300*300) + float average_error_allowed_in_bad_pixels = 1.f; + int large_error_allowed = 1; #else // Windows and all other ARM64 have 3 pixels off by 1: crbug.com/259915 float percentage_pixels_large_error = 0.00333334f; // 3px / (300*300) @@ -901,11 +928,11 @@ TEST_P(LayerTreeHostFiltersPixelTest, RotatedDropShadowFilter) { #endif float percentage_pixels_small_error = 0.0f; int small_error_allowed = 0; - pixel_comparator_.reset(new FuzzyPixelComparator( + pixel_comparator_ = std::make_unique<FuzzyPixelComparator>( true, // discard_alpha percentage_pixels_large_error, percentage_pixels_small_error, average_error_allowed_in_bad_pixels, large_error_allowed, - small_error_allowed)); + small_error_allowed); #else if (use_skia_vulkan()) pixel_comparator_ = std::make_unique<FuzzyPixelOffByOneComparator>(true); @@ -1158,6 +1185,9 @@ INSTANTIATE_TEST_SUITE_P(All, ::testing::ValuesIn(viz::GetRendererTypes()), ::testing::PrintToStringParamName()); +// viz::GetRendererTypes() can return an empty list on some platforms. +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(BackdropFilterOffsetTest); + TEST_P(BackdropFilterOffsetTest, StandardDpi) { RunPixelTestType(1.f); } @@ -1194,11 +1224,11 @@ class BackdropFilterInvertTest : public LayerTreeHostFiltersPixelTest { int large_error_allowed = 1; float percentage_pixels_small_error = 0.0f; int small_error_allowed = 0; - pixel_comparator_.reset(new FuzzyPixelComparator( + pixel_comparator_ = std::make_unique<FuzzyPixelComparator>( true, // discard_alpha percentage_pixels_large_error, percentage_pixels_small_error, average_error_allowed_in_bad_pixels, large_error_allowed, - small_error_allowed)); + small_error_allowed); } RunPixelTest(std::move(root), expected_result); } @@ -1218,6 +1248,9 @@ INSTANTIATE_TEST_SUITE_P(All, ::testing::ValuesIn(viz::GetRendererTypes()), ::testing::PrintToStringParamName()); +// viz::GetRendererTypes() can return an empty list on some platforms. +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(BackdropFilterInvertTest); + TEST_P(BackdropFilterInvertTest, StandardDpi) { RunPixelTestType(1.f); } diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_readback.cc b/chromium/cc/trees/layer_tree_host_pixeltest_readback.cc index ea957180edb..4a7504c5a5b 100644 --- a/chromium/cc/trees/layer_tree_host_pixeltest_readback.cc +++ b/chromium/cc/trees/layer_tree_host_pixeltest_readback.cc @@ -105,7 +105,9 @@ class LayerTreeHostReadbackPixelTest void ReadbackResultAsBitmap(std::unique_ptr<viz::CopyOutputResult> result) { EXPECT_TRUE(task_runner_provider()->IsMainThread()); EXPECT_FALSE(result->IsEmpty()); - result_bitmap_ = std::make_unique<SkBitmap>(result->AsSkBitmap()); + auto scoped_sk_bitmap = result->ScopedAccessSkBitmap(); + result_bitmap_ = + std::make_unique<SkBitmap>(scoped_sk_bitmap.GetOutScopedBitmap()); EXPECT_TRUE(result_bitmap_->readyToDraw()); EndTest(); } @@ -121,12 +123,12 @@ class LayerTreeHostReadbackPixelTest std::unique_ptr<viz::SingleReleaseCallback> release_callback = result->TakeTextureOwnership(); - const SkBitmap bitmap = + SkBitmap bitmap = CopyMailboxToBitmap(result->size(), mailbox, sync_token, color_space); release_callback->Run(gpu::SyncToken(), false); ReadbackResultAsBitmap(std::make_unique<viz::CopyOutputSkBitmapResult>( - result->rect(), bitmap)); + result->rect(), std::move(bitmap))); } gfx::Rect copy_subrect_; diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_scrollbars.cc b/chromium/cc/trees/layer_tree_host_pixeltest_scrollbars.cc index b531ed8694d..9eb9276fe56 100644 --- a/chromium/cc/trees/layer_tree_host_pixeltest_scrollbars.cc +++ b/chromium/cc/trees/layer_tree_host_pixeltest_scrollbars.cc @@ -78,6 +78,9 @@ INSTANTIATE_TEST_SUITE_P(All, ::testing::ValuesIn(viz::GetGpuRendererTypes()), ::testing::PrintToStringParamName()); +// viz::GetGpuRendererTypes() can return an empty list on some platforms. +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(LayerTreeHostScrollbarsPixelTest); + TEST_P(LayerTreeHostScrollbarsPixelTest, NoScale) { scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorWHITE); @@ -258,6 +261,10 @@ INSTANTIATE_TEST_SUITE_P(All, ::testing::ValuesIn(viz::GetGpuRendererTypes()), ::testing::PrintToStringParamName()); +// viz::GetGpuRendererTypes() can return an empty list on some platforms. +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST( + LayerTreeHostOverlayScrollbarsPixelTest); + // Simulate increasing the thickness of a painted overlay scrollbar. Ensure that // the scrollbar border remains crisp. TEST_P(LayerTreeHostOverlayScrollbarsPixelTest, NinePatchScrollbarScaledUp) { diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_synchronous.cc b/chromium/cc/trees/layer_tree_host_pixeltest_synchronous.cc index 266aabd3262..5e19dadb059 100644 --- a/chromium/cc/trees/layer_tree_host_pixeltest_synchronous.cc +++ b/chromium/cc/trees/layer_tree_host_pixeltest_synchronous.cc @@ -56,6 +56,10 @@ INSTANTIATE_TEST_SUITE_P(All, ::testing::ValuesIn(viz::GetGpuRendererTypes()), ::testing::PrintToStringParamName()); +// viz::GetGpuRendererTypes() can return an empty list on some platforms. +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST( + LayerTreeHostSynchronousPixelTest); + TEST_P(LayerTreeHostSynchronousPixelTest, OneContentLayerZeroCopy) { set_raster_type(TestRasterType::kZeroCopy); DoContentLayerTest(); diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_tiles.cc b/chromium/cc/trees/layer_tree_host_pixeltest_tiles.cc index a3f7a12b671..a10ad840469 100644 --- a/chromium/cc/trees/layer_tree_host_pixeltest_tiles.cc +++ b/chromium/cc/trees/layer_tree_host_pixeltest_tiles.cc @@ -284,6 +284,10 @@ INSTANTIATE_TEST_SUITE_P(All, ::testing::ValuesIn(kTestCasesMultiThread), ::testing::PrintToStringParamName()); +// kTestCasesMultiThread is empty on some platforms. +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST( + LayerTreeHostTilesTestPartialInvalidationMultiThread); + #if (defined(OS_LINUX) || defined(OS_CHROMEOS)) && defined(THREAD_SANITIZER) // Flaky on Linux TSAN. https://crbug.com/707711 #define MAYBE_PartialRaster DISABLED_PartialRaster diff --git a/chromium/cc/trees/layer_tree_host_unittest.cc b/chromium/cc/trees/layer_tree_host_unittest.cc index 79ceaf87f18..221b06ff45e 100644 --- a/chromium/cc/trees/layer_tree_host_unittest.cc +++ b/chromium/cc/trees/layer_tree_host_unittest.cc @@ -8,6 +8,7 @@ #include <stdint.h> #include <algorithm> +#include <memory> #include "base/auto_reset.h" #include "base/bind.h" @@ -2998,7 +2999,7 @@ class LayerTreeHostTestDamageWithScale : public LayerTreeHostTest { &client_, std::move(recording)); root_layer_->SetBounds(gfx::Size(50, 50)); - recording.reset(new FakeRecordingSource); + recording = std::make_unique<FakeRecordingSource>(); child_layer_ = FakePictureLayer::CreateWithRecordingSource( &client_, std::move(recording)); child_layer_->SetBounds(gfx::Size(25, 25)); @@ -3047,13 +3048,13 @@ class LayerTreeHostTestDamageWithScale : public LayerTreeHostTest { host_impl->active_tree()->LayerById(child_layer_->id())); // We remove tilings pretty aggressively if they are not ideal. Add this // back in so that we can compare - // child_layer_impl->visible_drawable_content_rect() to the damage. + // child_layer_impl->GetEnclosingRectInTargetSpace to the damage. child_layer_impl->AddTilingUntilNextDraw(1.3f); - EXPECT_EQ(gfx::Rect(25, 25), root_damage_rect); - EXPECT_EQ(child_layer_impl->visible_drawable_content_rect(), + EXPECT_EQ(gfx::Rect(26, 26), root_damage_rect); + EXPECT_EQ(child_layer_impl->GetEnclosingRectInTargetSpace(), root_damage_rect); - EXPECT_TRUE(child_layer_impl->visible_drawable_content_rect().Contains( + EXPECT_TRUE(child_layer_impl->GetEnclosingRectInTargetSpace().Contains( gfx::Rect(child_layer_->bounds()))); break; } @@ -8903,10 +8904,10 @@ class LayerTreeHostTestDelegatedInkMetadataOnAndOff base::TimeTicks timestamp = base::TimeTicks::Now(); bool is_hovering = true; - expected_metadata_ = viz::DelegatedInkMetadata( + expected_metadata_ = gfx::DelegatedInkMetadata( point, diameter, color, timestamp, area, is_hovering); layer_tree_host()->SetDelegatedInkMetadata( - std::make_unique<viz::DelegatedInkMetadata>( + std::make_unique<gfx::DelegatedInkMetadata>( expected_metadata_.value())); } @@ -8928,7 +8929,7 @@ class LayerTreeHostTestDelegatedInkMetadataOnAndOff void ExpectMetadata(base::Optional<DelegatedInkBrowserMetadata> browser_delegated_ink_metadata, - viz::DelegatedInkMetadata* actual_metadata) { + gfx::DelegatedInkMetadata* actual_metadata) { if (expected_metadata_.has_value()) { EXPECT_TRUE(browser_delegated_ink_metadata.has_value()); EXPECT_TRUE(actual_metadata); @@ -8965,7 +8966,7 @@ class LayerTreeHostTestDelegatedInkMetadataOnAndOff } private: - base::Optional<viz::DelegatedInkMetadata> expected_metadata_; + base::Optional<gfx::DelegatedInkMetadata> expected_metadata_; FakeContentLayerClient client_; scoped_refptr<Layer> layer_; bool set_needs_display_ = true; @@ -9464,10 +9465,11 @@ class LayerTreeHostTestDocumentTransitionsPropagatedToMetadata layer_tree_host()->AddDocumentTransitionRequest( DocumentTransitionRequest::CreatePrepare( DocumentTransitionRequest::Effect::kExplode, - base::TimeDelta::FromMilliseconds(123), + /*document_tag=*/0, /*shared_element_count=*/0, base::BindLambdaForTesting([this]() { CommitLambdaCalled(); }))); layer_tree_host()->AddDocumentTransitionRequest( DocumentTransitionRequest::CreateStart( + /*document_tag=*/0, /*shared_element_count=*/0, base::BindLambdaForTesting([this]() { CommitLambdaCalled(); }))); } @@ -9477,23 +9479,29 @@ class LayerTreeHostTestDocumentTransitionsPropagatedToMetadata const viz::CompositorFrame& frame) override { ASSERT_EQ(2u, frame.metadata.transition_directives.size()); const auto& save = frame.metadata.transition_directives[0]; + submitted_sequence_ids_.push_back(save.sequence_id()); EXPECT_EQ(save.type(), viz::CompositorFrameTransitionDirective::Type::kSave); EXPECT_EQ(save.effect(), viz::CompositorFrameTransitionDirective::Effect::kExplode); - EXPECT_EQ(save.duration(), base::TimeDelta::FromMilliseconds(123)); const auto& animate = frame.metadata.transition_directives[1]; EXPECT_GT(animate.sequence_id(), save.sequence_id()); EXPECT_EQ(animate.type(), viz::CompositorFrameTransitionDirective::Type::kAnimate); + submitted_sequence_ids_.push_back(animate.sequence_id()); + } + void DidReceiveCompositorFrameAck() override { + layer_tree_host()->NotifyTransitionRequestsFinished( + submitted_sequence_ids_); EndTest(); } void AfterTest() override { EXPECT_EQ(2, num_lambda_calls_); } + std::vector<uint32_t> submitted_sequence_ids_; int num_lambda_calls_ = 0; }; diff --git a/chromium/cc/trees/layer_tree_host_unittest_animation.cc b/chromium/cc/trees/layer_tree_host_unittest_animation.cc index 4d80dbe7f76..1b528540583 100644 --- a/chromium/cc/trees/layer_tree_host_unittest_animation.cc +++ b/chromium/cc/trees/layer_tree_host_unittest_animation.cc @@ -185,7 +185,7 @@ class LayerTreeHostAnimationTestAddKeyframeModel int group) override { EXPECT_LT(base::TimeTicks(), monotonic_time); - KeyframeModel* keyframe_model = + gfx::KeyframeModel* keyframe_model = animation_->GetKeyframeModel(TargetProperty::OPACITY); if (keyframe_model) animation_->RemoveKeyframeModel(keyframe_model->id()); @@ -936,7 +936,7 @@ class LayerTreeHostAnimationTestScrollOffsetAnimationAdjusted GetImplTimelineAndAnimationByID(*host_impl); // This happens after the impl-only animation is added in // WillCommitCompleteOnThread. - KeyframeModel* keyframe_model = + gfx::KeyframeModel* keyframe_model = ScrollOffsetKeyframeEffect(*host_impl, scroll_layer_) .GetKeyframeModel(TargetProperty::SCROLL_OFFSET); DCHECK(keyframe_model); @@ -962,7 +962,7 @@ class LayerTreeHostAnimationTestScrollOffsetAnimationAdjusted void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override { if (host_impl->sync_tree()->source_frame_number() == 1) { - KeyframeModel* keyframe_model = + gfx::KeyframeModel* keyframe_model = ScrollOffsetKeyframeEffect(*host_impl, scroll_layer_) .GetKeyframeModel(TargetProperty::SCROLL_OFFSET); DCHECK(keyframe_model); diff --git a/chromium/cc/trees/layer_tree_host_unittest_checkerimaging.cc b/chromium/cc/trees/layer_tree_host_unittest_checkerimaging.cc index 9801b1718cb..6c47500d499 100644 --- a/chromium/cc/trees/layer_tree_host_unittest_checkerimaging.cc +++ b/chromium/cc/trees/layer_tree_host_unittest_checkerimaging.cc @@ -20,8 +20,9 @@ const char kCheckerboardImagesMetric[] = "CheckerboardedImagesCount"; class LayerTreeHostCheckerImagingTest : public LayerTreeTest { public: - LayerTreeHostCheckerImagingTest() : url_(GURL("https://example.com")), - ukm_source_id_(123) {} + LayerTreeHostCheckerImagingTest() + : url_(GURL("https://example.com")), + ukm_source_id_(ukm::AssignNewSourceId()) {} void BeginTest() override { layer_tree_host()->SetSourceURL(ukm_source_id_, url_); @@ -36,8 +37,9 @@ class LayerTreeHostCheckerImagingTest : public LayerTreeTest { recorder->UpdateSourceURL(ukm_source_id_, url_); // Change the source to ensure any accumulated metrics are flushed. - impl->ukm_manager()->SetSourceId(200); - recorder->UpdateSourceURL(200, GURL("chrome://test2")); + ukm::SourceId newSourceId = ukm::AssignNewSourceId(); + impl->ukm_manager()->SetSourceId(newSourceId); + recorder->UpdateSourceURL(newSourceId, GURL("chrome://test2")); const auto& entries = recorder->GetEntriesByName(kRenderingEvent); ASSERT_EQ(1u, entries.size()); diff --git a/chromium/cc/trees/layer_tree_host_unittest_copyrequest.cc b/chromium/cc/trees/layer_tree_host_unittest_copyrequest.cc index d6fbf463ba1..73e69db5df9 100644 --- a/chromium/cc/trees/layer_tree_host_unittest_copyrequest.cc +++ b/chromium/cc/trees/layer_tree_host_unittest_copyrequest.cc @@ -158,7 +158,8 @@ class LayerTreeHostCopyRequestTestMultipleRequests std::unique_ptr<viz::CopyOutputResult> result) { EXPECT_TRUE(layer_tree_host()->GetTaskRunnerProvider()->IsMainThread()); EXPECT_FALSE(result->IsEmpty()); - const SkBitmap& bitmap = result->AsSkBitmap(); + auto scoped_sk_bitmap = result->ScopedAccessSkBitmap(); + const SkBitmap& bitmap = scoped_sk_bitmap.bitmap(); EXPECT_TRUE(bitmap.readyToDraw()); EXPECT_EQ(result->size(), gfx::Size(bitmap.width(), bitmap.height())); callbacks_[id] = result->size(); @@ -179,6 +180,10 @@ INSTANTIATE_TEST_SUITE_P( CombineWithCompositorModes(viz::GetRendererTypesNoDawn()), PrintTupleToStringParamName()); +// viz::GetGpuRendererTypesNoDawn() is empty on some platforms. +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST( + LayerTreeHostCopyRequestTestMultipleRequests); + TEST_P(LayerTreeHostCopyRequestTestMultipleRequests, Test) { RunTest(compositor_mode()); } @@ -222,6 +227,10 @@ INSTANTIATE_TEST_SUITE_P( CombineWithCompositorModes(viz::GetGpuRendererTypesNoDawn()), PrintTupleToStringParamName()); +// viz::GetGpuRendererTypesNoDawn() is empty on some platforms. +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST( + LayerTreeHostCopyRequestTestMultipleRequestsOutOfOrder); + TEST_P(LayerTreeHostCopyRequestTestMultipleRequestsOutOfOrder, Test) { RunTest(compositor_mode()); } @@ -281,6 +290,10 @@ INSTANTIATE_TEST_SUITE_P( CombineWithCompositorModes(viz::GetGpuRendererTypesNoDawn()), PrintTupleToStringParamName()); +// viz::GetGpuRendererTypesNoDawn() is empty on some platforms. +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST( + LayerTreeHostCopyRequestCompletionCausesCommit); + TEST_P(LayerTreeHostCopyRequestCompletionCausesCommit, Test) { RunTest(compositor_mode()); } @@ -388,6 +401,10 @@ INSTANTIATE_TEST_SUITE_P( CombineWithCompositorModes(viz::GetGpuRendererTypesNoDawn()), PrintTupleToStringParamName()); +// viz::GetGpuRendererTypesNoDawn() is empty on some platforms. +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST( + LayerTreeHostCopyRequestTestLayerDestroyed); + TEST_P(LayerTreeHostCopyRequestTestLayerDestroyed, Test) { RunTest(compositor_mode()); } @@ -494,6 +511,10 @@ INSTANTIATE_TEST_SUITE_P( CombineWithCompositorModes(viz::GetGpuRendererTypesNoDawn()), PrintTupleToStringParamName()); +// viz::GetGpuRendererTypesNoDawn() is empty on some platforms. +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST( + LayerTreeHostCopyRequestTestInHiddenSubtree); + TEST_P(LayerTreeHostCopyRequestTestInHiddenSubtree, Test) { RunTest(compositor_mode()); } @@ -615,6 +636,10 @@ INSTANTIATE_TEST_SUITE_P( CombineWithCompositorModes(viz::GetGpuRendererTypesNoDawn()), PrintTupleToStringParamName()); +// viz::GetGpuRendererTypesNoDawn() is empty on some platforms. +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST( + LayerTreeHostTestHiddenSurfaceNotAllocatedForSubtreeCopyRequest); + TEST_P(LayerTreeHostTestHiddenSurfaceNotAllocatedForSubtreeCopyRequest, Test) { RunTest(compositor_mode()); } @@ -671,6 +696,10 @@ INSTANTIATE_TEST_SUITE_P( CombineWithCompositorModes(viz::GetGpuRendererTypesNoDawn()), PrintTupleToStringParamName()); +// viz::GetGpuRendererTypesNoDawn() is empty on some platforms. +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST( + LayerTreeHostCopyRequestTestClippedOut); + TEST_P(LayerTreeHostCopyRequestTestClippedOut, Test) { RunTest(compositor_mode()); } @@ -731,6 +760,10 @@ INSTANTIATE_TEST_SUITE_P( CombineWithCompositorModes(viz::GetGpuRendererTypesNoDawn()), PrintTupleToStringParamName()); +// viz::GetGpuRendererTypesNoDawn() is empty on some platforms. +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST( + LayerTreeHostCopyRequestTestScaledLayer); + TEST_P(LayerTreeHostCopyRequestTestScaledLayer, Test) { RunTest(compositor_mode()); } @@ -825,6 +858,10 @@ INSTANTIATE_TEST_SUITE_P( CombineWithCompositorModes(viz::GetGpuRendererTypesNoDawn()), PrintTupleToStringParamName()); +// viz::GetGpuRendererTypesNoDawn() is empty on some platforms. +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST( + LayerTreeHostTestAsyncTwoReadbacksWithoutDraw); + TEST_P(LayerTreeHostTestAsyncTwoReadbacksWithoutDraw, Test) { RunTest(compositor_mode()); } @@ -975,6 +1012,10 @@ INSTANTIATE_TEST_SUITE_P( CombineWithCompositorModes(viz::GetGpuRendererTypesNoDawn()), PrintTupleToStringParamName()); +// viz::GetGpuRendererTypesNoDawn() is empty on some platforms. +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST( + LayerTreeHostCopyRequestTestDeleteSharedImage); + TEST_P(LayerTreeHostCopyRequestTestDeleteSharedImage, Test) { RunTest(compositor_mode()); } @@ -1125,6 +1166,10 @@ INSTANTIATE_TEST_SUITE_P( CombineWithCompositorModes(viz::GetGpuRendererTypesNoDawn()), PrintTupleToStringParamName()); +// viz::GetGpuRendererTypesNoDawn() is empty on some platforms. +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST( + LayerTreeHostCopyRequestTestCreatesSharedImage); + TEST_P(LayerTreeHostCopyRequestTestCreatesSharedImage, Test) { RunTest(compositor_mode()); } @@ -1213,6 +1258,10 @@ INSTANTIATE_TEST_SUITE_P( CombineWithCompositorModes(viz::GetGpuRendererTypesNoDawn()), PrintTupleToStringParamName()); +// viz::GetGpuRendererTypesNoDawn() is empty on some platforms. +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST( + LayerTreeHostCopyRequestTestDestroyBeforeCopy); + TEST_P(LayerTreeHostCopyRequestTestDestroyBeforeCopy, Test) { RunTest(compositor_mode()); } @@ -1296,6 +1345,10 @@ INSTANTIATE_TEST_SUITE_P( CombineWithCompositorModes(viz::GetGpuRendererTypesNoDawn()), PrintTupleToStringParamName()); +// viz::GetGpuRendererTypesNoDawn() is empty on some platforms. +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST( + LayerTreeHostCopyRequestTestShutdownBeforeCopy); + TEST_P(LayerTreeHostCopyRequestTestShutdownBeforeCopy, Test) { RunTest(compositor_mode()); } @@ -1428,6 +1481,10 @@ INSTANTIATE_TEST_SUITE_P( CombineWithCompositorModes(viz::GetGpuRendererTypesNoDawn()), PrintTupleToStringParamName()); +// viz::GetGpuRendererTypesNoDawn() is empty on some platforms. +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST( + LayerTreeHostCopyRequestTestMultipleDrawsHiddenCopyRequest); + TEST_P(LayerTreeHostCopyRequestTestMultipleDrawsHiddenCopyRequest, Test) { RunTest(compositor_mode()); } diff --git a/chromium/cc/trees/layer_tree_host_unittest_scroll.cc b/chromium/cc/trees/layer_tree_host_unittest_scroll.cc index 1aa61b98e70..16dc43ba245 100644 --- a/chromium/cc/trees/layer_tree_host_unittest_scroll.cc +++ b/chromium/cc/trees/layer_tree_host_unittest_scroll.cc @@ -541,7 +541,7 @@ class LayerTreeHostScrollTestScrollSnapping : public LayerTreeHostScrollTest { PostSetNeedsCommitToMainThread(); break; case 2: - translate.Translate(-3, 0); + translate.Translate(-4, 0); EXPECT_EQ(translate, scroll_layer->draw_properties().screen_space_transform); EndTest(); diff --git a/chromium/cc/trees/layer_tree_impl.cc b/chromium/cc/trees/layer_tree_impl.cc index 1ac475f2afe..171f018b5c9 100644 --- a/chromium/cc/trees/layer_tree_impl.cc +++ b/chromium/cc/trees/layer_tree_impl.cc @@ -1491,7 +1491,7 @@ LayerImpl* LayerTreeImpl::LayerById(int id) const { return iter != layer_id_map_.end() ? iter->second : nullptr; } -// TODO(masonfreed): If this shows up on profiles, this could use +// TODO(masonf): If this shows up on profiles, this could use // a layer_element_map_ approach similar to LayerById(). LayerImpl* LayerTreeImpl::LayerByElementId(ElementId element_id) const { auto it = diff --git a/chromium/cc/trees/layer_tree_impl.h b/chromium/cc/trees/layer_tree_impl.h index ec13bafda99..6d8a6ce426a 100644 --- a/chromium/cc/trees/layer_tree_impl.h +++ b/chromium/cc/trees/layer_tree_impl.h @@ -735,10 +735,10 @@ class CC_EXPORT LayerTreeImpl { // only be called after the JS API |updateInkTrailStartPoint| has been // called, which populates the metadata with provided information. void set_delegated_ink_metadata( - std::unique_ptr<viz::DelegatedInkMetadata> metadata) { + std::unique_ptr<gfx::DelegatedInkMetadata> metadata) { delegated_ink_metadata_ = std::move(metadata); } - std::unique_ptr<viz::DelegatedInkMetadata> take_delegated_ink_metadata() { + std::unique_ptr<gfx::DelegatedInkMetadata> take_delegated_ink_metadata() { return std::move(delegated_ink_metadata_); } @@ -750,6 +750,10 @@ class CC_EXPORT LayerTreeImpl { return device_viewport_rect_changed_; } + bool viewport_mobile_optimized() const { + return host_impl_->viewport_mobile_optimized(); + } + // Add a document transition request from the embedder. void AddDocumentTransitionRequest( std::unique_ptr<DocumentTransitionRequest> request); @@ -914,7 +918,7 @@ class CC_EXPORT LayerTreeImpl { // Event metrics that are reported back from the main thread. EventMetrics::List events_metrics_from_main_thread_; - std::unique_ptr<viz::DelegatedInkMetadata> delegated_ink_metadata_; + std::unique_ptr<gfx::DelegatedInkMetadata> delegated_ink_metadata_; // Document transition requests to be transferred to Viz. std::vector<std::unique_ptr<DocumentTransitionRequest>> diff --git a/chromium/cc/trees/property_tree.cc b/chromium/cc/trees/property_tree.cc index 8bc3982468a..541f361a773 100644 --- a/chromium/cc/trees/property_tree.cc +++ b/chromium/cc/trees/property_tree.cc @@ -1534,10 +1534,7 @@ void ScrollTree::CollectScrollDeltas( } } -void ScrollTree::CollectScrollDeltasForTesting() { - LayerTreeSettings settings; - bool use_fractional_deltas = settings.commit_fractional_scroll_deltas; - +void ScrollTree::CollectScrollDeltasForTesting(bool use_fractional_deltas) { for (auto map_entry : synced_scroll_offset_map_) { PullDeltaForMainThread(map_entry.second.get(), use_fractional_deltas); } @@ -1545,7 +1542,8 @@ void ScrollTree::CollectScrollDeltasForTesting() { void ScrollTree::PushScrollUpdatesFromMainThread( PropertyTrees* main_property_trees, - LayerTreeImpl* sync_tree) { + LayerTreeImpl* sync_tree, + bool use_fractional_deltas) { DCHECK(!property_trees()->is_main_thread); const ScrollOffsetMap& main_scroll_offset_map = main_property_trees->scroll_tree.scroll_offset_map_; @@ -1571,6 +1569,16 @@ void ScrollTree::PushScrollUpdatesFromMainThread( // committed PropertyTrees. bool needs_scroll_update = synced_scroll_offset->PushMainToPending(map_entry.second); + // If `use_fractional_deltas` is false, then check against the rounded + // pending offset instead of the offset directly. This matches + // PullDeltaForMainThread where only an integer delta is extracted and + // prevents unnecessary property change in this case. + if (!use_fractional_deltas) { + gfx::ScrollOffset pending_offset = synced_scroll_offset->Current(false); + gfx::ScrollOffset rounded_offset = gfx::ScrollOffset( + roundf(pending_offset.x()), roundf(pending_offset.y())); + needs_scroll_update = map_entry.second != rounded_offset; + } // If we are committing directly to the active tree, push pending to active // here. If the value differs between the pending and active trees, we need diff --git a/chromium/cc/trees/property_tree.h b/chromium/cc/trees/property_tree.h index 2bb36062737..b9ebbce70f0 100644 --- a/chromium/cc/trees/property_tree.h +++ b/chromium/cc/trees/property_tree.h @@ -461,7 +461,8 @@ class CC_EXPORT ScrollTree final : public PropertyTree<ScrollNode> { // Pushes scroll updates from the ScrollTree on the main thread onto the // impl thread associated state. void PushScrollUpdatesFromMainThread(PropertyTrees* main_property_trees, - LayerTreeImpl* sync_tree); + LayerTreeImpl* sync_tree, + bool use_fractional_deltas); // Pushes scroll updates from the ScrollTree on the pending tree onto the // active tree associated state. @@ -481,7 +482,7 @@ class CC_EXPORT ScrollTree final : public PropertyTree<ScrollNode> { const gfx::Vector2dF& delta); const gfx::ScrollOffset GetScrollOffsetBaseForTesting(ElementId id) const; const gfx::ScrollOffset GetScrollOffsetDeltaForTesting(ElementId id) const; - void CollectScrollDeltasForTesting(); + void CollectScrollDeltasForTesting(bool use_fractional_deltas = false); gfx::Vector2dF ScrollBy(const ScrollNode& scroll_node, const gfx::Vector2dF& scroll, diff --git a/chromium/cc/trees/property_tree_unittest.cc b/chromium/cc/trees/property_tree_unittest.cc index 489ece12ea2..0bd9e1bd1a5 100644 --- a/chromium/cc/trees/property_tree_unittest.cc +++ b/chromium/cc/trees/property_tree_unittest.cc @@ -7,10 +7,14 @@ #include <utility> #include "cc/input/main_thread_scrolling_reason.h" +#include "cc/test/fake_impl_task_runner_provider.h" +#include "cc/test/fake_layer_tree_host_impl.h" #include "cc/test/geometry_test_utils.h" +#include "cc/test/test_task_graph_runner.h" #include "cc/trees/clip_node.h" #include "cc/trees/draw_property_utils.h" #include "cc/trees/effect_node.h" +#include "cc/trees/layer_tree_impl.h" #include "cc/trees/scroll_node.h" #include "cc/trees/transform_node.h" #include "components/viz/common/frame_sinks/copy_output_request.h" @@ -629,5 +633,70 @@ TEST(ScrollTreeTest, GetPixelSnappedScrollOffsetNegativeOffset) { EXPECT_EQ(offset.y(), 0); } +// Verify that when fractional scroll delta is turned off, that the remaining +// fractional delta does not cause additional property changes. +TEST(ScrollTreeTest, PushScrollUpdatesFromMainThreadIntegerDelta) { + const bool use_fractional_deltas = false; + + // Set up main property trees. + PropertyTrees property_trees; + ScrollTree& main_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 = main_scroll_tree.Insert(ScrollNode(), 0); + main_scroll_tree.Node(scroll_node_id)->transform_id = transform_node_id; + main_scroll_tree.Node(scroll_node_id)->element_id = element_id; + + // Set up FakeLayerTreeHostImpl. + TestTaskGraphRunner task_graph_runner; + FakeImplTaskRunnerProvider impl_task_runner_provider; + FakeLayerTreeHostImpl host_impl( + LayerTreeSettings(), &impl_task_runner_provider, &task_graph_runner); + host_impl.CreatePendingTree(); + + // Set up pending property trees. + PropertyTrees* pending_property_trees = + host_impl.pending_tree()->property_trees(); + EXPECT_TRUE(pending_property_trees); + ScrollTree& pending_scroll_tree = pending_property_trees->scroll_tree; + TransformTree& pending_transform_tree = + pending_property_trees->transform_tree; + transform_node_id = pending_transform_tree.Insert(TransformNode(), 0); + scroll_node_id = pending_scroll_tree.Insert(ScrollNode(), 0); + pending_scroll_tree.Node(scroll_node_id)->transform_id = transform_node_id; + pending_scroll_tree.Node(scroll_node_id)->element_id = element_id; + pending_property_trees->element_id_to_scroll_node_index[element_id] = + scroll_node_id; + + // Push main scroll to pending. + main_scroll_tree.SetScrollOffset(element_id, gfx::ScrollOffset(0, 1)); + pending_scroll_tree.PushScrollUpdatesFromMainThread( + &property_trees, host_impl.pending_tree(), use_fractional_deltas); + const SyncedScrollOffset* scroll_offset = + pending_scroll_tree.GetSyncedScrollOffset(element_id); + EXPECT_TRUE(scroll_offset); + + // Set a fractional delta and check it is not pulled with fractional delta + // turned off. + pending_scroll_tree.SetScrollOffsetDeltaForTesting(element_id, + gfx::Vector2dF(0, 0.25)); + main_scroll_tree.CollectScrollDeltasForTesting(use_fractional_deltas); + EXPECT_EQ(gfx::ScrollOffset(0, 1), + main_scroll_tree.current_scroll_offset(element_id)); + + // Rounding logic turned on should not cause property change on push. + host_impl.pending_tree()->property_trees()->changed = false; + pending_scroll_tree.PushScrollUpdatesFromMainThread( + &property_trees, host_impl.pending_tree(), use_fractional_deltas); + EXPECT_FALSE(host_impl.pending_tree()->property_trees()->changed); + + // Rounding logic turned off should cause property change on push. + host_impl.pending_tree()->property_trees()->changed = false; + pending_scroll_tree.PushScrollUpdatesFromMainThread( + &property_trees, host_impl.pending_tree(), true); + EXPECT_TRUE(host_impl.pending_tree()->property_trees()->changed); +} + } // namespace } // namespace cc diff --git a/chromium/cc/trees/proxy_common.h b/chromium/cc/trees/proxy_common.h index 51666ea720c..79c8a4bd243 100644 --- a/chromium/cc/trees/proxy_common.h +++ b/chromium/cc/trees/proxy_common.h @@ -32,6 +32,7 @@ struct CC_EXPORT BeginMainFrameAndCommitState { // Bit encoding of the FrameSequenceTrackerType for active trackers ActiveFrameSequenceTrackers active_sequence_trackers = 0; bool evicted_ui_resources = false; + std::vector<uint32_t> finished_transition_request_sequence_ids; }; } // namespace cc diff --git a/chromium/cc/trees/proxy_impl.cc b/chromium/cc/trees/proxy_impl.cc index a67c14b6b64..d9d417b439a 100644 --- a/chromium/cc/trees/proxy_impl.cc +++ b/chromium/cc/trees/proxy_impl.cc @@ -7,6 +7,7 @@ #include <string.h> #include <algorithm> +#include <memory> #include <string> #include <utility> #include <vector> @@ -90,9 +91,11 @@ ProxyImpl::ProxyImpl(base::WeakPtr<ProxyMain> proxy_main_weak_ptr, CompositorTimingHistory::RENDERER_UMA, layer_tree_host->rendering_stats_instrumentation(), host_impl_->compositor_frame_reporting_controller())); - scheduler_.reset(new Scheduler(this, scheduler_settings, layer_tree_host_id_, - task_runner_provider_->ImplThreadTaskRunner(), - std::move(compositor_timing_history))); + scheduler_ = std::make_unique<Scheduler>( + this, scheduler_settings, layer_tree_host_id_, + task_runner_provider_->ImplThreadTaskRunner(), + std::move(compositor_timing_history), layer_tree_host->TakeMainPipeline(), + layer_tree_host->TakeCompositorPipeline()); DCHECK_EQ(scheduler_->visible(), host_impl_->visible()); } @@ -614,6 +617,8 @@ void ProxyImpl::ScheduledActionSendBeginMainFrame( begin_main_frame_state->commit_data = host_impl_->ProcessCompositorDeltas(); begin_main_frame_state->completed_image_decode_requests = host_impl_->TakeCompletedImageDecodeRequests(); + begin_main_frame_state->finished_transition_request_sequence_ids = + host_impl_->TakeFinishedTransitionRequestSequenceIds(); begin_main_frame_state->mutator_events = host_impl_->TakeMutatorEvents(); begin_main_frame_state->active_sequence_trackers = host_impl_->FrameSequenceTrackerActiveTypes(); @@ -771,7 +776,8 @@ DrawResult ProxyImpl::DrawInternal(bool forced_draw) { DCHECK_NE(frame.frame_token, 0u); // Drawing implies we submitted a frame to the LayerTreeFrameSink. scheduler_->DidSubmitCompositorFrame(frame.frame_token, - host_impl_->TakeEventsMetrics()); + host_impl_->TakeEventsMetrics(), + frame.has_missing_content); } result = DRAW_SUCCESS; } else { diff --git a/chromium/cc/trees/proxy_main.cc b/chromium/cc/trees/proxy_main.cc index cfafe9c81b5..6f1e341a730 100644 --- a/chromium/cc/trees/proxy_main.cc +++ b/chromium/cc/trees/proxy_main.cc @@ -145,6 +145,9 @@ void ProxyMain::BeginMainFrame( layer_tree_host_->ImageDecodesFinished( std::move(begin_main_frame_state->completed_image_decode_requests)); + layer_tree_host_->NotifyTransitionRequestsFinished(std::move( + begin_main_frame_state->finished_transition_request_sequence_ids)); + // Visibility check needs to happen before setting // max_requested_pipeline_stage_. Otherwise a requested commit could get lost // after tab becomes visible again. diff --git a/chromium/cc/trees/single_thread_proxy.cc b/chromium/cc/trees/single_thread_proxy.cc index 628454e3b90..8f563410e76 100644 --- a/chromium/cc/trees/single_thread_proxy.cc +++ b/chromium/cc/trees/single_thread_proxy.cc @@ -87,10 +87,12 @@ void SingleThreadProxy::Start() { CompositorTimingHistory::BROWSER_UMA, layer_tree_host_->rendering_stats_instrumentation(), host_impl_->compositor_frame_reporting_controller())); - scheduler_on_impl_thread_.reset( - new Scheduler(this, scheduler_settings, layer_tree_host_->GetId(), - task_runner_provider_->MainThreadTaskRunner(), - std::move(compositor_timing_history))); + scheduler_on_impl_thread_ = std::make_unique<Scheduler>( + this, scheduler_settings, layer_tree_host_->GetId(), + task_runner_provider_->MainThreadTaskRunner(), + std::move(compositor_timing_history), + layer_tree_host_->TakeMainPipeline(), + layer_tree_host_->TakeCompositorPipeline()); } } @@ -212,6 +214,9 @@ void SingleThreadProxy::DoCommit(const viz::BeginFrameArgs& commit_args) { IssueImageDecodeFinishedCallbacks(); host_impl_->CommitComplete(); + layer_tree_host_->NotifyTransitionRequestsFinished( + host_impl_->TakeFinishedTransitionRequestSequenceIds()); + // Commit goes directly to the active tree, but we need to synchronously // "activate" the tree still during commit to satisfy any potential // SetNextCommitWaitsForActivation calls. Unfortunately, the tree @@ -516,6 +521,9 @@ void SingleThreadProxy::NotifyImageDecodeRequestFinished() { DebugScopedSetImplThread impl(task_runner_provider_); IssueImageDecodeFinishedCallbacks(); + + layer_tree_host_->NotifyTransitionRequestsFinished( + host_impl_->TakeFinishedTransitionRequestSequenceIds()); return; } SetNeedsCommitOnImplThread(); @@ -625,6 +633,7 @@ void SingleThreadProxy::CompositeImmediatelyForTest( // Note: We do not want to prevent SetNeedsAnimate from requesting // a commit here. commit_requested_ = true; + StopDeferringCommits(PaintHoldingCommitTrigger::kFeatureDisabled); DoBeginMainFrame(begin_frame_args); commit_requested_ = false; DoPainting(); @@ -712,7 +721,8 @@ DrawResult SingleThreadProxy::DoComposite(LayerTreeHostImpl::FrameData* frame) { if (scheduler_on_impl_thread_) { // Drawing implies we submitted a frame to the LayerTreeFrameSink. scheduler_on_impl_thread_->DidSubmitCompositorFrame( - frame->frame_token, host_impl_->TakeEventsMetrics()); + frame->frame_token, host_impl_->TakeEventsMetrics(), + frame->has_missing_content); } single_thread_client_->DidSubmitCompositorFrame(); } |